*** empty log message ***
[gnus] / lisp / gnus.el
1 ;;; (ding) Gnus: a newsreader for GNU Emacs
2 ;; Copyright (C) 1987,88,89,90,93,94,95 Free Software Foundation, Inc.
3
4 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
5 ;;      Lars Ingebrigtsen <larsi@ifi.uio.no>
6 ;; Keywords: news
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to
22 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 ;;; Commentary:
25
26 ;; Although (ding) Gnus looks suspiciously like GNUS, it isn't quite
27 ;; the same beast. Most internal structures have been changed. If you
28 ;; have written packages that depend on any of the hash tables,
29 ;; `gnus-newsrc-assoc', `gnus-killed-assoc', marked lists, the .newsrc
30 ;; buffer, or internal knowledge of the `nntp-header-' macros, or
31 ;; dependence on the buffers having a certain format, your code will
32 ;; fail.
33
34 ;;; Code:
35
36 (require 'mail-utils)
37 (require 'rnews)
38 (require 'rmail)
39 (require 'timezone)
40
41 (require 'nnheader)
42 (require 'nnmail)
43
44 ;; Customization variables
45
46 (defvar gnus-select-method 
47   (list 'nntp (or (getenv "NNTPSERVER") 
48                   (if (and gnus-default-nntp-server
49                            (not (string= gnus-default-nntp-server "")))
50                       gnus-default-nntp-server)
51                   (system-name))
52         "nntp")
53   "Default method for selecting a newsgroup.
54 This variable should be a list, where the first element is how the
55 news is to be fetched, the second is the address, and the optional
56 third element is the \"port number\", if nntp is used.
57
58 For instance, if you want to get your news via NNTP from
59 \"flab.flab.edu\" on port 23, you could say:
60
61 (setq gnus-select-method '(nntp \"flab.flab.edu\" 23))
62
63 If you want to use your local spool, say:
64
65 (setq gnus-select-method (list 'nnspool (system-name)))
66
67 If you use this variable, you must set `gnus-nntp-server' to nil.")
68
69 ;; Added by Sudish Joseph <joseph@cis.ohio-state.edu>.
70 (defvar gnus-post-method nil
71   "Preferred method for posting USENET news.
72 If this variable is nil, GNUS will use the current method to decide
73 which method to use when posting.  If it is non-nil, it will override
74 the current method. This method will not be used in mail groups and
75 the like, only in \"real\" newsgroups.
76
77 The value must be a valid method as discussed in the documentation of
78 `gnus-select-method'.")
79
80 (defvar gnus-default-nntp-server nil
81   "Specify a default NNTP server.
82 This variable should be defined in paths.el.")
83
84 (defvar gnus-secondary-servers nil
85   "List of NNTP servers that the user can choose between interactively.
86 The list should contain lists, where each list contains the name of
87 the server. To make Gnus query you for a server, you have to give
88 `gnus' a non-numeric prefix - `C-u M-x gnus', in short.")
89
90 (defvar gnus-nntp-server nil
91   "*The name of the host running the NNTP server.
92 This variable is semi-obsolete. Use the `gnus-select-method'
93 variable instead.")
94
95 (defvar gnus-nntp-service "nntp"
96   "NNTP service name (\"nntp\" or 119).
97 This is an obsolete variable, which is scarcely used. If you use an
98 nntp server for your newsgroup and want to change the port number
99 used to 899, you would say something along these lines:
100
101 (setq gnus-select-method '(nntp \"my.nntp.server\" 899))")
102
103 (defvar gnus-startup-file "~/.newsrc"
104   "Your `.newsrc' file.  Use `.newsrc-SERVER' instead if it exists.")
105
106 (defvar gnus-signature-file "~/.signature"
107   "Your `.signature' file.")
108
109 (defvar gnus-init-file "~/.gnus"
110   "Your Gnus elisp startup file.
111 If a file with the .el or .elc suffixes exist, they will be read
112 instead.") 
113
114 (defvar gnus-default-subscribed-newsgroups nil
115   "This variable lists what newsgroups should be susbcribed the first time Gnus is used.
116 It should be a list of strings.
117 If it is `t', Gnus will not do anything special the first time it is
118 started; it'll just use the normal newsgroups subscription methods.")
119
120 (defconst gnus-backup-default-subscribed-newsgroups 
121   '("news.announce.newusers" "news.groups.questions")
122   "Default default new newsgroups the first time Gnus is run.")
123
124 (defvar gnus-post-prepare-function nil
125   "Function that is run after a post buffer has been prepared.
126 It is called with the name of the newsgroup that is posted to. It
127 might be use, for instance, for inserting signatures based on the
128 newsgroup name. (In that case, `gnus-signature-file' and
129 `mail-signature' should both be set to nil).")
130
131 (defvar gnus-use-cross-reference t
132   "Non-nil means that cross referenced articles will be marked as read.
133 If nil, ignore cross references.  If t, mark articles as read in
134 all newsgroups.")
135
136 (defvar gnus-use-followup-to 'use
137   "Specifies what to do with Followup-To field.
138 If nil, ignore the field. If it is t, use its value, but ignore 
139 `poster'. If it is neither nil nor t, always use the value.")
140
141 (defvar gnus-followup-to-function nil
142   "A variable that contains a function that returns a followup address.
143 The function will be called in the buffer of the article that is being
144 followed up. The buffer will be narrowed to the headers of the
145 article. To pick header fields, one might use `mail-fetch-field'.  The
146 function will be called with the name of the current newsgroup as the
147 argument.
148
149 Here's an example `gnus-followup-to-function':
150
151 (setq gnus-followup-to-function
152       (lambda (group)
153         (cond ((string= group \"mail.list\")
154                (or (mail-fetch-field \"sender\") 
155                    (mail-fetch-field \"from\")))
156               (t
157                (or (mail-fetch-field \"reply-to\") 
158                    (mail-fetch-field \"from\"))))))")
159
160 (defvar gnus-reply-to-function nil
161   "A variable that contains a function that returns a reply address.
162 See the `gnus-followup-to-function' variable for an explanation of how
163 this variable is used.")
164
165 (defvar gnus-large-newsgroup 200
166   "The number of articles which indicates a large newsgroup.
167 If the number of articles in a newsgroup is greater than the value,
168 confirmation is required for selecting the newsgroup.")
169
170 (defvar gnus-author-copy (getenv "AUTHORCOPY")
171   "Name of the file the article will be saved before it is posted using the FCC: field.
172 Initialized from the AUTHORCOPY environment variable.
173
174 Articles are saved using a function specified by the the variable
175 `gnus-author-copy-saver' (`rmail-output' is default) if a file name is
176 given.  Instead, if the first character of the name is `|', the
177 contents of the article is piped out to the named program. It is
178 possible to save an article in an MH folder as follows:
179
180 \(setq gnus-author-copy \"|/usr/local/lib/mh/rcvstore +Article\")")
181
182 (defvar gnus-author-copy-saver (function rmail-output)
183   "A function called with a file name to save an author copy to.
184 The default function is `rmail-output' which saves in Unix mailbox format.")
185
186 (defvar gnus-use-long-file-name (not (memq system-type '(usg-unix-v xenix)))
187   "Non-nil means that the default name of a file to save articles in is the newsgroup name.
188 If it's nil, the directory form of the newsgroup name is used instead.")
189
190 (defvar gnus-article-save-directory (getenv "SAVEDIR")
191   "Name of the directory articles will be saved in (default \"~/News\").
192 Initialized from the SAVEDIR environment variable.")
193
194 (defvar gnus-kill-files-directory (getenv "SAVEDIR")
195   "Name of the directory where kill files will be stored (default \"~/News\").
196 Initialized from the SAVEDIR environment variable.")
197
198 (defvar gnus-kill-expiry-days 7
199   "*Number of days before unused kill file entries are expired.")
200
201 (defvar gnus-default-article-saver (function gnus-summary-save-in-rmail)
202   "A function to save articles in your favorite format.
203 The function must be interactively callable (in other words, it must
204 be an Emacs command).
205
206 Gnus provides the following functions:
207
208 * gnus-summary-save-in-rmail (Rmail format)
209 * gnus-summary-save-in-mail (Unix mail format)
210 * gnus-summary-save-in-folder (MH folder)
211 * gnus-summary-save-in-file (article format).")
212
213 (defvar gnus-rmail-save-name (function gnus-plain-save-name)
214   "A function generating a file name to save articles in Rmail format.
215 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
216
217 (defvar gnus-mail-save-name (function gnus-plain-save-name)
218   "A function generating a file name to save articles in Unix mail format.
219 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
220
221 (defvar gnus-folder-save-name (function gnus-folder-save-name)
222   "A function generating a file name to save articles in MH folder.
223 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER.")
224
225 (defvar gnus-file-save-name (function gnus-numeric-save-name)
226   "A function generating a file name to save articles in article format.
227 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
228
229 (defvar gnus-kill-file-name "KILL"
230   "Suffix of the kill files.")
231
232 (defvar gnus-visual t
233   "*If non-nil, will do various highlighting.
234 If nil, no mouse highlight (or any other) will be performed. This
235 might speed up Gnus some when generating large Newsgroup and Summary
236 buffers.")
237
238 (defvar gnus-novice-user t
239   "*Non-nil means that you are a usenet novice.
240 If non-nil, verbose messages may be displayed and confirmations may be
241 required.")
242
243 (defvar gnus-expert-user nil
244   "*Non-nil means that you will never be asked for confirmation about anything.
245 And that means *anything*.")
246
247 (defvar gnus-keep-same-level nil
248   "Non-nil means that the next newsgroup after the current will be on the same level.
249 When you type, for instance, `n' after reading the last article in the
250 current newsgroup, you will go to the next newsgroup. If this variable
251 is nil, the next newsgroup will be the next from the Newsgroup
252 buffer. If this variable is non-nil, Gnus will either put you in the
253 next newsgroup with the same level, or, if no such newsgroup is
254 available, the next newsgroup with the lowest possible level higher
255 than the current level.")
256
257 (defvar gnus-gather-loose-threads t
258   "Non-nil means sub-threads from a common thread will be gathered.
259 If the root of a thread has expired or been read in a previous
260 session, the information necessary to build a complete thread has been
261 lost. Instead of having many small sub-threads from this original thread
262 scattered all over the Summary buffer, Gnus will gather them. If the
263 `gnus-summary-make-false-root' variable is non-nil, Gnus will also
264 present them as one thread with a new root.")
265
266 (defvar gnus-summary-make-false-root 'adopt
267   "nil means that Gnus won't print dummy roots of threads in the summary buffer.
268 If `gnus-gather-loose-threads' is non-nil, Gnus will try to gather all
269 loose sub-threads from an original thread into one large thread. If
270 this variable is nil, these sub-threads will not get a common root,
271 but will just be presented after one another. If this variable is
272 `dummy', Gnus will create a dummy root that will have all the
273 sub-threads as children.
274 If this variable is `adopt', Gnus will make one of the \"children\"
275 the parent and mark all the step-children as such.
276 If this variable is `empty', the \"children\" are printed with empty
277 subject fields.")
278
279 (defvar gnus-check-new-newsgroups t
280   "Non-nil means that Gnus will add new newsgroups at startup.
281 If this variable is nil, then you have to tell Gnus explicitly to
282 check for new newsgroups with \\<gnus-group-mode-map>\\[gnus-find-new-newsgroups].")
283
284 (defvar gnus-check-bogus-newsgroups nil
285   "Non-nil means that Gnus will check and delete bogus newsgroup at startup.
286 If this variable is nil, then you have to tell Gnus explicitly to
287 check for bogus newsgroups with \\<gnus-group-mode-map>\\[gnus-group-check-bogus-groups].")
288
289 (defvar gnus-read-active-file t
290   "Non-nil means that Gnus will read the entire active file at startup.
291 If this variable is nil, Gnus will only read parts of the active file.")
292
293 (defvar gnus-activate-foreign-newsgroups nil
294   "If nil, Gnus will not check foreign newsgroups at startup.
295 If it is non-nil, it should be a number between one and nine. Foreign
296 newsgroups that have a level lower or equal to this number will be
297 activated on startup. For instance, if you want to active all
298 subscribed newsgroups, but not the rest, you'd set this variable to 5.
299
300 If you subscribe to lots of newsgroups from different servers, startup
301 might take a while. By setting this variable to nil, you'll save time,
302 but you won't be told how many unread articles there are in the
303 newsgroups.")
304
305 (defvar gnus-save-newsrc-file t
306   "Non-nil means that Gnus will save a .newsrc file.
307 Gnus always saves its own startup file, which is called \".newsrc.el\".
308 The file called \".newsrc\" is in a format that can be readily
309 understood by other newsreaders. If you don't plan on using other
310 newsreaders, set this variable to nil to save some time on exit.")
311
312 (defvar gnus-save-killed-list t
313   "If non-nil, save the list of killed groups to the startup file.
314 This will save both time (when starting and quitting) and space (on
315 disk), but it will also mean that Gnus has no record of what
316 newsgroups are new or old, so the automatic new newsgroups
317 subscription methods become meaningless. You should always set
318 `gnus-check-new-newsgroups' to nil if you set this variable to nil.") 
319
320 (defvar gnus-interactive-catchup t
321   "Require your confirmation when catching up a newsgroup if non-nil.")
322
323 (defvar gnus-interactive-post t
324   "Newsgroup and subject will be asked for if non-nil.")
325
326 (defvar gnus-interactive-exit t
327   "Require your confirmation when exiting Gnus if non-nil.")
328
329 (defvar gnus-kill-killed t
330   "If non-nil, Gnus will apply kill files to already \"killed\" articles.
331 If it is nil, Gnus will never apply kill files to articles that have
332 already been through the kill process, which might very well save lots
333 of time.")
334
335 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
336 (defvar gnus-summary-same-subject ""
337   "String indicating that the current article has the same subject as the previous.")
338
339 (defvar gnus-summary-default-interest nil
340   "Default article interest level.
341 If this variable is nil, interest levels will not be used.")
342
343 (defvar gnus-user-login-name nil
344   "The login name of the user.
345 Got from the function `user-login-name' if undefined.")
346
347 (defvar gnus-user-full-name nil
348   "The full name of the user.
349 Got from the NAME environment variable if undefined.")
350
351 (defvar gnus-show-mime nil
352   "*Show MIME message if non-nil.")
353
354 (defvar gnus-show-threads t
355   "*Show conversation threads in Summary Mode if non-nil.")
356
357 (defvar gnus-thread-hide-subtree nil
358   "Non-nil means hide thread subtrees initially.
359 If non-nil, you have to run the command `gnus-summary-show-thread' by
360 hand or by using `gnus-select-article-hook' to show hidden threads.")
361
362 (defvar gnus-thread-hide-killed t
363   "Non-nil means hide killed thread subtrees automatically.")
364
365 (defvar gnus-thread-ignore-subject nil
366   "Don't take care of subject differences, but only references if non-nil.
367 If it is non-nil, some commands work with subjects do not work properly.")
368
369 (defvar gnus-thread-indent-level 4
370   "Indentation of thread subtrees.")
371
372 ;; jwz: nuke newsgroups whose name is all digits - that means that
373 ;; some loser has let articles get into the root of the news spool,
374 ;; which is toxic. Lines beginning with whitespace also tend to be
375 ;; toxic.
376 (defvar gnus-ignored-newsgroups
377   (purecopy (mapconcat 'identity
378                        '("^to\\."               ; not "real" groups
379                          "^[0-9. \t]+ "         ; all digits in name
380                          "[][\"#'();\\]"        ; bogus characters
381                          )
382                        "\\|"))
383   "A regexp to match uninteresting newsgroups in the active file.
384 Any lines in the active file matching this regular expression are
385 removed from the newsgroup list before anything else is done to it,
386 thus making them effectively non-existant.")
387
388 (defvar gnus-ignored-headers
389   "^Path:\\|^Posting-Version:\\|^Article-I.D.:\\|^Expires:\\|^Date-Received:\\|^References:\\|^Control:\\|^Xref:\\|^Lines:\\|^Posted:\\|^Relay-Version:\\|^Message-ID:\\|^Nf-ID:\\|^Nf-From:\\|^Approved:\\|^Sender:\\|^Received:\\|^Mail-from:"
390   "All header lines that match this regexp will be hidden.
391 Also see `gnus-visible-headers'.")
392
393 (defvar gnus-visible-headers "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:"
394   "All header lines that do not match this regexp will be hidden.
395 Also see `gnus-ignored-headers'.")
396
397 (defvar gnus-sorted-header-list
398   '("^From:" "^Subject:" "^Summary:" "^Keywords:" "^Newsgroups:" 
399     "^Date:" "^Organization:")
400   "This variable is a list of regular expressions.
401 If it is non-nil, header lines that match the regular expressions will
402 be placed first in the Article buffer in the sequence specified by
403 this list.")
404
405 (defvar gnus-required-headers
406   '(From Date Newsgroups Subject Message-ID Organization Lines X-Newsreader)
407   ;; changed by jwz because it's not so nice to do "Lines: 0" by default.
408   ;; and to remove Path, since it's incorrect for Gnus to try
409   ;; and generate that - it is the responsibility of inews or nntpd.
410   "All required fields for articles you post.
411 RFC977 and RFC1036 require From, Date, Newsgroups, Subject, Message-ID
412 and Path fields.  Organization, Lines and X-Newsreader are optional.
413 If you want Gnus not to insert some field, remove it from this
414 variable.")
415
416 (defvar gnus-show-all-headers nil
417   "*Show all headers of an article if non-nil.")
418
419 (defvar gnus-save-all-headers t
420   "*Save all headers of an article if non-nil.")
421
422 (defvar gnus-inhibit-startup-message nil
423   "The startup message will not be displayed if this function is non-nil.")
424
425 (defvar gnus-auto-extend-newsgroup t
426   "Extend visible articles to forward and backward if non-nil.")
427
428 (defvar gnus-auto-select-first t
429   "Select the first unread article automagically if non-nil.
430 If you want to prevent automatic selection of the first unread article
431 in some newsgroups, set the variable to nil in `gnus-select-group-hook'
432 or `gnus-apply-kill-hook'.")
433
434 (defvar gnus-auto-select-next t
435   "Select the next newsgroup automagically if non-nil.
436 If the value is t and the next newsgroup is empty, Gnus will exit
437 Summary mode and go back to Group mode.  If the value is neither nil
438 nor t, Gnus will select the following unread newsgroup.  Especially, if
439 the value is the symbol `quietly', the next unread newsgroup will be
440 selected without any confirmations.")
441
442 (defvar gnus-auto-select-same nil
443   "Select the next article with the same subject automagically if non-nil.")
444
445 (defvar gnus-auto-center-summary t
446   "*Always center the current summary in Gnus Summary window if non-nil.")
447
448 (defvar gnus-auto-mail-to-author nil
449   "Insert `To: author' of the article when following up if non-nil.
450 Mail is sent using the function specified by the variable
451 `gnus-mail-send-method'.")
452
453 (defvar gnus-break-pages t
454   "*Break an article into pages if non-nil.
455 Page delimiter is specified by the variable `gnus-page-delimiter'.")
456
457 (defvar gnus-page-delimiter "^\^L"
458   "Regexp describing line-beginnings that separate pages of news article.")
459
460 (defvar gnus-digest-show-summary t
461   "Show a summary of undigestified messages if non-nil.")
462
463 (defvar gnus-digest-separator "^Subject:[ \t]"
464   "Regexp that separates messages in a digest article.")
465
466 (defvar gnus-use-full-window t
467   "*Non-nil means to take up the entire screen of Emacs.")
468
469 (defvar gnus-window-configuration
470   '((summary (0 1 0))
471     (newsgroups (1 0 0))
472     (article (0 3 10)))
473   "Specify window configurations for each action.
474 The format of the variable is either a list of (ACTION (G S A)), where
475 G, S, and A are the relative height of Group, Summary, and Article
476 windows, respectively, or a list of (ACTION FUNCTION), where FUNCTION
477 is a function that will be called with ACTION as an argument. ACTION
478 can be `summary', `newsgroups', or `article'.")
479
480 (defvar gnus-show-mime-method (function metamail-buffer)
481   "Function to process a MIME message.
482 The function is expected to process current buffer as a MIME message.")
483
484 (defvar gnus-mail-reply-method
485   (function gnus-mail-reply-using-mail)
486   "Function to compose reply mail.
487 The function `gnus-mail-reply-using-mail' uses usual sendmail mail
488 program.  The function `gnus-mail-reply-using-mhe' uses the MH-E mail
489 program.  You can use yet another program by customizing this variable.")
490
491 (defvar gnus-mail-forward-method
492   (function gnus-mail-forward-using-mail)
493   "Function to forward current message to another user.
494 The function `gnus-mail-reply-using-mail' uses usual sendmail mail
495 program.  You can use yet another program by customizing this variable.")
496
497 (defvar gnus-mail-other-window-method
498   (function gnus-mail-other-window-using-mail)
499   "Function to compose mail in other window.
500 The function `gnus-mail-other-window-using-mail' uses the usual sendmail
501 mail program.  The function `gnus-mail-other-window-using-mhe' uses the MH-E
502 mail program.  You can use yet another program by customizing this variable.")
503
504 (defvar gnus-mail-send-method send-mail-function
505   "Function to mail a message too which is being posted as an article.
506 The message must have To: or Cc: field.  The default is copied from
507 the variable `send-mail-function'.")
508
509 (defvar gnus-subscribe-newsgroup-method
510   (function gnus-subscribe-zombies)
511   "Function called with a newsgroup name when new newsgroup is found.
512 The function `gnus-subscribe-randomly' inserts a new newsgroup a the
513 beginning of newsgroups.  The function `gnus-subscribe-alphabetically'
514 inserts it in strict alphabetic order.  The function
515 `gnus-subscribe-hierarchically' inserts it in hierarchical newsgroup
516 order.  The function `gnus-subscribe-interactively' asks for your decision.")
517
518 ;; Suggested by a bug report by Hallvard B Furuseth
519 ;; <h.b.furuseth@usit.uio.no>. 
520 (defvar gnus-subscribe-options-newsgroup-method
521   (function gnus-subscribe-alphabetically)
522   "This function is called to subscribe newsgroups mentioned on \"options -n\" lines.
523 If, for instance, you want to subscribe to all newsgroups in the
524 \"no\" and \"alt\" hierarchies, you'd put the following in your
525 .newsrc file:
526
527 options -n no.all alt.all
528
529 Gnus will the subscribe all new newsgroups in these hierarchies with
530 the subscription method in this variable.")
531
532 ;; Mark variables suggested by Thomas Michanek
533 ;; <Thomas.Michanek@telelogic.se>. 
534 (defvar gnus-unread-mark " "
535   "Mark used for unread articles.")
536
537 (defvar gnus-read-mark "D"
538   "Mark used for read articles.")
539
540 (defvar gnus-ticked-mark "-"
541   "Mark used for ticked articles.")
542
543 (defvar gnus-dormant-mark "+"
544   "Mark used for dormant articles.")
545
546 (defvar gnus-killed-mark "K"
547   "Mark used for killed articles.")
548
549 (defvar gnus-kill-file-mark "X"
550   "Mark used for articles killed by kill files.")
551
552 (defvar gnus-catchup-mark "C"
553   "Mark used for articles that are caught up.")
554
555 (defvar gnus-group-mode-hook nil
556   "A hook for Gnus Group Mode.")
557
558 (defvar gnus-summary-mode-hook nil
559   "A hook for Gnus Summary Mode.")
560
561 (defvar gnus-article-mode-hook nil
562   "A hook for Gnus Article Mode.")
563
564 (defvar gnus-kill-file-mode-hook nil
565   "A hook for Gnus KILL File Mode.")
566
567 (defvar gnus-open-server-hook nil
568   "A hook called just before opening connection to news server.")
569
570 (defvar gnus-startup-hook nil
571   "A hook called at startup time.
572 This hook is called after Gnus is connected to the NNTP server. So, it
573 is possible to change the behavior of Gnus according to the selected
574 NNTP server.")
575
576 (defvar gnus-group-prepare-hook nil
577   "A hook called after the newsgroup list is created in the Newsgroup buffer.
578 If you want to modify the Newsgroup buffer, you can use this hook.")
579
580 (defvar gnus-summary-prepare-hook nil
581   "A hook called after summary list is created in the Summary buffer.
582 If you want to modify the Summary buffer, you can use this hook.")
583
584 (defvar gnus-article-prepare-hook nil
585   "A hook called after an article is prepared in the Article buffer.
586 If you want to run a special decoding program like nkf, use this hook.")
587
588 (defvar gnus-article-display-hook nil
589   "A hook called after the article is displayed in the Article buffer.
590 The hook is designed to change the contents of the Article
591 buffer. Typical functions that this hook may contain are
592 `gnus-article-hide-headers' (hide selected headers),
593 `gnus-article-hide-signature' (hide signature) and
594 `gnus-article-treat-overstrike' (turn \"^H_\" into bold characters).")
595 (add-hook 'gnus-article-display-hook 'gnus-article-hide-headers-if-wanted)
596
597 (defvar gnus-select-group-hook nil
598   "A hook called when a newsgroup is selected.
599 If you want to sort Summary buffer by date and then by subject, you
600 can use the following hook:
601
602  (setq gnus-select-group-hook
603       (list
604         (lambda ()
605           ;; First of all, sort by date.
606           (gnus-keysort-headers
607            (function string-lessp)
608             (lambda (a)
609               (gnus-sortable-date (header-date a))))
610           ;; Then sort by subject string ignoring `Re:'.
611           ;; If case-fold-search is non-nil, case of letters is ignored.
612           (gnus-keysort-headers
613            (function string-lessp)
614             (lambda (a)
615               (if case-fold-search
616                   (downcase (gnus-simplify-subject (header-subject a) t))
617                 (gnus-simplify-subject (header-subject a) t)))))))
618
619 If you'd like to simplify subjects like the
620 `gnus-summary-next-same-subject' command does, you can use the
621 following hook:
622
623  (setq gnus-select-group-hook
624       (list
625         (lambda ()
626           (mapcar (lambda (header)
627                      (header-set-subject
628                       header
629                       (gnus-simplify-subject
630                        (header-subject header) 're-only)))
631                   gnus-newsgroup-headers))))
632 ")
633
634 (defvar gnus-select-article-hook
635   '(gnus-summary-show-thread)
636   "A hook called when an article is selected.
637 The default hook shows conversation thread subtrees of the selected
638 article automatically using `gnus-summary-show-thread'.
639
640 If you'd like to run RMAIL on a digest article automagically, you can
641 use the following hook:
642
643 \(setq gnus-select-article-hook
644       (list
645         (lambda ()
646           (gnus-summary-show-thread)
647           (cond ((string-equal \"comp.sys.sun\" gnus-newsgroup-name)
648                  (gnus-summary-rmail-digest))
649                 ((and (string-equal \"comp.text\" gnus-newsgroup-name)
650                       (string-match \"^TeXhax Digest\"
651                                     (header-subject gnus-current-headers)))
652                  (gnus-summary-rmail-digest)
653                  )))))")
654
655 (defvar gnus-select-digest-hook
656   (list
657     (lambda ()
658       ;; Reply-To: is required by `undigestify-rmail-message'.
659       (or (mail-position-on-field "Reply-to" t)
660           (progn
661             (mail-position-on-field "Reply-to")
662             (insert (gnus-fetch-field "From"))))))
663   "A hook called when reading digest messages using Rmail.
664 This hook can be used to modify incomplete digest articles as follows
665 \(this is the default):
666
667 \(setq gnus-select-digest-hook
668       (list
669         (lambda ()
670           ;; Reply-To: is required by `undigestify-rmail-message'.
671           (or (mail-position-on-field \"Reply-to\" t)
672               (progn
673                 (mail-position-on-field \"Reply-to\")
674                 (insert (gnus-fetch-field \"From\")))))))")
675
676 (defvar gnus-rmail-digest-hook nil
677   "A hook called when reading digest messages using Rmail.
678 This hook is intended to customize Rmail mode for reading digest articles.")
679
680 (defvar gnus-apply-kill-hook '(gnus-apply-kill-file)
681   "A hook called when a newsgroup is selected and summary list is prepared.
682 This hook is intended to apply a KILL file to the selected newsgroup.
683 The function `gnus-apply-kill-file' is called by default.
684
685 Since a general KILL file is too heavy to use only for a few
686 newsgroups, I recommend you to use a lighter hook function. For
687 example, if you'd like to apply a KILL file to articles which contains
688 a string `rmgroup' in subject in newsgroup `control', you can use the
689 following hook:
690
691 \(setq gnus-apply-kill-hook
692       (list
693         (lambda ()
694           (cond ((string-match \"control\" gnus-newsgroup-name)
695                  (gnus-kill \"Subject\" \"rmgroup\")
696                  (gnus-expunge \"X\"))))))")
697
698 (defvar gnus-visual-mark-article-hook 'gnus-visual-highlight-selected-summary
699   "Hook run after selecting an article in the Summary buffer.
700 It is meant to be used for highlighting the article in some way. It is
701 not run if `gnus-visual' is nil.")
702
703 (defvar gnus-prepare-article-hook (list (function gnus-inews-insert-signature))
704   "A hook called after preparing body, but before preparing header fields.
705 The default hook (`gnus-inews-insert-signature') inserts a signature
706 file specified by the variable `gnus-signature-file'.")
707
708 (defvar gnus-inews-article-hook (list (function gnus-inews-do-fcc))
709   "A hook called before finally posting an article.
710 The default hook (`gnus-inews-do-fcc') does FCC processing (save article
711 to a file).")
712
713 (defvar gnus-exit-group-hook nil
714   "A hook called when exiting (not quitting) Summary mode.
715 If your machine is so slow that exiting from Summary mode takes very
716 long time, set the variable `gnus-use-cross-reference' to nil. This
717 inhibits marking articles as read using cross-reference information.")
718
719 (defvar gnus-suspend-gnus-hook nil
720   "A hook called when suspending (not exiting) Gnus.")
721
722 (defvar gnus-exit-gnus-hook (list 'nntp-request-close)
723   "A hook called when exiting Gnus.")
724
725 (defvar gnus-save-newsrc-hook nil
726   "A hook called when saving the newsrc file.
727 This hook is called before saving the `.newsrc' file.")
728
729 (defvar gnus-auto-expirable-newsgroups nil
730   "All newsgroups that match this regexp will have all read articles automatically marked as expirable.")
731
732 (defvar gnus-subscribe-hierarchical-interactive nil
733   "If non-nil, Gnus will offer to subscribe hierarchically.
734 When a new hierarchy appears, Gnus will ask the user:
735
736 'alt.binaries': Do you want to subscribe to this hierarchy? ([d]ys):
737
738 If the user pressed `d', Gnus will descend the hierarchy, `y' will
739 subscribe to all newsgroups in the hierarchy and `s' will skip this
740 hierarchy in its entirety.")
741
742 (defvar gnus-group-line-format "%M%S%5y: %G %z\n"
743   "Format of Newsgroups lines.
744 It works along the same lines as a normal formatting string,
745 with some simple extrensions.
746
747 %M    Only marked articles (character, \"*\" or \" \")
748 %S    Whether the group is subscribed (character, \"U\", \"K\", \"Z\" or \" \")
749 %L    Level of subscribedness (integer, 1-9)
750 %N    Number of unread articles (integer)
751 %I    Number of dormant articles (integer)
752 %i    Number of ticked and dormant (integer)
753 %T    Number of ticked articles (integer)
754 %R    Number of read articles (integer)
755 %t    Total number of articles (integer)
756 %y    Number of unread, unticked articles (integer)
757 %G    Group name (string)
758 %D    Newsgroup description (string)
759 %s    Select method (string)
760 %o    Moderated group (char, \"m\")
761 %O    Moderated group (string, \"(m)\" or \"\")
762 %n    Select from where (string)
763 %z    A string that look like `<%s:%n>' if a foreign select method is used
764
765 Note that this format specification is not always respected. For
766 reasons of efficiency, when listing killed groups, this specification
767 is ignored altogether. If the spec is changed considerably, your
768 output may end up looking strange when listing both alive and killed
769 groups.
770
771 If you use %o or %O, reading the active file will be slower and quite
772 a bit of extra memory will be used. %D will also worsen performance.
773 Also note that if you change the format specification to include any
774 of these specs, you must probably re-start Gnus to see them go into
775 effect.") 
776
777 (defvar gnus-summary-line-format "%U%R%X%i %I%[%4L: %-20,20n%] %s\n"
778   "The format specification of the lines in the Summary buffer.
779 The first specification must always be \"%U%R%X\", at least in this
780 version of Gnus.
781
782 It works along the same lines as a normal formatting string,
783 with some simple extensions.
784
785 %N   Article number, left padded with spaces (integer)
786 %S   Subject (string)
787 %s   Subject if it is at the root of a thread, and \"\" otherwise (string)
788 %n   Name of the poster (string)
789 %A   Address of the poster (string)
790 %L   Number of lines in the article (integer)
791 %D   Date of the article (string)
792 %I   Indentation based on thread level (a string of spaces)
793 %T   A string with two possible values: 80 spaces if the article
794      is on thread level two or larger and 0 spaces on level one
795 %C   This is the current article (character, \"+\" or \" \")
796 %U   Status of this article (character, \"D\", \"K\", \"-\" or \" \") 
797 %[   Opening bracket (character, \"[\" or \"=\")
798 %]   Closing bracket (character, \"]\" or \"=\")
799 %>   Spaces of length thread-level (string)
800 %<   Spaces of length (- 20 thread-level) (string)
801 %i   Article interest (integer, 0-9)
802 ")
803
804 (defconst gnus-summary-dummy-line-format "*   :                          : %S\n"
805   "The format specification for the dummy roots in the Summary buffer.
806 It works along the same lines as a normal formatting string,
807 with some simple extensions.
808
809 %S  The subject")
810
811 (defvar gnus-summary-mode-line-format "(ding) %G/%A %Z"
812   "The format specification for the Summary mode line.")
813
814 (defvar gnus-article-mode-line-format "(ding) %G/%A %S"
815   "The format specification for the Article mode line.")
816
817 (defconst gnus-group-mode-line-format "(ding) List of Newsgroups   {%M:%S}"
818   "The format specification for the Newsgroup mode line.")
819
820
821 \f
822 ;; Site dependent variables. You have to define these variables in
823 ;;  site-init.el, default.el or your .emacs.
824
825 (defvar gnus-local-timezone nil
826   "Local time zone.
827 This value is used only if `current-time-zone' does not work in your Emacs.
828 It specifies the GMT offset, i.e. a decimal integer
829 of the form +-HHMM giving the hours and minutes ahead of (i.e. east of) GMT.
830 For example, +0900 should be used in Japan, since it is 9 hours ahead of GMT.
831
832 For backwards compatibility, it may also be a string like \"JST\",
833 but strings are obsolescent: you should use numeric offsets instead.")
834
835 (defvar gnus-local-domain nil
836   "Local domain name without a host name like: \"stars.flab.Fujitsu.CO.JP\"
837 The `DOMAINNAME' environment variable is used instead if defined.  If
838 the function (system-name) returns the full internet name, there is no
839 need to define the name.")
840
841 (defvar gnus-local-organization nil
842   "Local organization like: \"Fujitsu Laboratories Ltd., Kawasaki, Japan.\"
843 The `ORGANIZATION' environment variable is used instead if defined.")
844
845 (defvar gnus-use-generic-from nil
846   "If nil, prepend local host name to the defined domain in the From:
847 field; if stringp, use this; if non-nil, strip of the local host name.")
848
849 (defvar gnus-use-generic-path nil
850   "If nil, use the NNTP server name in the Path: field; if stringp,
851 use this; if non-nil, use no host name (user name only)")
852
853 (defvar gnus-valid-select-methods
854   '(("nntp" post) ("nnspool" post) ("nnvirtual" none) 
855     ("nnmail" mail respool) ("nnml" mail respool)
856     ("nnmh" mail respool))
857   "A list of valid select methods.
858 Each element in this list should be a list. The first element of these
859 lists should be a string with the name of the select method. The
860 other elements may be be the category of this method (ie. `post',
861 `mail', `none' or whatever) or other properties that this method has
862 (like being respoolable). 
863 If you implement a new select method, all you should have to change is
864 this variable. I think.")
865
866 (defvar gnus-updated-mode-lines '(group article summary)
867   "This variable is a list of buffers that should keep their mode lines updated.
868 The list may contain the symbols `group', `article' and `summary'. If
869 the corresponding symbol is present, Gnus will keep that mode line
870 updated with information that may be pertinent. 
871 If this variable is nil, screen refresh may be quicker.")
872
873 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
874 (defvar gnus-mouse-face 'highlight
875   "Face used for mouse highlighting in Gnus.
876 No mouse highlights will be done if `gnus-visual' is nil.")
877
878 (defvar gnus-visual-summary-update-hook 
879   (list 'gnus-visual-summary-highlight-line)
880   "A hook called when a summary line is changed.
881 The cursor will be positioned at the summary line.
882
883 The default hook `gnus-visual-summary-highlight-line' will highlight the line
884 according to the `gnus-visual-summary-highlight' variable.")
885
886 \f
887 ;; Internal variables
888
889 ;; Avoid highlighting in kill files.
890 (defvar gnus-summary-inhibit-highlight nil)
891
892 (defvar caesar-translate-table nil)
893
894 (defvar gnus-dribble-buffer nil)
895
896 (defvar gnus-article-reply nil)
897 (defvar gnus-article-check-size nil)
898
899 (defvar gnus-newsgroup-dependencies nil)
900
901 (defconst gnus-group-edit-buffer "*Gnus edit newsgroup*")
902
903 (defvar gnus-default-subscribe-level 2
904   "Default subscription level.")
905
906 (defvar gnus-default-unsubscribe-level 6
907   "Default unsubscription level.")
908
909 (defvar gnus-default-kill-level 9
910   "Default kill level.")
911
912 (defconst gnus-group-line-format-alist
913   (list (list ?M 'marked ?c)
914         (list ?S 'subscribed ?c)
915         (list ?L 'level ?d)
916         (list ?N 'number ?s)
917         (list ?I 'number-of-dormant ?d)
918         (list ?T 'number-of-ticked ?d)
919         (list ?R 'number-of-read ?s)
920         (list ?t 'number-total ?d)
921         (list ?y 'number-of-unread-unticked ?s)
922         (list ?i 'number-of-ticked-and-dormant ?d)
923         (list ?G 'group ?s)
924         (list ?D 'newsgroup-description ?s)
925         (list ?o 'moderated ?c)
926         (list ?O 'moderated-string ?s)
927         (list ?s 'news-server ?s)
928         (list ?n 'news-method ?s)
929         (list ?z 'news-method-string ?s)))
930
931 (defconst gnus-summary-line-format-alist 
932   (list (list ?N 'number ?d)
933         (list ?S 'subject ?s)
934         (list ?s 'subject-or-nil ?s)
935         (list ?n 'name ?s)
936         (list ?A 'address ?s)
937         (list ?F 'from ?s)
938         (list ?x (macroexpand '(header-xref header)) ?s)
939         (list ?D (macroexpand '(header-date header)) ?s)
940         (list ?M (macroexpand '(header-id header)) ?s)
941         (list ?r (macroexpand '(header-references header)) ?s)
942         (list ?L 'lines ?d)
943         (list ?I 'indentation ?s)
944         (list ?T '(thread-space (if (< level 1) "" (make-string (frame-width) ? ))) 
945               ?s)
946         (list ?C '(if current ?+ ? ) ?c)
947         (list ?R 'replied ?c)
948         (list ?X 'expirable ?c)
949         (list ?\[ 'opening-bracket ?c)
950         (list ?\] 'closing-bracket ?c)
951         (list ?\> '(make-string level ? ) ?s)
952         (list ?\< '(make-string (max 0 (- 20 level)) ? ) ?s)
953         (list ?i 'interest ?s)
954         (list ?U 'unread ?c))
955   "An alist of format specifications that can appear in summary lines,
956 and what variables they correspond with, along with the type of the
957 variable (string, integer, character, etc).")
958
959 (defconst gnus-summary-dummy-line-format-alist
960   (list (list ?S 'subject ?s)
961         (list ?N 'number ?d)))
962
963 (defconst gnus-summary-mode-line-format-alist 
964   (list (list ?G 'group-name ?s)
965         (list ?A 'article-number ?d)
966         (list ?Z 'unread-and-unselected ?s)
967         (list ?V 'gnus-version ?s)
968         (list ?U 'unread ?d)
969         (list ?S 'subject ?s)
970         (list ?u 'unselected ?d)))
971
972 (defconst gnus-group-mode-line-format-alist 
973   (list (list ?S 'news-server ?s)
974         (list ?M 'news-method ?s)))
975
976 (defvar gnus-have-read-active-file nil)
977
978 (defconst gnus-foreign-group-prefix "foreign.")
979
980 (defconst gnus-maintainer "Lars Magne Ingebrigtsen <larsi@ifi.uio.no>"
981   "The mail address of the Gnus maintainer.")
982
983 (defconst gnus-version "(ding) Gnus v0.10"
984   "Version numbers of this version of Gnus.")
985
986 (defvar gnus-info-nodes
987   '((gnus-group-mode            "(gnus)Newsgroup Commands")
988     (gnus-summary-mode          "(gnus)Summary Commands")
989     (gnus-article-mode          "(gnus)Article Commands")
990     (gnus-kill-file-mode        "(gnus)Kill File"))
991   "Assoc list of major modes and related Info nodes.")
992
993 (defvar gnus-group-buffer "*Newsgroup*")
994 (defvar gnus-summary-buffer "*Summary*")
995 (defvar gnus-article-buffer "*Article*")
996 (defvar gnus-digest-buffer "Gnus Digest")
997 (defvar gnus-digest-summary-buffer "Gnus Digest-summary")
998
999 (defvar gnus-buffer-list nil
1000   "Gnus buffers that should be killed when exiting.")
1001
1002 (defvar gnus-variable-list
1003   '(gnus-newsrc-options
1004     gnus-newsrc-options-n-yes gnus-newsrc-options-n-no
1005     gnus-newsrc-assoc gnus-killed-list gnus-zombie-list)
1006   "Gnus variables saved in the quick startup file.")
1007
1008 (defvar gnus-overload-functions
1009   '((news-inews gnus-inews-news "rnewspost")
1010     (caesar-region gnus-caesar-region "rnews"))
1011   "Functions overloaded by gnus.
1012 It is a list of `(original overload &optional file)'.")
1013
1014 (defvar gnus-newsrc-options nil
1015   "Options line in the .newsrc file.")
1016
1017 (defvar gnus-newsrc-options-n-yes nil
1018   "Regexp representing subscribed newsgroups.")
1019
1020 (defvar gnus-newsrc-options-n-no nil
1021   "Regexp representing unsubscribed newsgroups.")
1022
1023 (defvar gnus-newsrc-assoc nil
1024   "Assoc list of read articles.
1025 gnus-newsrc-hashtb should be kept so that both hold the same information.")
1026
1027 (defvar gnus-newsrc-hashtb nil
1028   "Hashtable of gnus-newsrc-assoc.")
1029
1030 (defvar gnus-killed-list nil
1031   "List of killed newsgroups.")
1032
1033 (defvar gnus-killed-hashtb nil
1034   "Hash table equivalent of gnus-killed-list.")
1035
1036 (defvar gnus-zombie-list nil
1037   "List of almost dead newsgroups.")
1038
1039 (defvar gnus-description-hashtb nil
1040   "Descriptions of newsgroups (from the file 'newsgroups').")
1041
1042 (defvar gnus-list-of-killed-groups nil
1043   "List of newsgroups that have recently been killed by the user.")
1044
1045 (defvar gnus-xref-hashtb nil
1046   "Hash table of cross-posted articles.")
1047
1048 (defvar gnus-active-hashtb nil
1049   "Hashtable of active articles.")
1050
1051 (defvar gnus-moderated-list nil
1052   "List of moderated newsgroups.")
1053
1054 (defvar gnus-current-startup-file nil
1055   "Startup file for the current host.")
1056
1057 (defvar gnus-last-search-regexp nil
1058   "Default regexp for article search command.")
1059
1060 (defvar gnus-last-shell-command nil
1061   "Default shell command on article.")
1062
1063 (defvar gnus-current-select-method nil
1064   "The current method for selecting a newsgroup.")
1065
1066 (defvar gnus-have-all-newsgroups nil)
1067
1068 (defvar gnus-article-internal-prepare-hook nil)
1069
1070 (defvar gnus-newsgroup-name nil)
1071 (defvar gnus-newsgroup-begin nil)
1072 (defvar gnus-newsgroup-end nil)
1073 (defvar gnus-newsgroup-last-rmail nil)
1074 (defvar gnus-newsgroup-last-mail nil)
1075 (defvar gnus-newsgroup-last-folder nil)
1076 (defvar gnus-newsgroup-last-file nil)
1077 (defvar gnus-newsgroup-auto-expire nil
1078   "If non-nil, all read articles will be marked as expirable.")
1079
1080 (defvar gnus-newsgroup-selected-overlay nil)
1081
1082 (defvar gnus-newsgroup-unreads nil
1083   "List of unread articles in the current newsgroup.")
1084
1085 (defvar gnus-newsgroup-unselected nil
1086   "List of unselected unread articles in the current newsgroup.")
1087
1088 (defvar gnus-newsgroup-marked nil
1089   "List of ticked articles in the current newsgroup (a subset of unread art).")
1090
1091 (defvar gnus-newsgroup-killed nil
1092   "List of ranges of articles that have been through the kill process.")
1093
1094 (defvar gnus-newsgroup-replied nil
1095   "List of articles that have been replied to in the current newsgroup.")
1096
1097 (defvar gnus-newsgroup-expirable nil
1098   "List of articles in the current newsgroup that can be expired.")
1099
1100 (defvar gnus-newsgroup-processable nil
1101   "List of articles in the current newsgroup that can be processed.")
1102
1103 (defvar gnus-newsgroup-bookmarks nil
1104   "List of articles in the current newsgroup that have bookmarks.")
1105
1106 (defvar gnus-newsgroup-dormant nil
1107   "List of dormant articles in the current newsgroup.")
1108
1109 (defvar gnus-newsgroup-headers nil
1110   "List of article headers in the current newsgroup.")
1111 (defvar gnus-newsgroup-headers-hashtb-by-number nil)
1112
1113 (defvar gnus-current-article nil)
1114 (defvar gnus-article-current nil)
1115 (defvar gnus-current-headers nil)
1116 (defvar gnus-have-all-headers nil "Must be either T or NIL.")
1117 (defvar gnus-last-article nil)
1118 (defvar gnus-current-kill-article nil)
1119 (defvar gnus-newsgroup-dormant-subjects nil)
1120 (defvar gnus-newsgroup-expunged-lines nil)
1121
1122 ;; Save window configuration.
1123 (defvar gnus-winconf-kill-file nil)
1124
1125 (defconst gnus-group-mode-map nil)
1126 (defvar gnus-summary-mode-map nil)
1127 (defvar gnus-article-mode-map nil)
1128 (defvar gnus-kill-file-mode-map nil)
1129
1130 ;; Format specs
1131 (defvar gnus-summary-line-format-spec nil)
1132 (defvar gnus-summary-dummy-line-format-spec nil)
1133 (defvar gnus-group-line-format-spec nil)
1134 (defvar gnus-summary-mode-line-format-spec nil)
1135 (defvar gnus-article-mode-line-format-spec nil)
1136 (defvar gnus-group-mode-line-format-spec nil)
1137
1138 (defvar gnus-reffed-article-number nil)
1139
1140 (defvar rmail-default-file (expand-file-name "~/XMBOX"))
1141 (defvar rmail-default-rmail-file (expand-file-name "~/XNEWS"))
1142
1143 (defconst gnus-summary-local-variables 
1144   '(gnus-newsgroup-name gnus-newsgroup-begin gnus-newsgroup-end 
1145     gnus-newsgroup-last-rmail gnus-newsgroup-last-mail 
1146     gnus-newsgroup-last-folder gnus-newsgroup-last-file 
1147     gnus-newsgroup-auto-expire gnus-newsgroup-unreads 
1148     gnus-newsgroup-unselected gnus-newsgroup-marked
1149     gnus-newsgroup-replied gnus-newsgroup-expirable
1150     gnus-newsgroup-processable gnus-newsgroup-killed
1151     gnus-newsgroup-bookmarks gnus-newsgroup-dormant
1152     gnus-newsgroup-dormant-subjects gnus-newsgroup-expunged-lines
1153     gnus-newsgroup-headers gnus-newsgroup-headers-hashtb-by-number
1154     gnus-current-article gnus-current-headers gnus-have-all-headers
1155     gnus-last-article gnus-article-internal-prepare-hook
1156     gnus-newsgroup-selected-overlay)
1157   "Variables that are buffer-local to the Summary buffers.")
1158
1159 (defvar gnus-mark-article-hook
1160   (list
1161    (lambda ()
1162      (or (memq gnus-current-article gnus-newsgroup-marked)
1163          (memq gnus-current-article gnus-newsgroup-dormant)
1164          (gnus-summary-mark-as-read gnus-current-article))))
1165   "A hook called when an article is selected at the first time.
1166 The hook is intended to mark an article as read (or unread)
1167 automatically when it is selected.
1168
1169 If you'd like to tick articles instead, use the following hook:
1170
1171 \(setq gnus-mark-article-hook
1172       (list
1173         (lambda ()
1174           (gnus-summary-tick-article gnus-current-article))))")
1175
1176 ;; Define some autoload functions Gnus may use.
1177 (eval-and-compile
1178   (autoload 'metamail-buffer "metamail")
1179   (autoload 'Info-goto-node "info")
1180   
1181   (autoload 'timezone-make-date-arpa-standard "timezone")
1182   (autoload 'timezone-fix-time "timezone")
1183   (autoload 'timezone-make-sortable-date "timezone")
1184   (autoload 'timezone-make-time-string "timezone")
1185   
1186   (autoload 'rmail-output "rmailout"
1187     "Append this message to Unix mail file named FILE-NAME." t)
1188   (autoload 'mail-position-on-field "sendmail")
1189   (autoload 'mail-setup "sendmail")
1190
1191   (autoload 'gnus-mail-reply-using-mhe "gnus-mh")
1192   (autoload 'gnus-mail-forward-using-mhe "gnus-mh")
1193   (autoload 'gnus-mail-other-window-using-mhe "gnus-mh")
1194   (autoload 'gnus-summary-save-in-folder "gnus-mh")
1195   (autoload 'gnus-Folder-save-name "gnus-mh")
1196   (autoload 'gnus-folder-save-name "gnus-mh")
1197   
1198   (autoload 'gnus-group-make-menu-bar "gnus-visual")
1199   (autoload 'gnus-summary-make-menu-bar "gnus-visual")
1200   (autoload 'gnus-visual-highlight-selected-summary "gnus-visual")
1201   (autoload 'gnus-visual-summary-highlight-line "gnus-visual")
1202   )
1203
1204 (put 'gnus-group-mode 'mode-class 'special)
1205 (put 'gnus-summary-mode 'mode-class 'special)
1206 (put 'gnus-article-mode 'mode-class 'special)
1207
1208 (autoload 'gnus-uu-ctl-map "gnus-uu" nil nil 'keymap)
1209 \f
1210
1211 ;; Fix by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
1212 (defun gnus-summary-position-cursor () nil)
1213 (defun gnus-group-position-cursor () nil)
1214 (fset 'gnus-summary-position-cursor 'gnus-goto-colon)
1215 (fset 'gnus-group-position-cursor 'gnus-goto-colon)
1216
1217 (defmacro gnus-eval-in-buffer-window (buffer &rest forms)
1218   "Pop to BUFFER, evaluate FORMS, and then returns to original window."
1219   (` (let ((GnusStartBufferWindow (selected-window)))
1220        (unwind-protect
1221            (progn
1222              (pop-to-buffer (, buffer))
1223              (,@ forms))
1224          (select-window GnusStartBufferWindow)))))
1225
1226 (defun gnus-make-hashtable (&optional hashsize)
1227   "Make a hash table (default and minimum size is 255).
1228 Optional argument HASHSIZE specifies the table size."
1229   (make-vector (if hashsize 
1230                    (max (gnus-create-hash-size hashsize) 255)
1231                  255) 0))
1232
1233 (defmacro gnus-gethash (string hashtable)
1234   "Get hash value of STRING in HASHTABLE."
1235   ;;(` (symbol-value (abbrev-symbol (, string) (, hashtable))))
1236   ;;(` (abbrev-expansion (, string) (, hashtable)))
1237   (` (symbol-value (intern-soft (, string) (, hashtable)))))
1238
1239 (defmacro gnus-sethash (string value hashtable)
1240   "Set hash value. Arguments are STRING, VALUE, and HASHTABLE."
1241   ;; We cannot use define-abbrev since it only accepts string as value.
1242 ;  (set (intern string hashtable) value))
1243   (` (set (intern (, string) (, hashtable)) (, value))))
1244
1245 (defsubst gnus-buffer-substring (beg end)
1246   (buffer-substring (match-beginning beg) (match-end end)))
1247
1248 (defsubst gnus-simplify-subject-re (subject)
1249   "Remove \"Re:\" from subject lines."
1250   (let ((case-fold-search t))
1251     (if (string-match "^re: *" subject)
1252         (substring subject (match-end 0))
1253       subject)))
1254
1255 \f
1256 ;;;
1257 ;;; Gnus Utility Functions
1258 ;;;
1259
1260 (defun gnus-extract-address-components (from)
1261   (let (name address)
1262     (if (string-match "([^)]+)" from)
1263         (setq name (substring from (1+ (match-beginning 0)) 
1264                               (1- (match-end 0)))))
1265     (if (string-match "\\b[^@ \t<>]+@[^@ \t<>]+\\b" from)
1266         (setq address (substring from (match-beginning 0) (match-end 0))))
1267     (if (and (not name) address)
1268         (if (string-match (concat "<" address ">") from)
1269             (setq name (substring from 0 (1- (match-beginning 0))))))
1270     (list (or name from) (or address from))))
1271
1272 (defun gnus-fetch-field (field)
1273   "Return the value of the header FIELD of current article."
1274   (save-excursion
1275     (save-restriction
1276       (gnus-narrow-to-headers)
1277       (mail-fetch-field field))))
1278
1279 (defun gnus-goto-colon ()
1280   (beginning-of-line)
1281   (search-forward ":" (save-excursion (end-of-line) (point)) t))
1282
1283 (defun gnus-narrow-to-headers ()
1284   (widen)
1285   (save-excursion
1286     (goto-char 1)
1287     (if (search-forward "\n\n")
1288         (narrow-to-region 1 (1- (point))))))
1289
1290 ;; Get a number that is suitable for hashing; bigger than MIN
1291 (defun gnus-create-hash-size (min)
1292   (let ((i 1))
1293     (while (< i min)
1294       (setq i (* 2 i)))
1295     (1- i)))
1296
1297 (defun gnus-update-format-specifications ()
1298   (setq gnus-summary-line-format-spec 
1299         (gnus-parse-format gnus-summary-line-format 
1300                            gnus-summary-line-format-alist))
1301   (setq gnus-summary-dummy-line-format-spec 
1302         (gnus-parse-format gnus-summary-dummy-line-format 
1303                            gnus-summary-dummy-line-format-alist))
1304   (if (and (memq 'newsgroup-description
1305                  (cdr (cdr (setq gnus-group-line-format-spec 
1306                                  (gnus-parse-format 
1307                                   gnus-group-line-format 
1308                                   gnus-group-line-format-alist)))))
1309            (not gnus-description-hashtb))
1310       (gnus-read-descriptions-file))
1311   (setq gnus-summary-mode-line-format-spec 
1312         (gnus-parse-format gnus-summary-mode-line-format 
1313                            gnus-summary-mode-line-format-alist))
1314   (setq gnus-article-mode-line-format-spec 
1315         (gnus-parse-format gnus-article-mode-line-format 
1316                            gnus-summary-mode-line-format-alist))
1317   (setq gnus-group-mode-line-format-spec 
1318         (gnus-parse-format gnus-group-mode-line-format 
1319                            gnus-group-mode-line-format-alist)))
1320
1321 (defun gnus-format-max-width (var length)
1322   (let (result)
1323     (if (> (length (setq result (eval var))) length)
1324         (format "%s" (substring result 0 length))
1325       (format "%s" result))))
1326
1327 (defun gnus-parse-format (format spec-alist)
1328 ;; This function parses the FORMAT string with the help of the
1329 ;; SPEC-ALIST and returns a list that can be eval'ed to return the
1330 ;; string. The list will consist of the symbol `format', a format
1331 ;; specification string, and a list of forms depending on the
1332 ;; SPEC-ALIST.
1333   (let ((max-width 0)
1334         spec flist fstring b newspec max-width elem beg)
1335     (save-excursion
1336       (set-buffer (get-buffer-create "*gnus work*"))
1337       (buffer-disable-undo (current-buffer))
1338       (gnus-add-current-to-buffer-list)
1339       (erase-buffer)
1340       (insert format)
1341       (goto-char 1)
1342       (while (re-search-forward "%[-0-9]*\\(,[0-9]*\\)*\\(.\\)" nil t)
1343         (setq spec (string-to-char (buffer-substring (match-beginning 2)
1344                                                      (match-end 2))))
1345         ;; First check if there are any specs that look anything like
1346         ;; "%12,12A", ie. with a "max width specification". These have
1347         ;; to be treated specially.
1348         (if (setq beg (match-beginning 1))
1349             (setq max-width 
1350                   (string-to-int 
1351                    (buffer-substring (1+ (match-beginning 1)) (match-end 1))))
1352           (setq max-width 0)
1353           (setq beg (match-beginning 2)))
1354         ;; Find the specification from `spec-alist'.
1355         (if (not (setq elem (cdr (assq spec spec-alist))))
1356             (setq elem '("*" ?s)))
1357         (if (not (= max-width 0))
1358             (progn
1359               (setq flist (cons (list 'gnus-format-max-width 
1360                                       (car elem) max-width) flist))
1361               (setq newspec ?s))
1362           (setq flist (cons (car elem) flist))
1363           (setq newspec (car (cdr elem))))
1364         ;; Remove the old specification (and possibly a ",12" string).
1365         (delete-region beg (match-end 2))
1366         ;; Insert the new specification.
1367         (goto-char beg)
1368         (insert newspec))
1369       (setq fstring (buffer-substring 1 (point-max)))
1370       (kill-buffer (current-buffer)))
1371     (cons 'format (cons fstring (nreverse flist)))))
1372
1373 ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
1374 (defun gnus-read-init-file ()
1375   (if (and gnus-init-file
1376            (file-exists-p gnus-init-file))
1377       (load gnus-init-file nil t)))
1378
1379 ;; Article file names when saving.
1380
1381 (defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
1382   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1383 If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group/num.
1384 Otherwise, it is like ~/News/news/group/num."
1385   (let ((default
1386           (expand-file-name
1387            (concat (if gnus-use-long-file-name
1388                        (gnus-capitalize-newsgroup newsgroup)
1389                      (gnus-newsgroup-directory-form newsgroup))
1390                    "/" (int-to-string (header-number headers)))
1391            (or gnus-article-save-directory "~/News"))))
1392     (if (and last-file
1393              (string-equal (file-name-directory default)
1394                            (file-name-directory last-file))
1395              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1396         default
1397       (or last-file default))))
1398
1399 (defun gnus-numeric-save-name (newsgroup headers &optional last-file)
1400   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1401 If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group/num.
1402 Otherwise, it is like ~/News/news/group/num."
1403   (let ((default
1404           (expand-file-name
1405            (concat (if gnus-use-long-file-name
1406                        newsgroup
1407                      (gnus-newsgroup-directory-form newsgroup))
1408                    "/" (int-to-string (header-number headers)))
1409            (or gnus-article-save-directory "~/News"))))
1410     (if (and last-file
1411              (string-equal (file-name-directory default)
1412                            (file-name-directory last-file))
1413              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1414         default
1415       (or last-file default))))
1416
1417 (defun gnus-Plain-save-name (newsgroup headers &optional last-file)
1418   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1419 If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group.
1420 Otherwise, it is like ~/News/news/group/news."
1421   (or last-file
1422       (expand-file-name
1423        (if gnus-use-long-file-name
1424            (gnus-capitalize-newsgroup newsgroup)
1425          (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
1426        (or gnus-article-save-directory "~/News"))))
1427
1428 (defun gnus-plain-save-name (newsgroup headers &optional last-file)
1429   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1430 If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group.
1431 Otherwise, it is like ~/News/news/group/news."
1432   (or last-file
1433       (expand-file-name
1434        (if gnus-use-long-file-name
1435            newsgroup
1436          (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
1437        (or gnus-article-save-directory "~/News"))))
1438
1439 ;; For subscribing new newsgroup
1440
1441 (defun gnus-subscribe-hierarchical-interactive (groups)
1442   (let ((groups (sort groups 'string<))
1443         prefixes prefix start rest ans group starts)
1444     (while groups
1445       (setq prefixes (list "^"))
1446       (while (and groups prefixes)
1447         (while (not (string-match (car prefixes) (car groups)))
1448           (setq prefixes (cdr prefixes)))
1449         (setq prefix (car prefixes))
1450         (setq start (1- (length prefix)))
1451         (if (and (string-match "[^\\.]\\." (car groups) start)
1452                  (cdr groups)
1453                  (setq prefix 
1454                        (concat "^" (substring (car groups) 0 (match-end 0))))
1455                  (string-match prefix (car (cdr groups))))
1456             (progn
1457               (setq prefixes (cons prefix prefixes))
1458               (message "Descend hierarchy %s'? ([y]nsq): " 
1459                        (substring prefix 1 (1- (length prefix))))
1460               (setq ans (read-char))
1461               (cond ((= ans ?n)
1462                      (while (and groups 
1463                                  (string-match prefix 
1464                                                (setq group (car groups))))
1465                        (setq gnus-killed-list 
1466                              (cons group gnus-killed-list))
1467                        (gnus-sethash group group gnus-killed-hashtb)
1468                        (setq groups (cdr groups)))
1469                      (setq starts (cdr starts)))
1470                     ((= ans ?s)
1471                      (while (and groups 
1472                                  (string-match prefix 
1473                                                (setq group (car groups))))
1474                        (gnus-sethash group group gnus-killed-hashtb)
1475                        (funcall gnus-subscribe-newsgroup-method 
1476                                 (car groups))
1477                        (setq groups (cdr groups)))
1478                      (setq starts (cdr starts)))
1479                     ((= ans ?q)
1480                      (while groups
1481                        (setq group (car groups))
1482                        (setq gnus-killed-list (cons group gnus-killed-list))
1483                        (gnus-sethash group group gnus-killed-hashtb)
1484                        (setq groups (cdr groups))))
1485                     (t nil)))
1486           (message "Subscribe '%s'? ([n]yq)" (car groups))
1487           (setq ans (read-char))
1488           (cond ((= ans ?y)
1489                  (funcall gnus-subscribe-newsgroup-method (car groups))
1490                  (gnus-sethash group group gnus-killed-hashtb))
1491                 ((= ans ?q)
1492                  (while groups
1493                    (setq group (car groups))
1494                    (setq gnus-killed-list (cons group gnus-killed-list))
1495                    (gnus-sethash group group gnus-killed-hashtb)
1496                    (setq groups (cdr groups))))
1497                 (t 
1498                  (setq gnus-killed-list (cons group gnus-killed-list))
1499                  (gnus-sethash group group gnus-killed-hashtb)))
1500           (setq groups (cdr groups)))))))
1501
1502 (defun gnus-subscribe-randomly (newsgroup)
1503   "Subscribe new NEWSGROUP by making it the first newsgroup."
1504   (gnus-subscribe-newsgroup newsgroup))
1505
1506 (defun gnus-subscribe-alphabetically (newgroup)
1507   "Subscribe new NEWSGROUP and insert it in alphabetical order."
1508   ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
1509   (let ((groups (cdr gnus-newsrc-assoc))
1510         before)
1511     (while (and (not before) groups)
1512       (if (string< newgroup (car (car groups)))
1513           (setq before (car (car groups)))
1514         (setq groups (cdr groups))))
1515     (gnus-subscribe-newsgroup newgroup before)))
1516
1517 (defun gnus-subscribe-hierarchically (newgroup)
1518   "Subscribe new NEWSGROUP and insert it in hierarchical newsgroup order."
1519   ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
1520   (save-excursion
1521     (set-buffer (find-file-noselect gnus-current-startup-file))
1522     (let ((groupkey newgroup)
1523           before)
1524       (while (and (not before) groupkey)
1525         (goto-char (point-min))
1526         (let ((groupkey-re
1527                (concat "^\\(" (regexp-quote groupkey) ".*\\)[!:]")))
1528           (while (and (re-search-forward groupkey-re nil t)
1529                       (progn
1530                         (setq before (buffer-substring
1531                                       (match-beginning 1) (match-end 1)))
1532                         (string< before newgroup)))))
1533         ;; Remove tail of newsgroup name (eg. a.b.c -> a.b)
1534         (setq groupkey
1535               (if (string-match "^\\(.*\\)\\.[^.]+$" groupkey)
1536                   (substring groupkey (match-beginning 1) (match-end 1)))))
1537       (gnus-subscribe-newsgroup newgroup before))))
1538
1539 (defun gnus-subscribe-interactively (newsgroup)
1540   "Subscribe new NEWSGROUP interactively.
1541 It is inserted in hierarchical newsgroup order if subscribed. If not,
1542 it is killed."
1543   (if (y-or-n-p (format "Subscribe new newsgroup: %s " newsgroup))
1544       (gnus-subscribe-hierarchically newsgroup)
1545     (setq gnus-killed-list (cons newsgroup gnus-killed-list))))
1546
1547 (defun gnus-subscribe-zombies (newsgroup)
1548   "Make new NEWSGROUP a zombie group."
1549   (setq gnus-zombie-list (cons newsgroup gnus-zombie-list)))
1550
1551 (defun gnus-subscribe-newsgroup (newsgroup &optional next)
1552   "Subscribe new NEWSGROUP.
1553 If NEXT is non-nil, it is inserted before NEXT. Otherwise it is made
1554 the first newsgroup."
1555   ;; We subscribe the group by changing its level to 3.
1556   (gnus-group-change-level 
1557    newsgroup 3 9 
1558    (if next (gnus-gethash next gnus-newsrc-hashtb)
1559      (gnus-gethash (car (nth 1 gnus-newsrc-assoc)) gnus-newsrc-hashtb)))
1560   (message "Subscribe newsgroup: %s" newsgroup))
1561
1562 ;; For directories
1563
1564 (defun gnus-newsgroup-directory-form (newsgroup)
1565   "Make hierarchical directory name from NEWSGROUP name."
1566   (let ((newsgroup (substring newsgroup 0)) ;Copy string.
1567         (len (length newsgroup))
1568         (idx 0))
1569     ;; Replace all occurrences of `.' with `/'.
1570     (while (< idx len)
1571       (if (= (aref newsgroup idx) ?.)
1572           (aset newsgroup idx ?/))
1573       (setq idx (1+ idx)))
1574     newsgroup
1575     ))
1576
1577 (defun gnus-make-directory (directory)
1578   "Make DIRECTORY recursively."
1579   (let ((directory (expand-file-name directory default-directory)))
1580     (or (file-exists-p directory)
1581         (gnus-make-directory-1 "" directory))
1582     ))
1583
1584 (defun gnus-make-directory-1 (head tail)
1585   (cond ((string-match "^/\\([^/]+\\)" tail)
1586          ;; ange-ftp interferes with calling match-* after
1587          ;; calling file-name-as-directory.
1588          (let ((beg (match-beginning 1))
1589                (end (match-end 1)))
1590            (setq head (concat (file-name-as-directory head)
1591                               (substring tail beg end)))
1592            (or (file-exists-p head)
1593                (call-process "mkdir" nil nil nil head))
1594            (gnus-make-directory-1 head (substring tail end))))
1595         ((string-equal tail "") t)
1596         ))
1597
1598 (defun gnus-capitalize-newsgroup (newsgroup)
1599   "Capitalize NEWSGROUP name with treating '.' and '-' as part of words."
1600   ;; Suggested by "Jonathan I. Kamens" <jik@pit-manager.MIT.EDU>.
1601   (let ((current-syntax-table (syntax-table)))
1602     (unwind-protect
1603         (progn
1604           (set-syntax-table (copy-syntax-table current-syntax-table))
1605           (modify-syntax-entry ?- "w")
1606           (modify-syntax-entry ?. "w")
1607           (capitalize newsgroup))
1608       (set-syntax-table current-syntax-table))))
1609
1610 ;; Var
1611
1612 (defun gnus-simplify-subject (subject &optional re-only)
1613   "Remove `Re:' and words in parentheses.
1614 If optional argument RE-ONLY is non-nil, strip `Re:' only."
1615   (let ((case-fold-search t))           ;Ignore case.
1616     ;; Remove `Re:' and `Re^N:'.
1617     (if (string-match "^re:[ \t]*" subject)
1618         (setq subject (substring subject (match-end 0))))
1619     ;; Remove words in parentheses from end.
1620     (or re-only
1621         (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1622           (setq subject (substring subject 0 (match-beginning 0)))))
1623     ;; Return subject string.
1624     subject
1625     ))
1626
1627 (defun gnus-add-current-to-buffer-list ()
1628   (setq gnus-buffer-list (cons (current-buffer) gnus-buffer-list)))
1629
1630 ;; Functions accessing headers.
1631 ;; Functions are more convenient than macros in some case.
1632
1633 (defun gnus-header-number (header)
1634   "Return article number in HEADER."
1635   (header-number header))
1636
1637 (defun gnus-header-subject (header)
1638   "Return subject string in HEADER."
1639   (header-subject header))
1640
1641 (defun gnus-header-from (header)
1642   "Return author string in HEADER."
1643   (header-from header))
1644
1645 (defun gnus-header-xref (header)
1646   "Return xref string in HEADER."
1647   (header-xref header))
1648
1649 (defun gnus-header-lines (header)
1650   "Return lines in HEADER."
1651   (header-lines header))
1652
1653 (defun gnus-header-date (header)
1654   "Return date in HEADER."
1655   (header-date header))
1656
1657 (defun gnus-header-id (header)
1658   "Return Id in HEADER."
1659   (header-id header))
1660
1661 (defun gnus-header-references (header)
1662   "Return references in HEADER."
1663   (header-references header))
1664
1665 (defun gnus-clear-system ()
1666   "Clear all variables and buffers."
1667   ;; Clear Gnus variables.
1668   (let ((variables gnus-variable-list))
1669     (while variables
1670       (set (car variables) nil)
1671       (setq variables (cdr variables))))
1672   ;; Clear other internal variables.
1673   (setq gnus-list-of-killed-groups nil
1674         gnus-have-read-active-file nil
1675         gnus-newsrc-assoc nil
1676         gnus-newsrc-hashtb nil
1677         gnus-killed-list nil
1678         gnus-zombie-list nil
1679         gnus-killed-hashtb nil
1680         gnus-active-hashtb nil
1681         gnus-moderated-list nil
1682         gnus-description-hashtb nil
1683         gnus-newsgroup-headers nil
1684         gnus-newsgroup-headers-hashtb-by-number nil
1685         gnus-current-select-method nil)
1686   ;; Kill the startup file.
1687   (and gnus-current-startup-file
1688        (get-file-buffer gnus-current-startup-file)
1689        (kill-buffer (get-file-buffer gnus-current-startup-file)))
1690   (setq gnus-current-startup-file nil)
1691   (gnus-dribble-clear)
1692   ;; Kill Gnus buffers.
1693   (while gnus-buffer-list
1694     (if (and (get-buffer (car gnus-buffer-list))
1695              (buffer-name (get-buffer (car gnus-buffer-list))))
1696         (kill-buffer (car gnus-buffer-list)))
1697     (setq gnus-buffer-list (cdr gnus-buffer-list))))
1698
1699 (defun gnus-configure-windows (action &optional force)
1700   "Configure Gnus windows according to the next ACTION.
1701 The ACTION is either a symbol, such as `summary', or a
1702 configuration list such as `(1 1 2)'.  If ACTION is not a list,
1703 configuration list is got from the variable gnus-window-configuration.
1704 If FORCE is non-nil, the updating will be done whether it is necessary
1705 or not."
1706   (let* ((windows
1707           (if (listp action) action 
1708             (if (listp gnus-window-configuration)
1709                 (car (cdr (assq action gnus-window-configuration)))
1710               gnus-window-configuration)))
1711          (grpwin (get-buffer-window gnus-group-buffer))
1712          (subwin (get-buffer-window gnus-summary-buffer))
1713          (artwin (get-buffer-window gnus-article-buffer))
1714          (winsum nil)
1715          (height nil)
1716          (grpheight 0)
1717          (subheight 0)
1718          (artheight 0))
1719     (if (and (symbolp windows) (fboundp windows))
1720         (funcall windows action)
1721       (if (and (not force)
1722                (or (null windows)               ;No configuration is specified.
1723                    (and (eq (null grpwin)
1724                             (zerop (nth 0 windows)))
1725                         (eq (null subwin)
1726                             (zerop (nth 1 windows)))
1727                         (eq (null artwin)
1728                             (zerop (nth 2 windows))))))
1729           ;; No need to change window configuration.
1730           nil
1731         (select-window (or grpwin subwin artwin (selected-window)))
1732         ;; First of all, compute the height of each window.
1733         (cond (gnus-use-full-window
1734                ;; Take up the entire screen.
1735                (delete-other-windows)
1736                (setq height (window-height (selected-window))))
1737               (t
1738                (setq height (+ (if grpwin (window-height grpwin) 0)
1739                                (if subwin (window-height subwin) 0)
1740                                (if artwin (window-height artwin) 0)))))
1741         ;; The Newsgroup buffer exits always. So, use it to extend the
1742         ;; Group window so as to get enough window space.
1743         (switch-to-buffer gnus-group-buffer 'norecord)
1744         (and (get-buffer gnus-summary-buffer)
1745              (delete-windows-on gnus-summary-buffer))
1746         (and (get-buffer gnus-article-buffer)
1747              (delete-windows-on gnus-article-buffer))
1748         ;; Compute expected window height.
1749         (setq winsum (apply (function +) windows))
1750         (if (not (zerop (nth 0 windows)))
1751             (setq grpheight (max window-min-height
1752                                  (/ (* height (nth 0 windows)) winsum))))
1753         (if (not (zerop (nth 1 windows)))
1754             (setq subheight (max window-min-height
1755                                  (/ (* height (nth 1 windows)) winsum))))
1756         (if (not (zerop (nth 2 windows)))
1757             (setq artheight (max window-min-height
1758                                  (/ (* height (nth 2 windows)) winsum))))
1759         (setq height (+ grpheight subheight artheight))
1760         (enlarge-window (max 0 (- height (window-height (selected-window)))))
1761         ;; Then split the window.
1762         (and (not (zerop artheight))
1763              (or (not (zerop grpheight))
1764                  (not (zerop subheight)))
1765              (split-window-vertically (+ grpheight subheight)))
1766         (and (not (zerop grpheight))
1767              (not (zerop subheight))
1768              (split-window-vertically grpheight))
1769         ;; Then select buffers in each window.
1770         (and (not (zerop grpheight))
1771              (progn
1772                (switch-to-buffer gnus-group-buffer 'norecord)
1773                (other-window 1)))
1774         (and (not (zerop subheight))
1775              (progn
1776                (switch-to-buffer gnus-summary-buffer 'norecord)
1777                (other-window 1)))
1778         (and (not (zerop artheight))
1779              (progn
1780                ;; If Article buffer does not exist, it will be created
1781                ;; and initialized.
1782                (gnus-article-setup-buffer)
1783                (switch-to-buffer gnus-article-buffer 'norecord)))))
1784     ))
1785
1786 (defun gnus-window-configuration-split (action)
1787   (switch-to-buffer gnus-group-buffer t)
1788   (delete-other-windows)
1789   (split-window-horizontally)
1790   (cond ((or (eq action 'newsgroup) (eq action 'summary))
1791          (if (and (get-buffer gnus-summary-buffer)
1792                   (buffer-name gnus-summary-buffer))
1793              (switch-to-buffer-other-window gnus-summary-buffer)))
1794         ((eq action 'article)
1795          (switch-to-buffer gnus-summary-buffer t)
1796          (other-window 1)
1797          (gnus-article-setup-buffer)
1798          (switch-to-buffer gnus-article-buffer t))))
1799
1800 (defun gnus-version ()
1801   "Version numbers of this version of Gnus."
1802   (interactive)
1803   (let ((methods gnus-valid-select-methods)
1804         (mess gnus-version)
1805         meth)
1806     ;; Go through all the legal select methods and add their version
1807     ;; numbers to the total version string. Only the backends that are
1808     ;; currently in use will have their message numbers taken into
1809     ;; consideration. 
1810     (while methods
1811       (setq meth (intern (concat (car (car methods)) "-version")))
1812       (and (boundp meth)
1813            (stringp (symbol-value meth))
1814            (setq mess (concat mess "; " (symbol-value meth))))
1815       (setq methods (cdr methods)))
1816     (message mess)))
1817
1818 (defun gnus-info-find-node ()
1819   "Find Info documentation of Gnus."
1820   (interactive)
1821   ;; Enlarge info window if needed.
1822   (cond ((eq major-mode 'gnus-group-mode)
1823          (gnus-configure-windows '(1 0 0)) ;Take all windows.
1824          (pop-to-buffer gnus-group-buffer))
1825         ((eq major-mode 'gnus-summary-mode)
1826          (gnus-configure-windows '(0 1 0)) ;Take all windows.
1827          (pop-to-buffer gnus-summary-buffer)))
1828   (Info-goto-node (car (cdr (assq major-mode gnus-info-nodes)))))
1829
1830 (defun gnus-bug ()
1831   "Send a bug report to the Gnus maintainers."
1832   (interactive)
1833   (pop-to-buffer "*Gnus Bug*")
1834   (erase-buffer)
1835   (mail-setup gnus-maintainer "[Gnus Bug Report] " nil nil nil nil)
1836   (goto-char (point-min))
1837   (search-forward mail-header-separator)
1838   (forward-line 1)
1839   (insert (format "%s\n%s\n\n" (gnus-version) (emacs-version)))
1840   (message ""))
1841
1842 (defun gnus-overload-functions (&optional overloads)
1843   "Overload functions specified by optional argument OVERLOADS.
1844 If nothing is specified, use the variable gnus-overload-functions."
1845   (let ((defs nil)
1846         (overloads (or overloads gnus-overload-functions)))
1847     (while overloads
1848       (setq defs (car overloads))
1849       (setq overloads (cdr overloads))
1850       ;; Load file before overloading function if necessary.  Make
1851       ;; sure we cannot use `require' always.
1852       (and (not (fboundp (car defs)))
1853            (car (cdr (cdr defs)))
1854            (load (car (cdr (cdr defs))) nil 'nomessage))
1855       (fset (car defs) (car (cdr defs)))
1856       )))
1857
1858 ;; List and range functions
1859
1860 (defun gnus-last-element (list)
1861   "Return last element of LIST."
1862   (while (cdr list)
1863     (setq list (cdr list)))
1864   (car list))
1865
1866 (defun gnus-set-difference (list1 list2)
1867   "Return a list of elements of LIST1 that do not appear in LIST2."
1868   (let ((list1 (copy-sequence list1)))
1869     (while list2
1870       (setq list1 (delq (car list2) list1))
1871       (setq list2 (cdr list2)))
1872     list1
1873     ))
1874
1875 (defun gnus-intersection (list1 list2)      
1876   (let ((result nil))
1877     (while list2
1878       (if (memq (car list2) list1)
1879           (setq result (cons (car list2) result)))
1880       (setq list2 (cdr list2)))
1881     result
1882     ))
1883
1884 (defun gnus-compress-sequence (numbers &optional always-list)
1885   "Convert list of numbers to a list of ranges or a single range.
1886 If ALWAYS-LIST is non-nil, this function will always release a list of
1887 ranges."
1888   (let* ((first (car numbers))
1889          (last (car numbers))
1890          result)
1891     (if (null numbers)
1892         nil
1893       (while numbers
1894         (cond ((= last (car numbers)) nil) ;Omit duplicated number
1895               ((= (1+ last) (car numbers)) ;Still in sequence
1896                (setq last (car numbers)))
1897               (t                                ;End of one sequence
1898                (setq result (cons (cons first last) result))
1899                (setq first (car numbers))
1900                (setq last  (car numbers))))
1901         (setq numbers (cdr numbers)))
1902       (if (and (not always-list) (null result))
1903           (cons first last)
1904         (nreverse (cons (cons first last) result))))))
1905
1906 (defun gnus-uncompress-sequence (ranges)
1907   "Expand a list of ranges into a list of numbers.
1908 RANGES is either a single range on the form `(num . num)' or a list of
1909 these ranges."
1910   (let (first last result)
1911     (if (null ranges)
1912         nil
1913       (if (atom (car ranges))
1914           (progn
1915             (setq first (car ranges))
1916             (setq last (cdr ranges))
1917             (while (<= first last)
1918               (setq result (cons first result))
1919               (setq first (1+ first))))
1920         (while ranges
1921           (setq first (car (car ranges)))
1922           (setq last  (cdr (car ranges)))
1923           (while (<= first last)
1924             (setq result (cons first result))
1925             (setq first (1+ first)))
1926           (setq ranges (cdr ranges))))
1927       (nreverse result))))
1928
1929 (defun gnus-add-to-range (ranges list)
1930   "Return a list of ranges that has all articles from both RANGES and LIST.
1931 Note: LIST has to be sorted over `<'."
1932   (let* ((ranges (if (and ranges (atom (car ranges))) (list ranges) ranges))
1933          (inrange ranges)
1934          range nranges first last)
1935     (if (not ranges)
1936         (gnus-compress-sequence list t)
1937       (while (and ranges list)
1938         (setq range (car ranges))
1939         (while (and list (<= (car list) (cdr range)))
1940           (setq list (cdr list)))
1941         (while (and list (= (1- (car list)) (cdr range)))
1942           (setcdr range (car list))
1943           (setq list (cdr list)))
1944         (if (and list (and (> (car list) (cdr range)) (cdr ranges)
1945                            (< (car list) (car (car (cdr ranges))))))
1946             (setcdr ranges (cons (cons (car list) (car list)) (cdr ranges))))
1947         (setq ranges (cdr ranges)))
1948       (if (and list (not ranges))
1949           (setq inrange (nconc inrange (gnus-compress-sequence list t))))
1950       (setq ranges inrange)
1951       (while ranges
1952         (if (and (cdr ranges) (>= (1+ (cdr (car ranges)))
1953                                   (car (car (cdr ranges)))))
1954             (progn
1955               (setcdr (car ranges) (cdr (car (cdr ranges))))
1956               (setcdr ranges (cdr (cdr ranges))))
1957           (setq ranges (cdr ranges))))
1958       (if (not (cdr inrange))
1959           (car inrange)
1960         inrange))))
1961
1962 (defun gnus-remove-from-range (ranges list)
1963   "Return a list of ranges that has all articles from LIST removed from RANGES.
1964 Note: LIST has to be sorted over `<'."
1965   ;; !!! This function shouldn't look like this, but I've got a headache.
1966   (gnus-compress-sequence 
1967    (gnus-set-difference 
1968     (gnus-uncompress-sequence ranges) list)))
1969
1970 (defun gnus-member-of-range (number ranges)
1971   (let ((not-stop t))
1972     (while (and ranges not-stop)
1973       (if (and (>= number (car (car ranges)))
1974                (<= number (cdr (car ranges))))
1975           (setq not-stop nil))
1976       (setq ranges (cdr ranges)))
1977     (not not-stop)))
1978
1979 \f
1980 ;;;
1981 ;;; Gnus Group Mode
1982 ;;;
1983
1984 (if gnus-group-mode-map
1985     nil
1986   (setq gnus-group-mode-map (make-keymap))
1987   (suppress-keymap gnus-group-mode-map)
1988   (define-key gnus-group-mode-map " " 'gnus-group-read-group)
1989   (define-key gnus-group-mode-map "=" 'gnus-group-select-group)
1990   (define-key gnus-group-mode-map "j" 'gnus-group-jump-to-group)
1991   (define-key gnus-group-mode-map "n" 'gnus-group-next-unread-group)
1992   (define-key gnus-group-mode-map "p" 'gnus-group-prev-unread-group)
1993   (define-key gnus-group-mode-map [del] 'gnus-group-prev-unread-group)
1994   (define-key gnus-group-mode-map "N" 'gnus-group-next-group)
1995   (define-key gnus-group-mode-map "P" 'gnus-group-prev-group)
1996   (define-key gnus-group-mode-map "\M-n" 'gnus-group-next-unread-group-same-level)
1997   (define-key gnus-group-mode-map "\M-p" 'gnus-group-prev-unread-group-same-level)
1998   (define-key gnus-group-mode-map "\r" 'gnus-group-select-group)
1999   (define-key gnus-group-mode-map "u" 'gnus-group-unsubscribe-current-group)
2000   (define-key gnus-group-mode-map "U" 'gnus-group-unsubscribe-group)
2001   (define-key gnus-group-mode-map "c" 'gnus-group-catchup-current)
2002   (define-key gnus-group-mode-map "C" 'gnus-group-catchup-current-all)
2003   (define-key gnus-group-mode-map "l" 'gnus-group-list-groups)
2004   (define-key gnus-group-mode-map "L" 'gnus-group-list-all-groups)
2005   (define-key gnus-group-mode-map "m" 'gnus-group-mail)
2006   (define-key gnus-group-mode-map "g" 'gnus-group-get-new-news)
2007   (define-key gnus-group-mode-map "\M-g" 'gnus-group-get-new-news-this-group)
2008   (define-key gnus-group-mode-map "R" 'gnus-group-restart)
2009   (define-key gnus-group-mode-map "r" 'gnus-group-read-init-file)
2010   (define-key gnus-group-mode-map "B" 'gnus-group-browse-foreign-server)
2011   (define-key gnus-group-mode-map "b" 'gnus-group-check-bogus-groups)
2012   (define-key gnus-group-mode-map "F" 'gnus-find-new-newsgroups)
2013   (define-key gnus-group-mode-map "\C-c\C-d" 'gnus-group-describe-group)
2014   (define-key gnus-group-mode-map "\M-d" 'gnus-group-describe-all-groups)
2015   (define-key gnus-group-mode-map "\C-c\C-a" 'gnus-group-apropos)
2016   (define-key gnus-group-mode-map "\C-c\M-C-a" 'gnus-group-description-apropos)
2017   (define-key gnus-group-mode-map "a" 'gnus-group-post-news)
2018   (define-key gnus-group-mode-map "\M-a" 'gnus-group-add-newsgroup)
2019   (define-key gnus-group-mode-map "\M-e" 'gnus-group-edit-newsgroup)
2020   (define-key gnus-group-mode-map "\ek" 'gnus-group-edit-local-kill)
2021   (define-key gnus-group-mode-map "\eK" 'gnus-group-edit-global-kill)
2022   (define-key gnus-group-mode-map "\C-k" 'gnus-group-kill-group)
2023   (define-key gnus-group-mode-map "\C-y" 'gnus-group-yank-group)
2024   (define-key gnus-group-mode-map "\C-w" 'gnus-group-kill-region)
2025   (define-key gnus-group-mode-map "\M-z" 'gnus-group-kill-all-zombies)
2026   (define-key gnus-group-mode-map "\C-x\C-t" 'gnus-group-transpose-groups)
2027   (define-key gnus-group-mode-map "\C-c\C-l" 'gnus-group-list-killed)
2028   (define-key gnus-group-mode-map "\C-c\C-k" 'gnus-group-list-killed)
2029   (define-key gnus-group-mode-map "\C-c\C-z" 'gnus-group-list-zombies)
2030   (define-key gnus-group-mode-map "\C-c\C-x" 'gnus-group-expire-articles)
2031   (define-key gnus-group-mode-map "\C-c\M-\C-x" 'gnus-group-expire-all-groups)
2032   (define-key gnus-group-mode-map "V" 'gnus-version)
2033   (define-key gnus-group-mode-map "S" 'gnus-group-set-current-level)
2034   (define-key gnus-group-mode-map "s" 'gnus-group-save-newsrc)
2035   (define-key gnus-group-mode-map "z" 'gnus-group-suspend)
2036   (define-key gnus-group-mode-map "Z" 'gnus-group-clear-dribble)
2037   (define-key gnus-group-mode-map "q" 'gnus-group-exit)
2038   (define-key gnus-group-mode-map "Q" 'gnus-group-quit)
2039   (define-key gnus-group-mode-map "?" 'gnus-group-describe-briefly)
2040   (define-key gnus-group-mode-map "\C-c\C-i" 'gnus-info-find-node)
2041   (define-key gnus-group-mode-map [mouse-2] 'gnus-mouse-pick-group)
2042   (gnus-group-make-menu-bar))
2043
2044 (defun gnus-group-mode ()
2045   "Major mode for reading news.
2046 All normal editing commands are switched off.
2047 The following commands are available:
2048
2049 \\<gnus-group-mode-map>
2050 \\[gnus-group-read-group]\t Choose the current group
2051 \\[gnus-group-select-group]\t Select the current group without selecting the first article
2052 \\[gnus-group-jump-to-group]\t Go to some group
2053 \\[gnus-group-next-unread-group]\t Go to the next unread group
2054 \\[gnus-group-prev-unread-group]\t Go to the previous unread group
2055 \\[gnus-group-next-group]\t Go to the next group
2056 \\[gnus-group-prev-group]\t Go to the previous group
2057 \\[gnus-group-next-unread-group-same-level]\t Go to the next unread group on the same level
2058 \\[gnus-group-prev-unread-group-same-level]\t Go to the previous unread group un the same level
2059 \\[gnus-group-unsubscribe-current-group]\t (Un)subscribe to the current group
2060 \\[gnus-group-unsubscribe-group]\t (Un)subscribe to some group
2061 \\[gnus-group-catchup-current]\t Mark all unread articles in the current group as read
2062 \\[gnus-group-catchup-current-all]\t Mark all alrticles in the current group as read
2063 \\[gnus-group-list-groups]\t List groups that have unread articles
2064 \\[gnus-group-list-all-groups]\t List all groups
2065 \\[gnus-group-mail]\t Compose a mail
2066 \\[gnus-group-get-new-news]\t Look for new news
2067 \\[gnus-group-get-new-news-this-group]\t Look for new news for the current group
2068 \\[gnus-group-restart]\t Restart Gnus
2069 \\[gnus-group-save-newsrc]\t Save the startup file(s)
2070 \\[gnus-group-browse-foreign-server]\t Browse a foreign (NNTP) server
2071 \\[gnus-group-check-bogus-groups]\t Check for and delete bogus newsgroups
2072 \\[gnus-find-new-newsgroups]\t Find new newsgroups
2073 \\[gnus-group-describe-group]\t Describe the current newsgroup
2074 \\[gnus-group-describe-all-groups]\t Describe all newsgroups
2075 \\[gnus-group-post-news]\t Post an article to some newsgroup
2076 \\[gnus-group-add-newsgroup]\t Add a newsgroup entry
2077 \\[gnus-group-edit-newsgroup]\t Edit a newsgroup entry
2078 \\[gnus-group-edit-local-kill]\t Edit a local kill file
2079 \\[gnus-group-edit-global-kill]\t Edit the global kill file
2080 \\[gnus-group-kill-group]\t Kill the current newsgroup
2081 \\[gnus-group-yank-group]\t Yank a previously killed newsgroup
2082 \\[gnus-group-kill-region]\t Kill all newsgroups between point and mark
2083 \\[gnus-group-kill-all-zombies]\t Kill all zombie newsgroups
2084 \\[gnus-group-transpose-groups]\t Transpose two newsgroups
2085 \\[gnus-group-list-killed]\t List all killed newsgroups
2086 \\[gnus-group-list-zombies]\t List all zombie newsgroups
2087 \\[gnus-group-expire-articles]\t Expire the expirable articles in the current newsgroup
2088 \\[gnus-group-expire-all-groups]\t Expire expirable articles in all newsgroups
2089 \\[gnus-version]\t Display the current Gnus version
2090 \\[gnus-group-set-current-level]\t Set the level of the current newsgroup
2091 \\[gnus-group-suspend]\t Suspend Gnus
2092 \\[gnus-group-clear-dribble]\t Clear the dribble buffer
2093 \\[gnus-group-exit]\t Stop reading news
2094 \\[gnus-group-quit]\t Stop reading news without saving the startup files
2095 \\[gnus-group-describe-briefly]\t Give a brief description of the current mode
2096 \\[gnus-info-find-node]\t Find the info pages for Gnus
2097 "
2098   (interactive)
2099   (kill-all-local-variables)
2100   (setq mode-line-modified "--- ")
2101   (setq major-mode 'gnus-group-mode)
2102   (setq mode-name "Newsgroup")
2103   (gnus-group-set-mode-line)
2104   (setq mode-line-process nil)
2105   (use-local-map gnus-group-mode-map)
2106   (buffer-disable-undo (current-buffer))
2107   (setq truncate-lines t)
2108   (setq buffer-read-only t)
2109   (run-hooks 'gnus-group-mode-hook))
2110
2111 (defun gnus-mouse-pick-group (e)
2112   (interactive "e")
2113   (mouse-set-point e)
2114   (gnus-group-read-group nil))
2115
2116 ;;;###autoload
2117 (defun gnus-no-server (&optional arg)
2118   "Read network news.
2119 If ARG is non-nil and a positive number, Gnus will use that as the
2120 startup level. If ARG is non-nil and not a positive number, Gnus will
2121 prompt the user for the name of an NNTP server to use.
2122 As opposed to `gnus', this command will not connect to the local server."
2123   (interactive "P")
2124   (gnus arg t))
2125
2126 (defalias '\(ding\) 'gnus)
2127
2128 ;;;###autoload
2129 (defun gnus (&optional arg dont-connect)
2130   "Read network news.
2131 If ARG is non-nil and a positive number, Gnus will use that as the
2132 startup level. If ARG is non-nil and not a positive number, Gnus will
2133 prompt the user for the name of an NNTP server to use."
2134   (interactive "P")
2135   (gnus-clear-system)
2136   (gnus-read-init-file)
2137   (if (and gnus-signature-file mail-signature)
2138       (setq gnus-signature-file nil))
2139   (let ((level (and arg (numberp arg) (> arg 0) arg)))
2140     (unwind-protect
2141         (progn
2142           (switch-to-buffer (get-buffer-create gnus-group-buffer))
2143           (gnus-add-current-to-buffer-list)
2144           (gnus-group-mode)
2145           (or dont-connect (gnus-start-news-server (and arg (not level)))))
2146       (if (and (not dont-connect) 
2147                (not (gnus-server-opened gnus-select-method)))
2148           (gnus-group-quit)
2149         ;; NNTP server is successfully open. 
2150         (gnus-update-format-specifications)
2151         (let ((buffer-read-only nil))
2152           (erase-buffer)
2153           (if (not gnus-inhibit-startup-message)
2154               (progn
2155                 (gnus-group-startup-message)
2156                 (sit-for 0))))
2157         (run-hooks 'gnus-startup-hook)
2158         (gnus-setup-news nil level)
2159         (gnus-dribble-open)
2160         (or (not gnus-novice-user)
2161             gnus-expert-user
2162             (gnus-group-describe-briefly)) ;Show brief help message.
2163         (gnus-group-list-groups (or level 5))))))
2164
2165 (defun gnus-group-startup-message (&optional x y)
2166   "Insert startup message in current buffer."
2167   ;; Insert the message.
2168   (erase-buffer)
2169   (insert
2170    (format "
2171 %s
2172        A newsreader 
2173   for GNU Emacs
2174
2175     Based on GNUS 
2176          written by 
2177  Masanobu UMEDA
2178
2179 Lars Ingebrigtsen 
2180   larsi@ifi.uio.no
2181
2182            gnus-version))
2183   ;; And then hack it.
2184   ;; 18 is the longest line.
2185   (indent-rigidly (point-min) (point-max) 
2186                   (/ (max (- (window-width) (or x 28)) 0) 2))
2187   (goto-char (point-min))
2188   ;; +4 is fuzzy factor.
2189   (insert-char ?\n (/ (max (- (window-height) (or y 12)) 0) 2)))
2190
2191 (defun gnus-group-list-groups (level &optional unread)
2192   "List newsgroups with level LEVEL or lower that have unread alticles.
2193 Default is 5, which lists all subscribed groups.
2194 If argument UNREAD is non-nil, groups with no unread articles are also listed."
2195   (interactive "P")
2196   (setq level (or level 5))
2197   (let ((case-fold-search nil)
2198         (group (gnus-group-group-name)))
2199     (set-buffer gnus-group-buffer)      ;May call from out of Group buffer
2200     (gnus-group-prepare level unread)
2201     (if (zerop (buffer-size))
2202         ;; Suggested by Andrew Eskilsson <pi92ae@lelle.pt.hk-r.se>.
2203         (message "No news is horrible news")
2204       (goto-char (point-min))
2205       (if (not group)
2206           ()
2207         ;; Find the right group to put point on. If the current group
2208         ;; has disapeared in the new listing, try to find the next
2209         ;; one. If no next one can be found, just leave point at the
2210         ;; first newsgroup in the buffer.
2211         (if (not (re-search-forward (gnus-group-make-regexp group) nil t))
2212             (let ((newsrc (nthcdr 3 (gnus-gethash group gnus-newsrc-hashtb))))
2213               (while (and newsrc
2214                           (not (re-search-forward 
2215                                 (gnus-group-make-regexp (car (car newsrc))) 
2216                                 nil t)))
2217                 (setq newsrc (cdr newsrc))))))
2218       ;; Adjust cursor point.
2219       (gnus-group-position-cursor))))
2220
2221 (defun gnus-group-prepare (level &optional all lowest) 
2222   "List all newsgroups with unread articles of level LEVEL or lower.
2223 If ALL is non-nil, list groups that have no unread articles.
2224 If LOWEST is non-nil, list all newsgroups of level LOWEST or higher."
2225   (set-buffer (get-buffer-create gnus-group-buffer))
2226   (gnus-add-current-to-buffer-list)
2227   (let ((buffer-read-only nil)
2228         (newsrc (cdr gnus-newsrc-assoc))
2229         (zombie gnus-zombie-list)
2230         (killed gnus-killed-list)
2231         info clevel unread active group)
2232     (if (not lowest)
2233         (setq lowest 1))
2234     (erase-buffer)
2235     (if (< lowest 8)
2236         ;; List alive newsgroups.
2237         (while newsrc
2238           (setq info (car newsrc)
2239                 group (car info)
2240                 newsrc (cdr newsrc)
2241                 unread (car (gnus-gethash group gnus-newsrc-hashtb)))
2242           (if (and unread ; This group might be bogus
2243                    (or all (eq unread t) 
2244                        (and (> unread 0)
2245                             (> unread 
2246                                (length (cdr (assq 'dormant (nth 3 info)))))))
2247                    (and (<= (setq clevel (car (cdr info))) level))
2248                    (>= clevel lowest))
2249               (gnus-group-insert-group-line 
2250                nil group (car (cdr info)) (nth 3 info) unread
2251                (nth 4 info)))))
2252
2253     ;; List zombies and killed lists somehwat faster, which was
2254     ;; suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>. It does
2255     ;; this by ignoring the group format specification altogether.
2256     (let ((lists (list 'gnus-zombie-list 'gnus-killed-list))
2257           mark beg)
2258       (while lists
2259         (if (or (and (eq (car lists) 'gnus-zombie-list)
2260                      (progn (setq mark ?Z)
2261                             (and (>= level 8) (<= lowest 8))))
2262                 (and (eq (car lists) 'gnus-killed-list)
2263                      (progn (setq mark ?K)
2264                             (and (>= level 9) (<= lowest 9)))))
2265             (progn
2266               (setq newsrc (set (car lists)
2267                                 (sort (symbol-value (car lists)) 
2268                                       (function string<))))
2269               (while newsrc
2270                 (setq group (car newsrc)
2271                       newsrc (cdr newsrc))
2272                 (insert (format " %c    *: %s" mark group))
2273                 (setq beg (point))
2274                 (insert (format " %s  %d\n" group 
2275                                 (if (= mark ?Z) 8 9)))
2276                 (set-text-properties beg (1- (point))
2277                                      '(invisible t)))))
2278         (setq lists (cdr lists))))
2279
2280     (gnus-group-set-mode-line)
2281     (setq gnus-have-all-newsgroups all)
2282     (run-hooks 'gnus-group-prepare-hook)))
2283
2284 (defun gnus-group-real-name (group)
2285   "Find the real name of a foreign newsgroup."
2286   (if (string-match (concat "^" gnus-foreign-group-prefix) group)
2287       (substring group (match-end 0))
2288     group))
2289
2290 (defun gnus-group-set-info (info)
2291   (let ((entry (gnus-gethash (car info) gnus-newsrc-hashtb)))
2292     (if entry
2293         (progn
2294           (setcar (nthcdr 2 entry) info)
2295           (if (and (not (eq (car entry) t)) 
2296                    (gnus-gethash (car info) gnus-active-hashtb))
2297               (setcar entry (length (gnus-list-of-unread-articles 
2298                                      (car info))))))
2299       (error "No such group: %s" (car info)))))
2300
2301 (defun gnus-group-update-group-line ()
2302   "This function updates the current line in the newsgroup buffer and
2303 moves the point to the colon."
2304   (let ((group (gnus-group-group-name))
2305         (buffer-read-only nil))
2306     (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
2307       (if entry
2308           (gnus-dribble-enter 
2309            (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2310                    ")"))))
2311     (beginning-of-line)
2312     (delete-region (point) (save-excursion (forward-line 1) (point)))
2313     (gnus-group-insert-group-line-info group)
2314     (forward-line -1)
2315     (gnus-group-position-cursor)))
2316
2317 (defun gnus-group-insert-group-line-info (group)
2318   (let ((entry (gnus-gethash group gnus-newsrc-hashtb)) 
2319         active info)
2320     (if entry
2321         (progn
2322           (setq info (nth 2 entry))
2323           (gnus-group-insert-group-line 
2324            nil group (nth 1 info) (nth 3 info) (car entry) (nth 4 info)))
2325       (setq active (gnus-gethash group gnus-active-hashtb))
2326       (gnus-group-insert-group-line 
2327        nil group (if (member group gnus-zombie-list) 8 9)
2328        nil (- (1+ (cdr active)) (car active)) nil))))
2329
2330 (defun gnus-group-insert-group-line (gformat group level marked number method)
2331   (let* ((gformat (or gformat gnus-group-line-format-spec))
2332          (active (gnus-gethash group gnus-active-hashtb))
2333          (number-total (if active (1+ (- (cdr active) (car active)))))
2334          (number-of-dormant (length (cdr (assq 'dormant marked))))
2335          (number-of-ticked (length (cdr (assq 'tick marked))))
2336          (number-of-ticked-and-dormant
2337           (+ number-of-ticked number-of-dormant))
2338          (number-of-unread-unticked 
2339           (if (numberp number) (- number number-of-ticked number-of-dormant)
2340             "*"))
2341          (number-of-read
2342           (if (numberp number)
2343               (- number-total number)
2344             "*"))
2345          (subscribed (cond ((< level 6) ? )
2346                            ((< level 8) ?U)
2347                            ((= level 8) ?Z)
2348                            (t ?K)))
2349          (buffer-read-only nil)
2350          (newsgroup-description 
2351           (if gnus-description-hashtb
2352               (or (gnus-gethash group gnus-description-hashtb) "")
2353             ""))
2354          (moderated (if (member group gnus-moderated-list) ?m ? ))
2355          (moderated-string (if (eq moderated ?m) "(m)" ""))
2356          (news-server (or (car (cdr method)) ""))
2357          (news-method (or (car method) ""))
2358          (news-method-string 
2359           (if method (format "(%s:%s)" (car method) (car (cdr method))) ""))
2360          (number (if (eq number t) "*" number))
2361          (marked (if (and 
2362                       (numberp number) 
2363                       (not (zerop number))
2364                       (>= (+ (length (cdr (assq 'tick marked)))
2365                              (length (cdr (assq 'dormant marked)))) number)
2366                       (> (length (cdr (assq 'tick marked))) 0))
2367                      ?* ? ))
2368          b)
2369     (beginning-of-line)
2370     (setq b (point))
2371     (let ((group (if method (gnus-group-real-name group) group)))
2372       ;; Insert the visible text.
2373       (insert-before-markers (eval gformat)))
2374     (forward-char -1)
2375     (if (and gnus-visual gnus-mouse-face)
2376         (overlay-put (make-overlay b (point)) 'mouse-face gnus-mouse-face))
2377     ;; Insert the invisible info on the end of the line.
2378     (set-text-properties 
2379      (prog1 
2380          (point)
2381        ;; The info is GROUP UNREAD MARKED LEVEL.
2382        (insert (format 
2383                 " %s%c%c%d" group (if (or (stringp number) (> number 0)) ?+ ? )
2384                 marked level)))
2385      (point) '(invisible t))
2386     (forward-char 1)))
2387
2388 (defun gnus-group-update-group (group &optional visible-only)
2389   "Update newsgroup info of GROUP.
2390 If VISIBLE-ONLY is non-nil, the group won't be displayed if it isn't already."
2391   (let ((buffer-read-only nil)
2392         (case-fold-search nil)
2393         (regexp (gnus-group-make-regexp group))
2394         (visible nil))
2395     (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
2396       (if entry
2397           (gnus-dribble-enter 
2398            (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2399                    ")"))))
2400     ;; Buffer may be narrowed.
2401     (save-restriction
2402       (widen)
2403       ;; Search a line to modify.  If the buffer is large, the search
2404       ;; takes long time.  In most cases, current point is on the line
2405       ;; we are looking for.  So, first of all, check current line. 
2406       ;; And then if current point is in the first half, search from
2407       ;; the beginning.  Otherwise, search from the end.
2408       (if (cond ((progn
2409                    (beginning-of-line)
2410                    (looking-at regexp)))
2411                 ((and (> (/ (buffer-size) 2) (point)) ;In the first half.
2412                       (progn
2413                         (goto-char (point-min))
2414                         (re-search-forward regexp nil t))))
2415                 ((progn
2416                    (goto-char (point-max))
2417                    (re-search-backward regexp nil t))))
2418           ;; GROUP is listed in current buffer. So, delete old line.
2419           (progn
2420             (setq visible t)
2421             (beginning-of-line)
2422             (delete-region (point) (progn (forward-line 1) (point))))
2423         ;; No such line in the buffer, find out where it's supposed to
2424         ;; go, and insert it there (or at the end of the buffer).
2425         ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
2426         (or visible-only
2427             (let ((entry (cdr (gnus-gethash group gnus-newsrc-hashtb))))
2428               (goto-char (point-min))
2429               (while (and entry
2430                           (not (re-search-forward (gnus-group-make-regexp
2431                                                    (car (car entry))) nil t)))
2432                 (setq entry (cdr entry)))
2433               (if (not entry)
2434                   (goto-char (point-max))))))
2435       (if (or visible (not visible-only))
2436           (progn
2437             (gnus-group-insert-group-line-info group)
2438             (forward-line -1)           ; Move point back to the inserted line.
2439             ))))
2440   (gnus-group-set-mode-line))
2441
2442 (defun gnus-group-set-mode-line ()
2443   (if (memq 'group gnus-updated-mode-lines)
2444       (let* ((gformat (or gnus-group-mode-line-format-spec
2445                           (setq gnus-group-mode-line-format-spec
2446                                 (gnus-parse-format 
2447                                  gnus-group-mode-line-format 
2448                                  gnus-group-mode-line-format-alist))))
2449              (news-server (car (cdr gnus-select-method)))
2450              (news-method (car gnus-select-method))
2451              (mode-string (eval gformat))
2452              (max-len 60))
2453         (if (> (length mode-string) max-len) 
2454             (setq mode-string (substring mode-string 0 (- max-len 4))))
2455         (setq mode-line-buffer-identification mode-string)
2456         (set-buffer-modified-p t))))
2457
2458 (defun gnus-group-group-name ()
2459   "Get the name of the newsgroup on the current line."
2460   (save-excursion
2461     (let ((buffer-read-only nil))
2462       (beginning-of-line)
2463       (if (re-search-forward " \\([^ ]*\\)...$" nil t)
2464           (prog2
2465               (set-text-properties (match-beginning 1) (match-end 1) nil)
2466               (buffer-substring (match-beginning 1) (match-end 1))
2467             (set-text-properties (match-beginning 1) (match-end 1) 
2468                                  '(invisible t)))))))
2469
2470 (defun gnus-group-group-level ()
2471   "Get the level of the newsgroup on the current line."
2472   (save-excursion
2473     (end-of-line)
2474     (forward-char -1)
2475     (let ((c (following-char)))
2476       (if (and (>= c ?1) (<= c ?9))
2477           (1+ (- c ?1))))))
2478
2479 (defun gnus-group-make-regexp (newsgroup)
2480   "Return regexp that will match the line that NEWSGROUP is on."
2481   (concat " " (regexp-quote newsgroup) "...$"))
2482
2483 (defun gnus-group-search-forward (&optional backward all level)
2484   "Find the next newsgroup with unread articles.
2485 If BACKWARD is non-nil, find the previous newsgroup instead.
2486 If ALL is non-nil, just find any newsgroup.
2487 If LEVEL is non-nil, find group with level LEVEL, or higher if no such
2488 group exists."
2489   (if (not level)
2490       (let ((regexp (if all "...$" "\\+.[1-5]$")))
2491         (prog1
2492             (if backward
2493                 (progn
2494                   (beginning-of-line)
2495                   (re-search-backward regexp nil t))
2496               (end-of-line)
2497               (re-search-forward regexp nil t))
2498           (gnus-group-position-cursor)))
2499     (let ((beg (point)))
2500       (while (and (< level 10)
2501                   (goto-char beg)
2502                   (let ((regexp (format "%s.%d$" (if all "." "\\+") level)))
2503                     (not            
2504                      (if backward
2505                          (progn
2506                            (beginning-of-line)
2507                            (re-search-backward regexp nil t))
2508                        (end-of-line)
2509                        (re-search-forward regexp nil t)))))
2510         (setq level (1+ level)))
2511       (< level 10))))
2512
2513 ;; Gnus Group mode command
2514
2515 (defun gnus-group-read-group (all &optional no-article)
2516   "Read news in this newsgroup.
2517 If argument ALL is non-nil, already read articles become readable.
2518 If optional argument NO-ARTICLE is non-nil, no article body is displayed."
2519   (interactive "P")
2520   (let ((group (gnus-group-group-name))
2521         number active)
2522     (if (not group)
2523         (error "No group on current line"))
2524     ;; This group might be a dead group. In that case we have to get
2525     ;; the number of unread articles from `gnus-active-hashtb'.
2526     (if (>= (gnus-group-group-level) 8)
2527         (setq number (- (1+ (cdr (setq active (gnus-gethash 
2528                                                group gnus-active-hashtb))))
2529                         (car active)))
2530       (setq number (car (gnus-gethash group gnus-newsrc-hashtb))))
2531     (gnus-summary-read-group 
2532      group (or all (and (numberp number) (zerop number))) no-article)))
2533
2534 (defun gnus-group-select-group (all)
2535   "Select this newsgroup.
2536 No article is selected automatically.
2537 If argument ALL is non-nil, already read articles become readable."
2538   (interactive "P")
2539   (gnus-group-read-group all t))
2540
2541 (defun gnus-group-jump-to-group (group)
2542   "Jump to newsgroup GROUP."
2543   (interactive
2544    (list 
2545     (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
2546   (let ((case-fold-search nil))
2547     (goto-char (point-min))
2548     ;; Either go to the line in the group buffer...
2549     (or (re-search-forward (gnus-group-make-regexp group) nil t)
2550         ;; ... or insert the line.
2551         (gnus-group-update-group group))
2552     ;; Adjust cursor point.
2553     (gnus-group-position-cursor)))
2554
2555 (defun gnus-group-next-group (n)
2556   "Go to next N'th newsgroup.
2557 If N is negative, search backward instead.
2558 Returns the difference between N and the number of skips actually
2559 done."
2560   (interactive "p")
2561   (gnus-group-next-unread-group n t))
2562
2563 (defun gnus-group-next-unread-group (n &optional all level)
2564   "Go to next N'th unread newsgroup.
2565 If N is negative, search backward instead.
2566 If ALL is non-nil, choose any newsgroup, unread or not.
2567 If LEVEL is non-nil, choose the next group with level LEVEL, or, if no
2568 such group can be found, the next group with a level higher than
2569 LEVEL.
2570 Returns the difference between N and the number of skips actually
2571 done."
2572   (interactive "p")
2573   (let ((backward (< n 0))
2574         (n (abs n)))
2575   (while (and (> n 0)
2576               (gnus-group-search-forward backward all level))
2577     (setq n (1- n)))
2578   (if (/= 0 n) (message "No more%s newsgroups%s" (if all "" " unread")
2579                         (if level " on this level or higher" "")))
2580   n))
2581
2582 (defun gnus-group-prev-group (n)
2583   "Go to previous N'th newsgroup.
2584 Returns the difference between N and the number of skips actually
2585 done."
2586   (interactive "p")
2587   (gnus-group-next-unread-group (- n) t))
2588
2589 (defun gnus-group-prev-unread-group (n)
2590   "Go to previous N'th unread newsgroup.
2591 Returns the difference between N and the number of skips actually
2592 done."  
2593   (interactive "p")
2594   (gnus-group-next-unread-group (- n)))
2595
2596 (defun gnus-group-next-unread-group-same-level (n)
2597   "Go to next N'th unread newsgroup on the same level.
2598 If N is negative, search backward instead.
2599 Returns the difference between N and the number of skips actually
2600 done."
2601   (interactive "p")
2602   (gnus-group-next-unread-group n t (gnus-group-group-level))
2603   (gnus-group-position-cursor))
2604
2605 (defun gnus-group-prev-unread-group-same-level (n)
2606   "Go to next N'th unread newsgroup on the same level.
2607 Returns the difference between N and the number of skips actually
2608 done."
2609   (interactive "p")
2610   (gnus-group-next-unread-group (- n) t (gnus-group-group-level))
2611   (gnus-group-position-cursor))
2612
2613 (defun gnus-group-add-newsgroup (&optional name how where)
2614   "Add a new newsgroup."
2615   (interactive)
2616   (let ((methods gnus-valid-select-methods)
2617         nname)
2618     (if (not name)
2619         (setq name (read-string "Newsgroup name: ")))
2620     (setq nname (concat gnus-foreign-group-prefix name))
2621     (while (gnus-gethash nname gnus-newsrc-hashtb)
2622       (setq name (read-string "Name already in use. Newsgroup name: "))
2623       (setq nname (concat gnus-foreign-group-prefix name)))
2624     (if (not how)
2625         (setq how (completing-read (format "%s method: " name) methods nil t)))
2626     (if (not where)
2627         (setq where (read-string 
2628                      (format "Get %s by method %s from: " name how))))
2629     (gnus-group-change-level 
2630      (list t nname 3 nil nil (list (intern how) where))
2631      3 9 (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)
2632      t)
2633     (gnus-group-insert-group-line-info nname)))
2634
2635 (defun gnus-group-edit-newsgroup ()
2636   (interactive)
2637   (let ((group (gnus-group-group-name))
2638         info)
2639     (if group (setq info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
2640       (error "No group on current line"))
2641     (switch-to-buffer (get-buffer-create gnus-group-edit-buffer))
2642     (gnus-add-current-to-buffer-list)
2643     (emacs-lisp-mode)
2644     (erase-buffer)
2645     (insert ";; Type `C-c C-c' after you have edited the newsgroup entry.\n\n")
2646     (insert (format "(gnus-group-set-info\n  '%S)\n" info))
2647     (local-set-key "\C-c\C-c" 'gnus-group-edit-newsgroup-done)))
2648
2649 (defun gnus-group-edit-newsgroup-done ()
2650   (interactive)
2651   (set-buffer (get-buffer-create gnus-group-edit-buffer))
2652   (eval-current-buffer)
2653   (kill-buffer (current-buffer))
2654   (set-buffer gnus-group-buffer)
2655   (gnus-group-update-group (gnus-group-group-name))
2656   (gnus-group-position-cursor))
2657
2658 (defun gnus-group-make-mail-groups (method)
2659   ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
2660   (interactive
2661    (list
2662     (intern
2663      (completing-read
2664       "Mail method: " 
2665       (gnus-methods-using 'mail) nil t "nnmail"))))
2666   (let ((groups nnmail-split-methods)
2667         group)
2668     (while groups
2669       (setq group (concat gnus-foreign-group-prefix (car (car groups))))
2670       (if (not (gnus-gethash group gnus-newsrc-hashtb))
2671           (progn
2672             (gnus-group-change-level 
2673              (list t group 1 nil nil (list method ""))
2674              1 9 (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)
2675              t)
2676             (gnus-group-insert-group-line-info group)))
2677       (setq groups (cdr groups)))))
2678
2679 (defun gnus-group-catchup-current (n &optional all)
2680   "Mark all articles not marked as unread in current newsgroup as read.
2681 If prefix argument N is numeric, the ARG next newsgroups will be
2682 caught up. If ALL is non-nil, marked articles will also be marked as
2683 read. Cross references (Xref: field) of articles are ignored.
2684 The difference between N and actual number of newsgroups that were
2685 caught up is returned."
2686   (interactive "p")
2687   (if (or (not gnus-interactive-catchup) ;Without confirmation?
2688           gnus-expert-user
2689           (y-or-n-p
2690            (if all
2691                "Do you really want to mark all articles as read? "
2692              "Mark all unread articles as read? ")))
2693       (progn
2694         (while 
2695             (and (> n 0)
2696                  (progn
2697                    (setq n (1- n))
2698                    (gnus-group-catchup (gnus-group-group-name) all)
2699                    (gnus-group-update-group-line)
2700                    t)
2701                  (= 0 (gnus-group-next-unread-group 1))))))
2702     n)
2703
2704 (defun gnus-group-catchup-current-all (n)
2705   "Mark all articles in current newsgroup as read.
2706 Cross references (Xref: field) of articles are ignored."
2707   (interactive "p")
2708   (gnus-group-catchup-current n 'all))
2709
2710 (defun gnus-group-catchup (group &optional all)
2711   "Mark all articles in GROUP as read.
2712 If ALL is non-nil, all articles are marked as read.
2713 The return value is the number of articles that were marked as read,
2714 or nil if no action could be taken."
2715   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
2716          (num (car entry))
2717          ticked)
2718     ;; Do the updating only if the newsgroup isn't killed
2719     (if entry
2720         (progn
2721           (setq ticked (if all nil (cdr (assq 'tick (nth 3 (nth 2 entry))))))
2722           (gnus-update-read-articles group ticked nil ticked)))
2723     num))
2724
2725 (defun gnus-group-expire-articles (newsgroup)
2726   "Expire all expirable articles in the current newsgroup."
2727   (interactive (list (gnus-group-group-name)))
2728   (if (not newsgroup) (error "No current newsgroup"))
2729   (let ((expirable 
2730          (assq 'expire (nth 3 (nth 2 (gnus-gethash newsgroup 
2731                                                    gnus-newsrc-hashtb))))))
2732  (and expirable 
2733       (gnus-check-backend-function 'request-expire-articles newsgroup)
2734       (setcdr expirable
2735               (gnus-request-expire-articles (cdr expirable) newsgroup)))))
2736
2737 (defun gnus-group-expire-all-groups ()
2738   "Expire all expirable articles in all newsgroups."
2739   (interactive)
2740   (let ((newsrc (cdr gnus-newsrc-assoc)))
2741     (while newsrc
2742       (gnus-group-expire-articles (car (car newsrc)))
2743       (setq newsrc (cdr newsrc)))))
2744
2745 (defun gnus-group-set-current-level (n)
2746   "Set the level of the current group to the numeric prefix."
2747   (interactive "P")
2748   (setq n (or n (string-to-int 
2749                  (completing-read 
2750                   "Level: " 
2751                   '(("1") ("2") ("3") ("4") ("5") ("6") ("7") ("8") ("9"))
2752                   nil t))))
2753   (let ((group (gnus-group-group-name)))
2754     (if (not group) (error "No newsgroup on current line.")
2755     (if (and (numberp n) (>= n 1) (<= n 9))
2756         (progn
2757           (gnus-group-change-level group n (gnus-group-group-level))
2758           (gnus-group-update-group-line))
2759       (error "Illegal level: %s" n)))))
2760
2761 (defun gnus-group-unsubscribe-current-group (arg)
2762   "Toggle subscribe from/to unsubscribe current group."
2763   (interactive "P")
2764   (let ((group (gnus-group-group-name)))
2765     (if group
2766         (progn
2767           (if (not arg) 
2768               (setq arg (if (<= (gnus-group-group-level) 5) 7 3)))
2769           (gnus-group-unsubscribe-group group arg)
2770           (gnus-group-next-group 1))
2771       (message "No newsgroup on current line"))))
2772
2773 (defun gnus-group-unsubscribe-group (group &optional level)
2774   "Toggle subscribe from/to unsubscribe GROUP.
2775 New newsgroup is added to .newsrc automatically."
2776   (interactive
2777    (list (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
2778   (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
2779     (cond (newsrc
2780            ;; Toggle subscription flag.
2781            (gnus-group-change-level 
2782             newsrc (if level level (if (< (nth 1 (nth 2 newsrc)) 6) 7 4)))
2783            (gnus-group-update-group group))
2784           ((and (stringp group)
2785                 (gnus-gethash group gnus-active-hashtb))
2786            ;; Add new newsgroup.
2787            (gnus-group-change-level 
2788             group 
2789             (if level level 3) 
2790             (if (member group gnus-zombie-list) 8 9)
2791             (or (and (gnus-group-group-name)
2792                      (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb))
2793                 (gnus-gethash (car (car gnus-newsrc-assoc)) 
2794                               gnus-newsrc-hashtb)))
2795            (gnus-group-update-group group))
2796           (t (error "No such newsgroup: %s" group)))
2797     (gnus-group-position-cursor)))
2798
2799 (defun gnus-group-transpose-groups (arg)
2800   "Exchange current newsgroup and previous newsgroup.
2801 With argument ARG, takes previous newsgroup and moves it past ARG newsgroup."
2802   (interactive "p")
2803   ;; BUG: last newsgroup and the last but one cannot be transposed
2804   ;; since gnus-group-search-forward does not move forward beyond the
2805   ;; last.  If we instead use forward-line, no problem, but I don't
2806   ;; want to use it for later extension.
2807   (while (> arg 0)
2808     (gnus-group-search-forward t t)
2809     (gnus-group-kill-group 1)
2810     (gnus-group-search-forward nil t)
2811     (gnus-group-yank-group)
2812     (gnus-group-search-forward nil t)
2813     (setq arg (1- arg))
2814     ))
2815
2816 (defun gnus-group-kill-all-zombies ()
2817   "Kill all zombie newsgroups."
2818   (interactive)
2819   (setq gnus-killed-list (nconc gnus-zombie-list gnus-killed-list))
2820   (setq gnus-zombie-list nil)
2821   (gnus-group-prepare 5)
2822   (goto-char (point-min))
2823   (gnus-group-position-cursor))
2824
2825 (defun gnus-group-kill-region (begin end)
2826   "Kill newsgroups in current region (excluding current point).
2827 The killed newsgroups can be yanked by using \\[gnus-group-yank-group]."
2828   (interactive "r")
2829   (let ((lines
2830          ;; Exclude a line where current point is on.
2831          (1-
2832           ;; Count lines.
2833           (save-excursion
2834             (count-lines
2835              (progn
2836                (goto-char begin)
2837                (beginning-of-line)
2838                (point))
2839              (progn
2840                (goto-char end)
2841                (end-of-line)
2842                (point)))))))
2843     (goto-char begin)
2844     (beginning-of-line)                 ;Important when LINES < 1
2845     (gnus-group-kill-group lines)))
2846
2847 (defun gnus-group-kill-group (n)
2848   "Kill newsgroup on current line, repeated prefix argument N times.
2849 The killed newsgroups can be yanked by using \\[gnus-group-yank-group].
2850 However, only groups that were alive can be yanked; already killed 
2851 groups or zombie groups can't be yanked.
2852 The return value is the name of the (last) newsgroup that was killed."
2853   (interactive "p")
2854   (let ((buffer-read-only nil)
2855         group entry level)
2856     (while (>= (setq n  (1- n)) 0)
2857       (setq group (gnus-group-group-name))
2858       (or group
2859           (signal 'end-of-buffer nil))
2860       (setq level (gnus-group-group-level))
2861       (beginning-of-line)
2862       (delete-region (point)
2863                      (progn (forward-line 1) (point)))
2864       (if (setq entry (gnus-gethash group gnus-newsrc-hashtb))
2865           (setq gnus-list-of-killed-groups 
2866                 (cons (cons (car entry) (nth 2 entry)) 
2867                       gnus-list-of-killed-groups)))
2868       (gnus-group-change-level 
2869        (if entry entry group) 9
2870        (if entry nil level)))
2871     (if (eobp)
2872         (forward-line -1))
2873     (gnus-group-position-cursor)
2874     group))
2875
2876 (defun gnus-group-yank-group (&optional arg)
2877   "Yank the last newsgroups killed with \\[gnus-group-kill-group],
2878 inserting it before the current newsgroup.  The numeric ARG specifies
2879 how many newsgroups are to be yanked.  The name of the (last)
2880 newsgroup yanked is returned."
2881   (interactive "p")
2882   (if (not arg) (setq arg 1))
2883   (let (info group prev)
2884     (while (>= (setq arg (1- arg)) 0)
2885       (if (not (setq info (car gnus-list-of-killed-groups)))
2886           (error "No more newsgroups to yank"))
2887       (setq group (nth 2 info))
2888       ;; Find which newsgroup to insert this one before - search
2889       ;; backward until something suitable is found. If there are no
2890       ;; other newsgroups in this buffer, just make this newsgroup the
2891       ;; first newsgroup.
2892       (while (and (not (setq prev (gnus-group-group-name)))
2893                   (= 0 (forward-line -1))))
2894       (if (not prev)
2895           (setq prev (car (car gnus-newsrc-assoc))))
2896       (gnus-group-change-level 
2897        info (nth 2 info) 9 
2898        (gnus-gethash prev gnus-newsrc-hashtb)
2899        t)
2900       (gnus-group-insert-group-line-info (nth 1 info))
2901       (setq gnus-list-of-killed-groups 
2902             (cdr gnus-list-of-killed-groups)))
2903     (forward-line -1)
2904     (gnus-group-position-cursor)
2905     group))
2906       
2907 (defun gnus-group-list-all-groups (arg)
2908   "List all newsgroups with level ARG or lower.
2909 Default is 7, which lists all subscribed and unsubscribed groups."
2910   (interactive "P")
2911   (setq arg (or arg 7))
2912   (gnus-group-list-groups arg t))
2913
2914 (defun gnus-group-list-killed ()
2915   "List all killed newsgroups in the Newsgroup buffer."
2916   (interactive)
2917   (gnus-group-prepare 9 t 9)
2918   (goto-char (point-min))
2919   (gnus-group-position-cursor))
2920
2921 (defun gnus-group-list-zombies ()
2922   "List all zombie newsgroups in the Newsgroup buffer."
2923   (interactive)
2924   (gnus-group-prepare 8 t 8)
2925   (goto-char (point-min))
2926   (gnus-group-position-cursor))
2927
2928 (defun gnus-group-get-new-news (&optional arg)
2929   "Get newly arrived articles.
2930 If ARG is non-nil, it should be a number between one and nine to
2931 specify which levels you are interested in re-scanning."
2932   (interactive "P")
2933   (if (and gnus-read-active-file (not arg))
2934       (gnus-read-active-file))
2935   (if arg
2936       (let ((gnus-read-active-file nil))
2937         (gnus-get-unread-articles arg))
2938     (gnus-get-unread-articles 7))
2939   (gnus-group-list-groups 5 gnus-have-all-newsgroups))
2940
2941 (defun gnus-group-get-new-news-this-group (n)
2942   "Check for newly arrived news in the current group (and the N-1 next groups).
2943 The difference between N and the number of newsgroup checked is returned.
2944 If N is negative, this group and the N-1 previous groups will be checked."
2945   (interactive "p")
2946   (let ((way (if (< n 0) -1 1))
2947         (n (abs n))
2948         (w-p (window-start))
2949         group)
2950     (while (and (> n 0)
2951                 (progn
2952                   (and (setq group (gnus-group-group-name))
2953                        (gnus-activate-newsgroup 
2954                         group (gnus-group-real-name group))
2955                        (progn
2956                          (gnus-get-unread-articles-in-group 
2957                           (nth 2 (gnus-gethash group gnus-newsrc-hashtb))
2958                           (gnus-gethash group gnus-active-hashtb))
2959                          (gnus-group-update-group-line)))
2960                   t)
2961                 (= 0 (gnus-group-next-group way)))
2962       (setq n (1- n)))
2963     (if (/= 0 n) (message "No more newsgroups"))
2964     ;; !!! I don't know why the buffer scrolls forward when updating
2965     ;; the first line in the Group buffer, but it does. So we set the
2966     ;; window start forcibly.
2967     (set-window-start (get-buffer-window (current-buffer)) w-p)
2968     n))
2969   
2970 (defun gnus-group-describe-group (&optional group)
2971   "Display a description of the current newsgroup."
2972   (interactive)
2973   (let ((group (or group (gnus-group-group-name))))
2974     (if (not group)
2975         (message "No group on current line")
2976       (and (or gnus-description-hashtb
2977                (gnus-read-descriptions-file))
2978            (message
2979             (or (gnus-gethash group gnus-description-hashtb)
2980                 "No description available"))))))
2981
2982 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
2983 (defun gnus-group-describe-all-groups ()
2984   "Pop up a buffer with descriptons of all newsgroups."
2985   (interactive)
2986   (if (not (or gnus-description-hashtb
2987                (gnus-read-descriptions-file)))
2988       (error "Couldn't request descriptions file"))
2989   (let ((buffer-read-only nil)
2990         beg)
2991     (erase-buffer)
2992     (mapatoms
2993      (lambda (group)
2994        (insert (format "      *: %-20s %s" (symbol-name group)
2995                        (symbol-value group)))
2996        (setq beg (point))
2997        (insert (format " %s  6\n" group))
2998        (set-text-properties beg (1- (point)) '(invisible t)))
2999      gnus-description-hashtb)
3000     (goto-char (point-min))
3001     (gnus-group-position-cursor)))
3002
3003 ;; Suggested by by Daniel Quinlan <quinlan@best.com>.
3004 (defun gnus-group-apropos (regexp &optional search-description)
3005   "List all newsgroups that have names that match a regexp."
3006   (interactive "sGnus apropos (regexp): ")
3007   (let ((prev "")
3008         (obuf (current-buffer))
3009         groups des prev)
3010     ;; Go through all newsgroups that are known to Gnus.
3011     (mapatoms 
3012      (lambda (group)
3013        (and (string-match regexp (symbol-name group))
3014             (setq groups (cons (symbol-name group) groups))))
3015      gnus-active-hashtb)
3016     ;; Go through all descriptions that are known to Gnus. 
3017     (if search-description
3018         (mapatoms 
3019          (lambda (group)
3020            (and (string-match regexp (symbol-value group))
3021                 (gnus-gethash (symbol-name group) gnus-active-hashtb)
3022                 (setq groups (cons (symbol-name group) groups))))
3023          gnus-description-hashtb))
3024     (if (not groups)
3025         (message "No groups matched \"%s\"." regexp)
3026       ;; Print out all the groups.
3027       (save-excursion
3028         (pop-to-buffer (get-buffer-create "*Gnus Help*"))
3029         (buffer-disable-undo (current-buffer))
3030         (erase-buffer)
3031         (setq groups (sort groups 'string<))
3032         (while groups
3033           ;; Groups may be entered twice into the list of groups.
3034           (if (not (string= (car groups) prev))
3035               (progn
3036                 (insert (setq prev (car groups)) "\n")
3037                 (if (and gnus-description-hashtb
3038                          (setq des (gnus-gethash (car groups) 
3039                                                  gnus-description-hashtb)))
3040                     (insert "  " des "\n"))))
3041           (setq groups (cdr groups)))
3042         (goto-char 1)))
3043     (pop-to-buffer obuf)))
3044
3045 (defun gnus-group-description-apropos (regexp)
3046   "List all newsgroups that have names or desccriptions that match a regexp."
3047   (interactive "sGnus description apropos (regexp): ")
3048   (if (not (or gnus-description-hashtb
3049                (gnus-read-descriptions-file)))
3050       (error "Couldn't request descriptions file"))
3051   (gnus-group-apropos regexp t))
3052
3053 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
3054 (defun gnus-group-save-newsrc ()
3055   "Save the Gnus startup files."
3056   (interactive)
3057   (gnus-save-newsrc-file))
3058
3059 (defun gnus-group-restart (&optional arg)
3060   "Force Gnus to read the .newsrc file."
3061   (interactive "P")
3062   (gnus-save-newsrc-file)
3063   (gnus-setup-news 'force)
3064   (gnus-group-list-groups (or arg 5) gnus-have-all-newsgroups))
3065
3066 (defun gnus-group-read-init-file ()
3067   "Read the Gnus elisp init file."
3068   (interactive)
3069   (gnus-read-init-file))
3070
3071 (defun gnus-group-check-bogus-groups ()
3072   "Check bogus newsgroups."
3073   (interactive)
3074   (gnus-check-bogus-newsgroups (not gnus-expert-user))  ;Require confirmation.
3075   (gnus-group-list-groups 5 gnus-have-all-newsgroups))
3076
3077 (defun gnus-group-mail ()
3078   "Start composing a mail."
3079   (interactive)
3080   (mail))
3081
3082 (defun gnus-group-edit-global-kill ()
3083   "Edit a global KILL file."
3084   (interactive)
3085   (setq gnus-current-kill-article nil)  ;No articles selected.
3086   (gnus-kill-file-edit-file nil)        ;Nil stands for global KILL file.
3087   (message
3088    (substitute-command-keys
3089     "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
3090
3091 (defun gnus-group-edit-local-kill ()
3092   "Edit a local KILL file."
3093   (interactive)
3094   (setq gnus-current-kill-article nil)  ;No articles selected.
3095   (gnus-kill-file-edit-file (gnus-group-group-name))
3096   (message
3097    (substitute-command-keys
3098     "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
3099
3100 (defun gnus-group-force-update ()
3101   "Update `.newsrc' file."
3102   (interactive)
3103   (gnus-save-newsrc-file))
3104
3105 (defun gnus-group-suspend ()
3106   "Suspend the current Gnus session.
3107 In fact, cleanup buffers except for Group Mode buffer.
3108 The hook gnus-suspend-gnus-hook is called before actually suspending."
3109   (interactive)
3110   (run-hooks 'gnus-suspend-gnus-hook)
3111   ;; Kill Gnus buffers except for Group Mode buffer.
3112   (let ((group-buf (get-buffer gnus-group-buffer)))
3113     (while gnus-buffer-list
3114       (and (not (eq (car gnus-buffer-list) group-buf))
3115            (get-buffer (car gnus-buffer-list))
3116            (buffer-name (get-buffer (car gnus-buffer-list)))
3117            (kill-buffer (car gnus-buffer-list)))
3118       (setq gnus-buffer-list (cdr gnus-buffer-list)))
3119     (setq gnus-buffer-list (list group-buf))
3120     (bury-buffer group-buf)
3121     (delete-windows-on group-buf t)))
3122
3123 (defun gnus-group-clear-dribble ()
3124   "Clear all information from the dribble buffer."
3125   (interactive)
3126   (gnus-dribble-clear))
3127
3128 (defun gnus-group-exit ()
3129   "Quit reading news after updating .newsrc.eld and .newsrc.
3130 The hook `gnus-exit-gnus-hook' is called before actually exiting."
3131   (interactive)
3132   (if (or noninteractive                ;For gnus-batch-kill
3133           (zerop (buffer-size))         ;No news is good news.
3134           (not (gnus-server-opened gnus-select-method)) ;NNTP connection closed
3135           (not gnus-interactive-exit)   ;Without confirmation
3136           gnus-expert-user
3137           (y-or-n-p "Are you sure you want to quit reading news? "))
3138       (progn
3139         (message "")                    ;Erase "Yes or No" question.
3140         (run-hooks 'gnus-exit-gnus-hook)
3141         (gnus-save-newsrc-file)
3142         (gnus-clear-system))))
3143
3144 (defun gnus-group-quit ()
3145   "Quit reading news without updating .newsrc.eld or .newsrc.
3146 The hook `gnus-exit-gnus-hook' is called before actually exiting."
3147   (interactive)
3148   (if (or noninteractive                ;For gnus-batch-kill
3149           (zerop (buffer-size))
3150           (not (gnus-server-opened gnus-select-method))
3151           gnus-expert-user
3152           (yes-or-no-p
3153            (format "Quit reading news without saving %s? "
3154                    (file-name-nondirectory gnus-current-startup-file))))
3155       (progn
3156         (message "")                    ;Erase "Yes or No" question.
3157         (run-hooks 'gnus-exit-gnus-hook)
3158         (gnus-dribble-save)
3159         (gnus-clear-system))))
3160
3161 (defun gnus-group-describe-briefly ()
3162   "Give a one line description of the Group mode commands."
3163   (interactive)
3164   (message
3165    (substitute-command-keys "\\[gnus-group-read-group]:Select  \\[gnus-group-next-unread-group]:Forward  \\[gnus-group-prev-unread-group]:Backward  \\[gnus-group-exit]:Exit  \\[gnus-info-find-node]:Run Info  \\[gnus-group-describe-briefly]:This help")))
3166
3167 (defun gnus-group-browse-foreign-server (method)
3168   "Browse a foreign news server.
3169 If called interactively, this function will ask for a select method
3170  (nntp, nnspool, etc.) and a server address (eg. nntp.some.where). 
3171 If not, METHOD should be a list where the first element is the method
3172 and the second element is the address."
3173   (interactive
3174    (list (list (completing-read "Select method: "
3175                                 gnus-valid-select-methods
3176                                 nil t "nntp")
3177                ;; Suggested by mapjph@bath.ac.uk.
3178                (completing-read 
3179                 "Server name: " 
3180                 (mapcar (lambda (server) (list server))
3181                         gnus-secondary-servers)))))
3182   (gnus-browse-foreign-server method))
3183
3184 \f
3185 ;;;
3186 ;;; Browse Server Mode
3187 ;;;
3188
3189 (defvar gnus-browse-server-mode-hook nil)
3190 (defvar gnus-browse-server-mode-map nil)
3191
3192 (if gnus-browse-server-mode-map
3193     nil
3194   (setq gnus-browse-server-mode-map (make-keymap))
3195   (suppress-keymap gnus-browse-server-mode-map)
3196   (define-key gnus-browse-server-mode-map " " 'gnus-browse-read-group)
3197   (define-key gnus-browse-server-mode-map "=" 'gnus-browse-read-group)
3198   (define-key gnus-browse-server-mode-map "n" 'gnus-group-next-group)
3199   (define-key gnus-browse-server-mode-map "p" 'gnus-group-prev-group)
3200   (define-key gnus-browse-server-mode-map [del] 'gnus-group-prev-group)
3201   (define-key gnus-browse-server-mode-map "N" 'gnus-group-next-group)
3202   (define-key gnus-browse-server-mode-map "P" 'gnus-group-prev-group)
3203   (define-key gnus-browse-server-mode-map "\M-n" 'gnus-group-next-group)
3204   (define-key gnus-browse-server-mode-map "\M-p" 'gnus-group-prev-group)
3205   (define-key gnus-browse-server-mode-map [down] 'gnus-group-next-group)
3206   (define-key gnus-browse-server-mode-map [up] 'gnus-group-prev-group)
3207   (define-key gnus-browse-server-mode-map "\r" 'gnus-group-next-group)
3208   (define-key gnus-browse-server-mode-map "u" 'gnus-browse-unsubscribe-current-group)
3209   (define-key gnus-browse-server-mode-map "q" 'gnus-browse-exit)
3210   (define-key gnus-browse-server-mode-map "Q" 'gnus-browse-exit)
3211   (define-key gnus-browse-server-mode-map "\C-c\C-c" 'gnus-browse-quit)
3212   (define-key gnus-browse-server-mode-map "?" 'gnus-browse-describe-briefly)
3213   (define-key gnus-browse-server-mode-map "\C-c\C-i" 'gnus-info-find-node)
3214   )
3215
3216 (defvar gnus-browse-current-method nil)
3217
3218 (defun gnus-browse-foreign-server (method)
3219   (setq gnus-browse-current-method method)
3220   (let ((gnus-select-method method)
3221         groups group)
3222     (message "Connecting to %s..." (nth 1 method))
3223     (if (not (gnus-request-list method))
3224         (error "Unable to contact server: " (gnus-status-message method)))
3225     (set-buffer (get-buffer-create "*Gnus Browse Server*"))
3226     (gnus-add-current-to-buffer-list)
3227     (buffer-disable-undo (current-buffer))
3228     (let ((buffer-read-only nil))
3229       (erase-buffer))
3230     (gnus-browse-server-mode)
3231     (setq mode-line-buffer-identification
3232           (format
3233            "(ding) Browse Server {%s:%s}" (car method) (car (cdr method))))
3234     (save-excursion
3235       (set-buffer nntp-server-buffer)
3236       (let ((cur (current-buffer)))
3237         (goto-char 1)
3238         (while (re-search-forward 
3239                 "\\(^[^ \t]+\\)[ \t]+[0-9]+[ \t]+[0-9]+" nil t)
3240           (goto-char (match-end 1))
3241           (setq groups (cons (cons (buffer-substring (match-beginning 1)
3242                                                      (match-end 1))
3243                                    (- (read cur) (read cur)))
3244                              groups)))))
3245     (setq groups (sort groups 
3246                        (lambda (l1 l2)
3247                          (string< (car l1) (car l2)))))
3248     (let ((buffer-read-only nil))
3249       (while groups
3250         (setq group (car groups))
3251         (insert 
3252          (format "K%7d: %s\n" (cdr group) (car group)))
3253         (setq groups (cdr groups))))
3254     (switch-to-buffer (current-buffer))
3255     (goto-char 1)
3256     (gnus-group-position-cursor)))
3257
3258 (defun gnus-browse-server-mode ()
3259   "Major mode for reading network news."
3260   (interactive)
3261   (kill-all-local-variables)
3262   (setq mode-line-modified "--- ")
3263   (setq major-mode 'gnus-browse-server-mode)
3264   (setq mode-name "Browse Server")
3265   (setq mode-line-process nil)
3266   (use-local-map gnus-browse-server-mode-map)
3267   (buffer-disable-undo (current-buffer))
3268   (setq truncate-lines t)
3269   (setq buffer-read-only t)
3270   (run-hooks 'gnus-browse-server-mode-hook))
3271
3272 (defun gnus-browse-read-group ()
3273   "Not implemented, and will probably never be."
3274   (interactive)
3275   (error "You can't read while browsing"))
3276
3277 (defun gnus-browse-unsubscribe-current-group (arg)
3278   "(Un)subscribe to the next ARG groups."
3279   (interactive "p")
3280   (let ((ward (if (< arg 0) -1 1))
3281         (arg (abs arg)))
3282     (while (and (> arg 0)
3283                 (gnus-browse-unsubscribe-group)
3284                 (= (gnus-group-next-group ward) 0))
3285       (setq arg (1- arg)))
3286     (gnus-group-position-cursor)
3287     (if (/= 0 arg) (message "No more newsgroups" ))
3288     arg))
3289   
3290 (defun gnus-browse-unsubscribe-group ()
3291   (let ((sub nil)
3292         (buffer-read-only nil)
3293         group)
3294     (save-excursion
3295       (beginning-of-line)
3296       (if (= (following-char) ?K) (setq sub t))
3297       (re-search-forward ": \\(.*\\)$" nil t)
3298       (setq group 
3299             (concat gnus-foreign-group-prefix 
3300                     (buffer-substring (match-beginning 1) (match-end 1))))
3301       (beginning-of-line)
3302       (delete-char 1)
3303       (if sub
3304           (progn
3305             (gnus-group-change-level 
3306              (list t group 3 nil nil gnus-browse-current-method) 3 9 
3307              (gnus-gethash (car (nth 1 gnus-newsrc-assoc)) gnus-newsrc-hashtb)
3308              t)
3309             (insert ? ))
3310         (gnus-group-change-level group 9 3)
3311         (insert ?K)))
3312     t))
3313
3314 (defun gnus-browse-exit ()
3315   "Quit browsing and return to the Newsgroup buffer."
3316   (interactive)
3317   (if (eq major-mode 'gnus-browse-server-mode)
3318       (kill-buffer (current-buffer)))
3319   (switch-to-buffer gnus-group-buffer)
3320   (gnus-group-list-groups 5))
3321
3322 (defun gnus-browse-describe-briefly ()
3323   "Give a one line description of the Group mode commands."
3324   (interactive)
3325   (message
3326    (substitute-command-keys "\\[gnus-group-next-group]:Forward  \\[gnus-group-prev-group]:Backward  \\[gnus-browse-exit]:Exit  \\[gnus-info-find-node]:Run Info  \\[gnus-browse-describe-briefly]:This help")))
3327       
3328 \f
3329 ;;;
3330 ;;; Gnus Summary Mode
3331 ;;;
3332
3333 (defmacro gnus-summary-add (key func)
3334   (` (define-key gnus-summary-mode-map (, key) (, func))))
3335
3336 (defvar gnus-summary-raise-map nil)
3337 (define-prefix-command 'gnus-summary-raise-map)
3338
3339 (defvar gnus-summary-lower-map nil)
3340 (define-prefix-command 'gnus-summary-lower-map)
3341
3342 (if gnus-summary-mode-map
3343     nil
3344   (setq gnus-summary-mode-map (make-keymap))
3345   (suppress-keymap gnus-summary-mode-map)
3346   (gnus-summary-add "\C-c\C-i" gnus-summary-raise-map)
3347   (gnus-summary-add "\C-c\C-k" gnus-summary-lower-map)
3348   (gnus-summary-add "\C-c\C-v" 'gnus-uu-ctl-map)
3349   (gnus-summary-add "@" 'gnus-summary-kill-below)
3350   (gnus-summary-add "\C-c " 'gnus-summary-clear-above)
3351   (gnus-summary-add "\C-c-" 'gnus-summary-tick-above)
3352   (gnus-summary-add "#" 'gnus-summary-mark-as-processable)
3353   (gnus-summary-add "\M-#" 'gnus-summary-unmark-as-processable)
3354   (gnus-summary-add "\C-c\M-#" 'gnus-summary-unmark-all-processable)
3355   (gnus-summary-add " " 'gnus-summary-next-page)
3356   (gnus-summary-add "\177" 'gnus-summary-prev-page)
3357   (gnus-summary-add "\r" 'gnus-summary-scroll-up)
3358   (gnus-summary-add "n" 'gnus-summary-next-unread-article)
3359   (gnus-summary-add "p" 'gnus-summary-prev-unread-article)
3360   (gnus-summary-add "N" 'gnus-summary-next-article)
3361   (gnus-summary-add "P" 'gnus-summary-prev-article)
3362   (gnus-summary-add "\M-\C-n" 'gnus-summary-next-same-subject)
3363   (gnus-summary-add "\M-\C-p" 'gnus-summary-prev-same-subject)
3364   (gnus-summary-add "\C-c\C-n" 'gnus-summary-next-digest)
3365   (gnus-summary-add "\C-c\C-p" 'gnus-summary-prev-digest)
3366   (gnus-summary-add "\M-n" 'gnus-summary-next-unread-subject)
3367   (gnus-summary-add "\M-p" 'gnus-summary-prev-unread-subject)
3368   (gnus-summary-add "." 'gnus-summary-first-unread-article)
3369   (gnus-summary-add "s" 'gnus-summary-isearch-article)
3370   (gnus-summary-add "\M-s" 'gnus-summary-search-article-forward)
3371   (gnus-summary-add "\M-r" 'gnus-summary-search-article-backward)
3372   (gnus-summary-add "<" 'gnus-summary-beginning-of-article)
3373   (gnus-summary-add ">" 'gnus-summary-end-of-article)
3374   (gnus-summary-add "j" 'gnus-summary-goto-subject)
3375   (gnus-summary-add "l" 'gnus-summary-goto-last-article)
3376   (gnus-summary-add "^" 'gnus-summary-refer-parent-article)
3377   (gnus-summary-add "\M-^" 'gnus-summary-refer-article)
3378   (gnus-summary-add "u" 'gnus-summary-tick-article-forward)
3379   (gnus-summary-add "-" 'gnus-summary-tick-article-forward)
3380   (gnus-summary-add "U" 'gnus-summary-tick-article-backward)
3381   (gnus-summary-add "d" 'gnus-summary-mark-as-read-forward)
3382   (gnus-summary-add "D" 'gnus-summary-mark-as-read-backward)
3383   (gnus-summary-add "\M-u" 'gnus-summary-clear-mark-forward)
3384   (gnus-summary-add "\M-U" 'gnus-summary-clear-mark-backward)
3385   (gnus-summary-add "k" 'gnus-summary-kill-same-subject-and-select)
3386   (gnus-summary-add "\C-k" 'gnus-summary-kill-same-subject)
3387   (gnus-summary-add "\M-\C-t" 'gnus-summary-toggle-threads)
3388   (gnus-summary-add "\M-\C-s" 'gnus-summary-show-thread)
3389   (gnus-summary-add "\M-\C-h" 'gnus-summary-hide-thread)
3390   (gnus-summary-add "\M-\C-f" 'gnus-summary-next-thread)
3391   (gnus-summary-add "\M-\C-b" 'gnus-summary-prev-thread)
3392   (gnus-summary-add "\M-\C-u" 'gnus-summary-up-thread)
3393   (gnus-summary-add "\M-\C-d" 'gnus-summary-down-thread)
3394   (gnus-summary-add "\M-\C-k" 'gnus-summary-kill-thread)
3395   (gnus-summary-add "&" 'gnus-summary-execute-command)
3396   (gnus-summary-add "c" 'gnus-summary-catchup-and-exit)
3397   (gnus-summary-add "\C-t" 'gnus-summary-toggle-truncation)
3398   (gnus-summary-add "\M-d" 'gnus-summary-delete-marked-as-read)
3399   (gnus-summary-add "\C-c\M-\C-d" 'gnus-summary-delete-marked-with)
3400   (gnus-summary-add "x" 'gnus-summary-mark-as-expirable)
3401   (gnus-summary-add "X" 'gnus-summary-unmark-as-expirable)
3402   (gnus-summary-add "\M-\C-x" 'gnus-summary-expire-articles)
3403   (gnus-summary-add [M-DEL] 'gnus-summary-delete-article)
3404   (gnus-summary-add "b" 'gnus-summary-set-bookmark)
3405   (gnus-summary-add "B" 'gnus-summary-remove-bookmark)
3406   (gnus-summary-add "+" 'gnus-summary-mark-as-dormant)
3407   (gnus-summary-add "\M-+" 'gnus-summary-show-all-dormant)
3408   (gnus-summary-add "\C-c\M-\C-s" 'gnus-summary-show-all-expunged)
3409   (gnus-summary-add "\C-c\C-sn" 'gnus-summary-sort-by-number)
3410   (gnus-summary-add "\C-c\C-sa" 'gnus-summary-sort-by-author)
3411   (gnus-summary-add "\C-c\C-ss" 'gnus-summary-sort-by-subject)
3412   (gnus-summary-add "\C-c\C-sd" 'gnus-summary-sort-by-date)
3413   (gnus-summary-add "\C-c\C-s\C-n" 'gnus-summary-sort-by-number)
3414   (gnus-summary-add "\C-c\C-s\C-a" 'gnus-summary-sort-by-author)
3415   (gnus-summary-add "\C-c\C-s\C-s" 'gnus-summary-sort-by-subject)
3416   (gnus-summary-add "\C-c\C-s\C-d" 'gnus-summary-sort-by-date)
3417   (gnus-summary-add "=" 'gnus-summary-expand-window)
3418   (gnus-summary-add "\C-x\C-s" 'gnus-summary-reselect-current-group)
3419   (gnus-summary-add "\M-g" 'gnus-summary-rescan-group)
3420   (gnus-summary-add "w" 'gnus-summary-stop-page-breaking)
3421   (gnus-summary-add "\C-c\C-r" 'gnus-summary-caesar-message)
3422   (gnus-summary-add "g" 'gnus-summary-show-article)
3423   (gnus-summary-add "t" 'gnus-summary-toggle-header)
3424   (gnus-summary-add "\M-t" 'gnus-summary-toggle-mime)
3425   (gnus-summary-add "\C-d" 'gnus-summary-rmail-digest)
3426   (gnus-summary-add "a" 'gnus-summary-post-news)
3427   (gnus-summary-add "f" 'gnus-summary-followup)
3428   (gnus-summary-add "F" 'gnus-summary-followup-with-original)
3429   (gnus-summary-add "C" 'gnus-summary-cancel-article)
3430   (gnus-summary-add "S" 'gnus-summary-supersede-article)
3431   (gnus-summary-add "r" 'gnus-summary-reply)
3432   (gnus-summary-add "R" 'gnus-summary-reply-with-original)
3433   (gnus-summary-add "\C-c\C-f" 'gnus-summary-mail-forward)
3434   (gnus-summary-add "m" 'gnus-summary-mail-other-window)
3435   (gnus-summary-add "o" 'gnus-summary-save-article)
3436   (gnus-summary-add "\C-o" 'gnus-summary-save-article-rmail)
3437   (gnus-summary-add "|" 'gnus-summary-pipe-output)
3438   (gnus-summary-add "\M-m" 'gnus-summary-move-article)
3439   (gnus-summary-add "\M-\C-m" 'gnus-summary-respool-article)
3440   (gnus-summary-add "\M-k" 'gnus-summary-edit-local-kill)
3441   (gnus-summary-add "\M-K" 'gnus-summary-edit-global-kill)
3442   (gnus-summary-add "V" 'gnus-version)
3443   (gnus-summary-add "\C-c\C-d" 'gnus-summary-describe-group)
3444   (gnus-summary-add "q" 'gnus-summary-exit)
3445   (gnus-summary-add "Q" 'gnus-summary-quit)
3446   (gnus-summary-add "?" 'gnus-summary-describe-briefly)
3447   ;;(gnus-summary-add "\C-c\C-i" 'gnus-info-find-node)
3448   (gnus-summary-add [mouse-2] 'gnus-mouse-pick-article)
3449   (gnus-summary-add "\C-c\C-x" 'gnus-kill-file-set-expunge-below)
3450   (gnus-summary-add "\C-c\C-m" 'gnus-kill-file-set-mark-below)
3451   (define-key gnus-summary-raise-map "\C-s" 
3452     'gnus-summary-temporarily-raise-by-subject)
3453   (define-key gnus-summary-raise-map "\C-a" 
3454     'gnus-summary-temporarily-raise-by-author)
3455   (define-key gnus-summary-raise-map "\C-t" 
3456     'gnus-summary-temporarily-raise-by-thread)
3457   (define-key gnus-summary-raise-map "\C-x" 
3458     'gnus-summary-temporarily-raise-by-xref)
3459   (define-key gnus-summary-raise-map "s" 'gnus-summary-raise-by-subject)
3460   (define-key gnus-summary-raise-map "a" 'gnus-summary-raise-by-author)
3461   (define-key gnus-summary-raise-map "x" 'gnus-summary-raise-by-xref)
3462   (define-key gnus-summary-raise-map "f" 'gnus-summary-raise-followups-to-author)
3463   (define-key gnus-summary-lower-map "\C-s" 'gnus-summary-temporarily-lower-by-subject)
3464   (define-key gnus-summary-lower-map "\C-a" 'gnus-summary-temporarily-lower-by-author)
3465   (define-key gnus-summary-lower-map "\C-t" 'gnus-summary-temporarily-lower-by-thread)
3466   (define-key gnus-summary-lower-map "\C-x" 'gnus-summary-temporarily-lower-by-xref)
3467   (define-key gnus-summary-lower-map "s" 'gnus-summary-lower-by-subject)
3468   (define-key gnus-summary-lower-map "a" 'gnus-summary-lower-by-author)
3469   (define-key gnus-summary-lower-map "x" 'gnus-summary-lower-by-xref)
3470   (define-key gnus-summary-lower-map "f" 'gnus-summary-lower-followups-to-author)
3471   (gnus-summary-add "(" 'gnus-summary-lower-interest)
3472   (gnus-summary-add ")" 'gnus-summary-raise-interest)
3473   (gnus-summary-add "I" 'gnus-summary-set-interest)
3474   (gnus-summary-make-menu-bar))
3475 \f
3476
3477 (defun gnus-summary-mode ()
3478   "Major mode for reading articles in this newsgroup.
3479 All normal editing commands are switched off.
3480 The following commands are available:
3481
3482 \\<gnus-summary-mode-map>
3483 \\[gnus-summary-next-page]\t Scroll the article buffer a page forwards
3484 \\[gnus-summary-prev-page]\t Scroll the article buffer a page backwards
3485 \\[gnus-summary-scroll-up]\t Scroll the article buffer one line forwards
3486 \\[gnus-summary-next-unread-article]\t Go to the next unread article
3487 \\[gnus-summary-prev-unread-article]\t Go to the previous unread article
3488 \\[gnus-summary-next-article]\t Go to the next article
3489 \\[gnus-summary-prev-article]\t Go to the previous article
3490 \\[gnus-summary-next-same-subject]\t Go to the next summary line with the same subject
3491 \\[gnus-summary-prev-same-subject]\t Go to the previous summary line with the same subject
3492 \\[gnus-summary-next-digest]\t Go to the next digest
3493 \\[gnus-summary-prev-digest]\t Go to the previous digest
3494 \\[gnus-summary-next-subject]\t Go to the next summary line
3495 \\[gnus-summary-prev-subject]\t Go to the previous summary line
3496 \\[gnus-summary-next-unread-subject]\t Go to the next unread summary line
3497 \\[gnus-summary-prev-unread-subject]\t Go to the previous unread summary line
3498 \\[gnus-summary-first-unread-article]\t Go to the first unread article
3499 \\[gnus-summary-goto-subject]\t Go to some subject
3500 \\[gnus-summary-goto-last-article]\t Go to the previous article
3501
3502 \\[gnus-summary-beginning-of-article]\t Go to the beginning of the article
3503 \\[gnus-summary-end-of-article]\t Go to the end of the article
3504
3505 \\[gnus-summary-refer-parent-article]\t Get the parent of the current article from the server
3506 \\[gnus-summary-refer-article]\t Request some article by Message-ID from the server
3507
3508 \\[gnus-summary-isearch-article]\t Do an interactive search on the current article
3509 \\[gnus-summary-search-article-forward]\t Search all articles forward for a regular expression
3510 \\[gnus-summary-search-article-backward]\t Search all articles backward for a regular expression
3511
3512 \\[gnus-summary-tick-article-forward]\t Tick current article and move forward
3513 \\[gnus-summary-tick-article-backward]\t Tick current article and move backward
3514 \\[gnus-summary-mark-as-read-forward]\t Mark the current article as read and move forward
3515 \\[gnus-summary-mark-as-read-backward]\t Mark the current article as read and move backward
3516 \\[gnus-summary-clear-mark-forward]\t Clear tick and read marks and move forward
3517 \\[gnus-summary-clear-mark-backward]\t Clear tick and read marks and move backward
3518 \\[gnus-summary-mark-as-processable]\t Set the process mark on the current article
3519 \\[gnus-summary-unmark-as-processable]\t Remove the process mark from the current article
3520 \\[gnus-summary-unmark-all-processable]\t Remove the process mark from all articles
3521
3522 \\[gnus-summary-kill-same-subject-and-select]\t Kill all articles with the current subject and select the next article
3523 \\[gnus-summary-kill-same-subject]\t Kill all articles with the current subject
3524
3525 \\[gnus-summary-toggle-threads]\t Toggle thread display
3526 \\[gnus-summary-show-thread]\t Show the current thread
3527 \\[gnus-summary-hide-thread]\t Hide the current thread
3528 \\[gnus-summary-next-thread]\t Go to the next thread
3529 \\[gnus-summary-prev-thread]\t Go to the previous thread
3530 \\[gnus-summary-up-thread]\t Go up the current thread
3531 \\[gnus-summary-down-thread]\t Descend the current thread
3532 \\[gnus-summary-kill-thread]\t Kill the current thread
3533 \\[gnus-summary-mark-as-expirable]\t Mark the current artivles as expirable
3534 \\[gnus-summary-unmark-as-expirable]\t Remove the expirable mark from the current article
3535 \\[gnus-summary-delete-marked-as-read]\t Delete all articles that are marked as read
3536 \\[gnus-summary-delete-marked-with]\t Delete all articles that have some mark
3537
3538 \\[gnus-summary-execute-command]\t Execute a command
3539 \\[gnus-summary-catchup-and-exit]\t Mark all unread articles as read and exit
3540 \\[gnus-summary-toggle-truncation]\t Toggle truncation of summary lines
3541 \\[gnus-summary-expand-window]\t Expand the summary window
3542
3543 \\[gnus-summary-sort-by-number]\t Sort the Summary buffer by article number
3544 \\[gnus-summary-sort-by-author]\t Sort the Summary buffer by author
3545 \\[gnus-summary-sort-by-subject]\t Sort the Summary buffer by subject
3546 \\[gnus-summary-sort-by-date]\t Sort the Summary buffer by date
3547
3548 \\[gnus-summary-reselect-current-group]\t Exit and reselect the current group
3549 \\[gnus-summary-rescan-group]\t Exit, get new articles and reselect the group
3550 \\[gnus-summary-stop-page-breaking]\t Stop page breaking of the current article
3551 \\[gnus-summary-caesar-message]\t Caesar rotate (rot13) the current article
3552 \\[gnus-summary-show-article]\t Reselect the current article
3553 \\[gnus-summary-toggle-header]\t Toggle header display
3554 \\[gnus-summary-toggle-mime]\t Toggle whether to use MIME
3555 \\[gnus-summary-rmail-digest]\t Use rmail digest
3556 \\[gnus-summary-post-news]\t Post an article to the current group
3557 \\[gnus-summary-followup]\t Post a followup to the current article
3558 \\[gnus-summary-followup-with-original]\t Post a followup and include the original article
3559 \\[gnus-summary-cancel-article]\t Cancel the current article
3560 \\[gnus-summary-supersede-article]\t Supersede the current article
3561 \\[gnus-summary-reply]\t Mail a reply to the author of the current article
3562 \\[gnus-summary-reply-with-original]\t Mail a reply and include the current article
3563 \\[gnus-summary-mail-forward]\t Forward the current article
3564 \\[gnus-summary-mail-other-window]\t Mail in the other window
3565 \\[gnus-summary-save-article]\t Save the current article
3566 \\[gnus-summary-save-article-rmail]\t Save the current article in rmail format
3567 \\[gnus-summary-pipe-output]\t Pipe the current article to a process
3568 \\[gnus-summary-move-article]\t Move the article to a different newsgroup
3569 \\[gnus-summary-respool-article]\t Respool the article
3570 \\[gnus-summary-edit-local-kill]\t Edit the local kill file
3571 \\[gnus-summary-edit-global-kill]\t Edit the global kill file
3572 \\[gnus-version]\t Display the current Gnus version
3573 \\[gnus-summary-exit]\t Exit the Summary buffer 
3574 \\[gnus-summary-quit]\t Exit the Summary buffer without saving any changes
3575 \\[gnus-summary-describe-group]\t Describe the current newsgroup
3576 \\[gnus-summary-describe-briefly]\t Give a brief key overview
3577 \\[gnus-info-find-node]\t Go to the Gnus info node
3578
3579 \\[gnus-kill-file-set-expunge-below]    Automatically expunge articles below LEVEL.
3580
3581 \\[gnus-kill-file-set-mark-below]       Automatically mark articles below LEVEL.
3582 \\[gnus-summary-temporarily-raise-by-subject]\t Temporarily raise score for articles with the current subject
3583 \\[gnus-summary-temporarily-raise-by-author]\t Temporarily raise score for articles from the current author
3584 \\[gnus-summary-temporarily-raise-by-xref]\t Temporarily raise score for articles with the current cross-posting
3585 \\[gnus-summary-raise-by-subject]\t Permanently raise score for articles with the current subject
3586 \\[gnus-summary-raise-by-author]\t Permanently raise score for articles from the current author
3587 \\[gnus-summary-raise-followups-to-author]\t Permanently raise score for followups to the current author
3588 \\[gnus-summary-raise-by-xref]\t Permanently raise score for articles with the current cross-posting
3589 \\[gnus-summary-temporarily-lower-by-subject]\t Temporarily lower score for articles with the current subject
3590 \\[gnus-summary-temporarily-lower-by-author]\t Temporarily lower score for articles from the current author
3591 \\[gnus-summary-temporarily-lower-by-xref]\t Temporarily lower score for articles with the current cross-posting
3592 \\[gnus-summary-lower-by-subject]\t Permanently lower score for articles with the current subject
3593 \\[gnus-summary-lower-by-author]\t Permanently lower score for articles from the current author
3594 \\[gnus-summary-lower-followups-to-author]\t Permanently lower score for followups to the current author
3595 \\[gnus-summary-lower-by-thread]\t Permanently lower score for articles in the current thread
3596 \\[gnus-summary-lower-by-xref]\t Permanently lower score for articles with the current cross-posting
3597 "
3598   (interactive)
3599   (kill-all-local-variables)
3600   (let ((locals gnus-summary-local-variables))
3601     (while locals
3602       (make-local-variable (car locals))
3603       (set (car locals) nil)
3604       (setq locals (cdr locals))))
3605   (gnus-update-format-specifications)
3606   (setq mode-line-modified "--- ")
3607   (setq major-mode 'gnus-summary-mode)
3608   (setq mode-name "Summary")
3609   (make-local-variable 'minor-mode-alist)
3610   (or (assq 'gnus-show-threads minor-mode-alist)
3611       (setq minor-mode-alist
3612             (cons (list 'gnus-show-threads " Thread") minor-mode-alist)))
3613   (gnus-set-mode-line 'summary)
3614   (use-local-map gnus-summary-mode-map)
3615   (buffer-disable-undo (current-buffer))
3616   (setq buffer-read-only t)             ;Disable modification
3617   (setq truncate-lines t)
3618   (setq selective-display t)
3619   (setq selective-display-ellipses t)   ;Display `...'
3620   (run-hooks 'gnus-summary-mode-hook))
3621
3622 (defun gnus-mouse-pick-article (e)
3623   (interactive "e")
3624   (mouse-set-point e)
3625   (gnus-summary-next-page nil t))
3626
3627 (defun gnus-summary-setup-buffer (group)
3628   "Initialize Summary buffer."
3629   (let ((buffer (concat "*Summary " group "*")))
3630     ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
3631     (setq gnus-summary-buffer (set-buffer (get-buffer-create buffer)))
3632     (gnus-add-current-to-buffer-list)
3633     (gnus-summary-mode)))
3634
3635 (defun gnus-summary-insert-dummy-line (sformat subject number)
3636   (if (not sformat) 
3637       (setq sformat gnus-summary-dummy-line-format-spec))
3638   (let (b)
3639     (beginning-of-line)
3640     (insert (eval sformat))
3641     (forward-char -1)
3642     (setq b (point))
3643     (insert (format "%s Z %d 0" subject number))
3644     (set-text-properties b (point) '(invisible t))
3645     (forward-char 1)))
3646
3647 (defun gnus-summary-insert-line 
3648   (sformat header level current unread replied expirable subject-or-nil
3649            &optional dummy)
3650   (if (not sformat) 
3651       (setq sformat gnus-summary-line-format-spec))
3652   (let* ((indentation 
3653           (make-string (* level gnus-thread-indent-level) ? ))
3654          (lines (or (header-lines header) 0))
3655          (interest (or gnus-summary-default-interest " "))
3656          (replied (if replied ?R ? ))
3657          (expirable (if expirable ?X ? ))
3658          (from (header-from header))
3659          (name-address (gnus-extract-address-components from))
3660          (address (car (cdr name-address)))
3661          (name (or (car name-address) (car (cdr name-address))))
3662          (number (header-number header))
3663          (subject (header-subject header))
3664          (buffer-read-only nil)
3665          (opening-bracket (if dummy ?\< ?\[))
3666          (closing-bracket (if dummy ?\> ?\]))
3667          b)
3668     ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
3669     (if (not (numberp lines)) (setq lines 0))
3670     (beginning-of-line)
3671     (setq b (point))
3672     (insert-before-markers (eval sformat))
3673     (forward-char -1)
3674     (if (and gnus-visual gnus-mouse-face)
3675         (overlay-put (make-overlay b (point)) 'mouse-face gnus-mouse-face))
3676     ;; Info format SUBJECT INTEREST UNREAD NUMBER LEVEL
3677     (set-text-properties
3678      (prog1
3679          (point)
3680        (insert (format "%s %d %c %d %d" (gnus-simplify-subject-re subject)
3681                        (or gnus-summary-default-interest 5)
3682                        unread number level)))
3683      (point) '(invisible t))
3684     (forward-char 1)))
3685
3686 (defun gnus-summary-update-lines ()
3687   ;; Rehighlight summary buffer according to `gnus-summary-highlight'.
3688   (if (and gnus-visual gnus-visual-summary-update-hook)
3689       (save-excursion
3690         (set-buffer gnus-summary-buffer)
3691         (goto-char (point-min))
3692         (while (not (eobp))
3693           (run-hooks 'gnus-summary-update-hook)
3694           (forward-line 1)))))
3695
3696 ;; Suggested by Daniel Quinlan <quinlan@best.com>.  
3697   
3698
3699 (defun gnus-summary-read-group (group &optional show-all no-article kill-buffer)
3700   "Start reading news in newsgroup GROUP.
3701 If SHOW-ALL is non-nil, already read articles are also listed.
3702 If NO-ARTICLE is non-nil, no article is selected initially."
3703   (message "Retrieving newsgroup: %s..." (gnus-group-real-name group))
3704   (gnus-summary-setup-buffer group)
3705   (if (gnus-select-newsgroup group show-all)
3706       (progn
3707         ;; You can change the order of subjects in this hook.
3708         (run-hooks 'gnus-select-group-hook)
3709         (gnus-summary-prepare)
3710         (if (and (zerop (buffer-size))
3711                  gnus-newsgroup-dormant)
3712             (gnus-summary-show-all-dormant))
3713         (let ((killed 
3714                (gnus-add-to-range 
3715                 gnus-newsgroup-killed 
3716                 (setq gnus-newsgroup-unreads
3717                       (sort gnus-newsgroup-unreads (function <)))))
3718               (gnus-newsgroup-killed 
3719                (if gnus-kill-killed nil gnus-newsgroup-killed)))
3720           (if (not (consp (car killed))) (setq killed (list killed)))
3721           ;; Function `gnus-apply-kill-file' must be called in this hook.
3722           (run-hooks 'gnus-apply-kill-hook)
3723           (setq gnus-newsgroup-killed killed))
3724         (if (zerop (buffer-size))
3725             (progn
3726               ;; This newsgroup is empty.
3727               (gnus-summary-catchup-and-exit nil t) ;Without confirmations.
3728               (message "No unread news"))
3729           ;; Hide conversation thread subtrees.  We cannot do this in
3730           ;; gnus-summary-prepare-hook since kill processing may not
3731           ;; work with hidden articles.
3732           (and gnus-show-threads
3733                gnus-thread-hide-subtree
3734                (gnus-summary-hide-all-threads))
3735           ;; Show first unread article if requested.
3736           (goto-char (point-min))
3737           (if (and (not no-article)
3738                    gnus-auto-select-first
3739                    (gnus-summary-first-unread-article))
3740               ;; Window is configured automatically.
3741               ;; Current buffer may be changed as a result of hook
3742               ;; evaluation, especially by gnus-summary-rmail-digest
3743               ;; command, so we should adjust cursor point carefully.
3744               (if (eq major-mode 'gnus-summary-mode)
3745                   (gnus-summary-position-cursor))
3746             (gnus-configure-windows 'summary)
3747             (pop-to-buffer gnus-summary-buffer)
3748             (gnus-set-mode-line 'summary)
3749             (gnus-summary-position-cursor))
3750           (if (and kill-buffer
3751                    (get-buffer kill-buffer)
3752                    (buffer-name (get-buffer kill-buffer)))
3753               (kill-buffer kill-buffer))))
3754     ;; Cannot select newsgroup GROUP.
3755     (message "Couldn't select newsgroup")
3756     (set-buffer gnus-group-buffer)
3757     (gnus-summary-position-cursor)))
3758
3759 (defun gnus-summary-prepare ()
3760   "Prepare summary list of current newsgroup in Summary buffer."
3761   (let ((buffer-read-only nil))
3762     (erase-buffer)
3763     (gnus-summary-prepare-threads 
3764           (if gnus-show-threads
3765               (gnus-gather-threads (gnus-make-threads))
3766             gnus-newsgroup-headers)
3767           0)
3768     (gnus-summary-delete-dormant)
3769     ;; Erase header retrieval message.
3770     (message "")
3771     ;; Call hooks for modifying Summary buffer.
3772     ;; Suggested by sven@tde.LTH.Se (Sven Mattisson).
3773     (goto-char (point-min))
3774     (run-hooks 'gnus-summary-prepare-hook)))
3775
3776 (defun gnus-summary-delete-dormant ()
3777   (let ((int gnus-newsgroup-dormant)
3778         (buffer-read-only nil)
3779         beg cur-level)
3780     (while int
3781       (if (gnus-summary-goto-subject (car int))
3782           (progn
3783             (beginning-of-line)
3784             (setq cur-level (gnus-summary-thread-level))
3785             (setq beg (point))
3786             (re-search-forward "[\n\r]")
3787             (if (<= (gnus-summary-thread-level) cur-level)
3788                 ;; If the level of the next article is greater than the
3789                 ;; level of this article, then it has to be the child of this
3790                 ;; article, so we do not delete this article.
3791                 (progn
3792                   (setq gnus-newsgroup-dormant-subjects
3793                         (cons (cons (car int) (buffer-substring beg (point)))
3794                               gnus-newsgroup-dormant-subjects))
3795                   (delete-region beg (point))))))
3796       (setq int (cdr int)))))
3797
3798 (defun gnus-gather-threads (threads)
3799   "Gather threads that have lost their roots."
3800   (if (not gnus-gather-loose-threads)
3801       threads 
3802     (let ((hashtb (gnus-make-hashtable 1023))
3803           (prev threads)
3804           (result threads)
3805           thread subject hthread unre-subject)
3806       (while threads
3807         (setq subject (header-subject (car (car threads))))
3808         (if (setq hthread (gnus-gethash 
3809                            (setq unre-subject 
3810                                  (gnus-simplify-subject-re subject))
3811                            hashtb))
3812             (progn
3813               (if (not (stringp (car (car hthread))))
3814                   (setcar hthread (list subject (car hthread))))
3815               (setcar hthread
3816                       (append (car hthread) (cons (car threads) nil)))
3817               (setcdr prev (cdr threads))
3818               (setq threads prev))
3819           (gnus-sethash unre-subject threads hashtb))
3820         (setq prev threads)
3821         (setq threads (cdr threads)))
3822       result)))
3823
3824 (defun gnus-make-threads ()
3825   ;; This function takes the dependencies already made by
3826   ;; `gnus-get-newsgroup-headers' and builds the trees. First we go
3827   ;; through the dependecies in the hash table and finds all the
3828   ;; roots. Roots do not refer back to any valid articles. 
3829   (let (roots mroots)
3830     (mapatoms
3831      (lambda (refs)
3832        (if (not (car (symbol-value refs)))
3833            (setq mroots (append (cdr (symbol-value refs)) mroots))
3834          ;; Ok, these refer back to valid articles, but if
3835          ;; `gnus-thread-ignore-subject' is nil, we have to check that
3836          ;; the root has the same subject as its children. The children
3837          ;; that do not are made into roots and removed from the list
3838          ;; of children. 
3839          (or gnus-thread-ignore-subject
3840              (let* ((prev (symbol-value refs))
3841                     (subject (gnus-simplify-subject-re 
3842                               (header-subject (car prev))))
3843                     (headers (cdr prev)))
3844                (while headers
3845                  (if (not (string= subject
3846                                    (gnus-simplify-subject-re 
3847                                     (header-subject (car headers)))))
3848                      (progn
3849                        (setq mroots (cons (car headers) mroots))
3850                        (setcdr prev (cdr headers)))
3851                    (setq prev headers))
3852                  (setq headers (cdr headers)))))))
3853      gnus-newsgroup-dependencies)
3854
3855     ;; We sort the roots according to article number. (This has to be
3856     ;; done because all sequencing information was lost when we built
3857     ;; the dependecies hash table.)
3858     (setq roots
3859           (sort
3860            mroots
3861            (lambda (h1 h2)
3862              (< (header-number h1) (header-number h2)))))
3863     ;; Now we have all the roots, so we go through all them all and
3864     ;; build the trees. 
3865     (mapcar (lambda (root) (gnus-make-sub-thread root)) roots)))
3866
3867 (defun gnus-make-sub-thread (root)
3868   ;; This function makes a sub-tree for a node in the tree.
3869   (let ((children (reverse (cdr (gnus-gethash (downcase (header-id root))
3870                                               gnus-newsgroup-dependencies)))))
3871     (if (not children)
3872         (list root)
3873       (cons root (mapcar 
3874                   (lambda (top) (gnus-make-sub-thread top)) children)))))
3875
3876 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
3877 (defvar gnus-tmp-prev-subject "")
3878
3879 ;; Basic ideas by Paul Dworkin <paul@media-lab.media.mit.edu>
3880 ;; Subject bug fix by jbw@bigbird.bu.edu (Joe Wells)
3881 (defun gnus-summary-prepare-threads 
3882   (threads level &optional not-child no-subject)
3883   "Prepare Summary buffer from THREADS and indentation LEVEL.  
3884 THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'  
3885 or a straight list of headers."
3886   (let (thread header number subject clevel)
3887     (while threads
3888       (setq thread (car threads)
3889             threads (cdr threads))
3890       ;; If `thread' is a cons, hierarchical threads are used.  If not,
3891       ;; `thread' is the header.
3892       (if (consp thread)
3893           (setq header (car thread))
3894         (setq header thread))
3895       (if (stringp header)
3896           ;; The header is a dummy root.
3897           (progn
3898             (cond ((eq gnus-summary-make-false-root 'adopt)
3899                    ;; We let the first article adopt the rest.
3900                    (gnus-summary-prepare-threads (list (car (cdr thread))) 0)
3901                    (setq thread (cdr (cdr thread)))
3902                    (while thread
3903                      (gnus-summary-prepare-threads (list (car thread)) 1 t)
3904                      (setq thread (cdr thread))))
3905                   ((eq gnus-summary-make-false-root 'dummy)
3906                    ;; We output a dummy root.
3907                    (gnus-summary-insert-dummy-line 
3908                     nil header (header-number (car (car (cdr thread)))))
3909                    (setq clevel 1))
3910                   ((eq gnus-summary-make-false-root 'empty)
3911                    ;; We print the articles with empty subject fields. 
3912                    (gnus-summary-prepare-threads (list (car (cdr thread))) 0)
3913                    (setq thread (cdr (cdr thread)))
3914                    (while thread
3915                      (gnus-summary-prepare-threads (list (car thread)) 0 nil t)
3916                      (setq thread (cdr thread))))
3917                   (t
3918                    ;; We do not make a root for the gathered
3919                    ;; sub-threads at all.  
3920                    (setq clevel 0)))
3921             ;; Print the sub-threads.
3922             (and (consp thread) (cdr thread)
3923                  (gnus-summary-prepare-threads (cdr thread) clevel)))
3924         ;; The header is a real article.
3925         (setq number (header-number header)
3926               subject (header-subject header)
3927               gnus-tmp-prev-subject subject)
3928         (gnus-summary-insert-line
3929          nil header level nil 
3930          (cond ((memq number gnus-newsgroup-marked) ?-)
3931                ((memq number gnus-newsgroup-dormant) ?+)
3932                ((memq number gnus-newsgroup-unreads) ? )
3933                (t ?D))
3934          (memq number gnus-newsgroup-replied)
3935          (memq number gnus-newsgroup-expirable)
3936          (if no-subject gnus-summary-same-subject
3937            (if (or (zerop level)
3938                    (and gnus-thread-ignore-subject
3939                         (not (string= 
3940                               (gnus-simplify-subject-re gnus-tmp-prev-subject)
3941                               (gnus-simplify-subject-re subject)))))
3942                subject
3943              gnus-summary-same-subject))
3944          not-child)
3945         ;; Recursively print subthreads.
3946         (and (consp thread) (cdr thread)
3947              (gnus-summary-prepare-threads (cdr thread) (1+ level)))))))
3948
3949 (defun gnus-select-newsgroup (group &optional show-all)
3950   "Select newsgroup GROUP.
3951 If SHOW-ALL is non-nil, all articles in the group are selected."
3952   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
3953          (real-group (gnus-group-real-name group))
3954          (info (nth 2 entry))
3955          articles header-marks)
3956
3957     (if (eq (car entry) t)
3958         (or (if (nth 4 info) 
3959                 (gnus-activate-foreign-newsgroup info)
3960               (gnus-activate-newsgroup (car info)))
3961             (error "Couldn't request newsgroup %s" group)))
3962     (setq gnus-current-select-method (or (nth 4 info)
3963                                          gnus-select-method))
3964     (gnus-check-news-server (nth 4 info))
3965     (if (not (gnus-request-group group t))
3966         (error "Couldn't request newsgroup %s" group))
3967
3968     (setq gnus-newsgroup-name group)
3969     (setq gnus-newsgroup-unselected nil)
3970     (setq gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
3971     (cond 
3972      ((or show-all
3973           ;; Check whether there are only dormant articles in this newsgroup. 
3974           (= (length gnus-newsgroup-unreads)
3975              (length (cdr (assq 'dormant (nth 3 info))))))
3976       ;; Select all active articles.
3977       (setq articles (gnus-uncompress-sequence 
3978                       (gnus-gethash group gnus-active-hashtb))))
3979      (t
3980       ;; Select unread articles only.
3981       (setq articles gnus-newsgroup-unreads)))
3982     ;; Require confirmation if selecting large newsgroup.
3983     (if (not (numberp gnus-large-newsgroup))
3984         nil
3985       (let ((number (length articles))
3986             selected break)
3987         (if (> number gnus-large-newsgroup)
3988             (progn
3989               (condition-case ()
3990                   (let ((input
3991                          (read-string
3992                           (format
3993                            "How many articles from %s (default %d): "
3994                            gnus-newsgroup-name number))))
3995                     (setq selected
3996                           (if (string-equal input "")
3997                               number (string-to-int input))))
3998                 (quit
3999                  (setq selected 0)))
4000               (if (< (abs selected) number)
4001                   (progn
4002                     (cond 
4003                      ((< selected 0) 
4004                       ;; Select the N oldest articles.
4005                       (setq articles (copy-sequence articles))
4006                       (setq break (nthcdr (1- (abs selected)) articles))
4007                       (setq gnus-newsgroup-unselected 
4008                             (gnus-intersection
4009                              (cdr break)
4010                              gnus-newsgroup-unreads))
4011                       (setcdr break nil))
4012                      ((> selected 0)
4013                       ;; Select the N most recent articles.
4014                       (setq gnus-newsgroup-unselected  
4015                             (copy-sequence articles))
4016                       (setq break (nthcdr (- number (1+ selected))
4017                                           gnus-newsgroup-unselected))
4018                       (setq articles (cdr break))
4019                       (setcdr break nil)
4020                       (setq gnus-newsgroup-unselected
4021                             (gnus-intersection
4022                              gnus-newsgroup-unselected
4023                              gnus-newsgroup-unreads)))
4024                      
4025                      (t
4026                       ;; Select no articles.
4027                       (setq gnus-newsgroup-unselected articles)
4028                       (setq articles nil)))))))
4029         ))
4030     (if (not articles)
4031         nil
4032       ;; Create the list of headers from the headers.
4033       (setq gnus-newsgroup-headers 
4034             (if (eq (gnus-retrieve-headers articles gnus-newsgroup-name) 'nov)
4035                 (progn
4036                   (gnus-get-newsgroup-headers-xover articles))
4037               (gnus-get-newsgroup-headers)))
4038       ;; Remove cancelled articles from the list of unread articles.
4039       (setq gnus-newsgroup-unreads
4040             (gnus-intersection gnus-newsgroup-unreads
4041                                (mapcar
4042                                 (lambda (headers)
4043                                   (header-number headers))
4044                                 gnus-newsgroup-headers)))
4045       ;; Ticked articles must be a subset of unread articles.
4046       (if info
4047           (progn
4048             (gnus-adjust-marked-articles info)
4049             (setq gnus-newsgroup-marked (cdr (assq 'tick (nth 3 info))))
4050             (setq gnus-newsgroup-replied (cdr (assq 'reply (nth 3 info))))
4051             (setq gnus-newsgroup-expirable (cdr (assq 'expire (nth 3 info))))
4052             (setq gnus-newsgroup-killed (cdr (assq 'killed (nth 3 info))))
4053             (setq gnus-newsgroup-bookmarks (cdr (assq 'bookmark (nth 3 info))))
4054             (setq gnus-newsgroup-dormant (cdr (assq 'dormant (nth 3 info))))
4055             (setq gnus-newsgroup-processable nil)))
4056       ;; Check whether auto-expire is to be done in this group.
4057       (setq gnus-newsgroup-auto-expire
4058             (and (stringp gnus-auto-expirable-newsgroups)
4059                  (string-match gnus-auto-expirable-newsgroups real-group)))
4060       ;; First and last article in this newsgroup.
4061       (setq gnus-newsgroup-begin
4062             (if gnus-newsgroup-headers
4063                 (header-number (car gnus-newsgroup-headers))
4064               0))
4065       (setq gnus-newsgroup-end
4066             (if gnus-newsgroup-headers
4067                 (header-number (gnus-last-element gnus-newsgroup-headers))
4068               0))
4069       ;; File name of the last saved article.
4070       (setq gnus-newsgroup-last-rmail nil)
4071       (setq gnus-newsgroup-last-mail nil)
4072       (setq gnus-newsgroup-last-folder nil)
4073       (setq gnus-newsgroup-last-file nil)
4074       ;; Reset article pointers etc.
4075       (setq gnus-current-article nil)
4076       (setq gnus-current-headers nil)
4077       (setq gnus-have-all-headers nil)
4078       (setq gnus-last-article nil)
4079       (setq gnus-xref-hashtb nil)
4080       (setq gnus-reffed-article-number -1)
4081       (setq gnus-newsgroup-headers-hashtb-by-number nil)
4082       ;; Update the format specifiers.
4083       (gnus-update-format-specifications)
4084       ;; GROUP is successfully selected.
4085       t)))
4086
4087 (defun gnus-adjust-marked-articles (info)
4088   "Remove all marked articles that are no longer legal."
4089   (let ((marked-lists (nth 3 info))
4090         (active (gnus-gethash (car info) gnus-active-hashtb))
4091         marked m prev)
4092     ;; There are four types of marked articles - ticked, replied,
4093     ;; expirable and dormant.  
4094     (while marked-lists
4095       (setq m (cdr (setq prev (car marked-lists))))
4096       (cond ((or (eq 'tick (car prev)) (eq 'dormant (car prev)))
4097              ;; Make sure that all ticked articles are a subset of the
4098              ;; unread/unselected articles.
4099              (while m
4100                (if (or (memq (car m) gnus-newsgroup-unreads)
4101                        (memq (car m) gnus-newsgroup-unselected))
4102                    (setq prev m)
4103                  (setcdr prev (cdr m)))
4104                (setq m (cdr m))))
4105             ((eq 'bookmark (car prev))
4106              ;; Bookmarks should be a subset of active articles.
4107              (while m
4108                (if (< (car (car m)) (car active))
4109                    (setcdr prev (cdr m))
4110                  (setq prev m))
4111                (setq m (cdr m))))
4112             ((eq 'killed (car prev))
4113              ;; Articles that have been through the kill process are
4114              ;; to be a subset of active articles.
4115              (while (and m (< (cdr (car m)) (car active)))
4116                (setcdr prev (cdr m)))
4117              (if (and m (< (car (car m)) (car active))) 
4118                  (setcar (car m) (car active))))
4119             ((or (eq 'reply (car marked)) (eq 'expire (car marked)))
4120              ;; The replied and expirable articles have to be articles
4121              ;; that are active. 
4122              (while m
4123                (if (< (car m) (car active))
4124                    (setcdr prev (cdr m))
4125                  (setq prev m))
4126                (setq m (cdr m)))))
4127       (setq marked-lists (cdr marked-lists)))
4128     ;; Remove all lists that are empty.
4129     (setq marked-lists (nth 3 info))
4130     (if marked-lists
4131         (progn
4132           (while (= 1 (length (car marked-lists)))
4133             (setq marked-lists (cdr marked-lists)))
4134           (setq m (cdr (setq prev marked-lists)))
4135           (while m
4136             (if (= 1 (length (car m)))
4137                 (setcdr prev (cdr m))
4138               (setq prev m))
4139             (setq m (cdr m)))
4140           (setcar (nthcdr 3 info) marked-lists)))
4141     ;; Finally, if there are no marked lists at all left, and if there
4142     ;; are no elements after the lists in the info list, we just chop
4143     ;; the info list off before the marked lists.
4144     (if (and (null marked-lists) (not (nthcdr 4 info)))
4145         (setcdr (nthcdr 2 info) nil)))
4146   info)
4147
4148 (defun gnus-set-marked-articles 
4149   (info ticked replied expirable killed dormant bookmark) 
4150   "Enter the various lists of marked articles into the newsgroup info list."
4151   (let (newmarked)
4152     (if ticked
4153         (setq newmarked (cons (cons 'tick ticked) nil)))
4154     (if replied 
4155         (setq newmarked (cons (cons 'reply replied) newmarked)))
4156     (if expirable 
4157         (setq newmarked (cons (cons 'expire expirable) newmarked)))
4158     (if killed
4159         (setq newmarked (cons (cons 'killed killed) newmarked)))
4160     (if dormant
4161         (setq newmarked (cons (cons 'dormant dormant) newmarked)))
4162     (if bookmark
4163         (setq newmarked (cons (cons 'bookmark bookmark) newmarked)))
4164     (if (nthcdr 3 info)
4165         (if newmarked
4166             (setcar (nthcdr 3 info) newmarked)
4167           (if (not (nthcdr 4 info))
4168               (setcdr (nthcdr 2 info) nil)
4169             (setcar (nthcdr 3 info) nil)))
4170       (if newmarked
4171           (setcdr (nthcdr 2 info) (cons newmarked nil))))))
4172
4173 (defun gnus-set-mode-line (where)
4174   "This function sets the mode line of the Article or Summary buffers.
4175 If WHERE is `summary', the summary mode line format will be used."
4176   (if (memq where gnus-updated-mode-lines)
4177       (let (mode-string)
4178         (save-excursion
4179           (set-buffer gnus-summary-buffer)
4180           (let* ((mformat (if (eq where 'article) 
4181                               gnus-article-mode-line-format-spec
4182                             gnus-summary-mode-line-format-spec))
4183                  (group-name gnus-newsgroup-name)
4184                  (article-number (or gnus-current-article 0))
4185                  (unread (- (length gnus-newsgroup-unreads)
4186                             (length gnus-newsgroup-dormant)))
4187                  (unselected (length gnus-newsgroup-unselected))
4188                  (unread-and-unselected
4189                   (cond ((and (zerop unread) (zerop unselected)) "")
4190                         ((zerop unselected) (format "{%d more}" unread))
4191                         (t (format "{%d(+%d) more}" unread unselected))))
4192                  (subject
4193                   (if gnus-current-headers
4194                       (header-subject gnus-current-headers) ""))
4195                  (max-len (if (eq where 'summary) 45 52)))
4196             (setq mode-string (eval mformat))
4197             (if (> (length mode-string) max-len) 
4198                 (setq mode-string 
4199                       (concat (substring mode-string 0 (- max-len 4)) "...")))
4200             (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
4201         (setq mode-line-buffer-identification mode-string)
4202         (set-buffer-modified-p t))))
4203
4204 (defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
4205   "Go through the HEADERS list and add all Xrefs to a hash table.
4206 The resulting hash table is returned, or nil if no Xrefs were found."
4207   (let ((prefix (if (and 
4208                      (string-match gnus-foreign-group-prefix from-newsgroup)
4209                      (not (eq 'nnvirtual (car gnus-current-select-method))))
4210                     gnus-foreign-group-prefix))
4211         (xref-hashtb (make-vector 63 0))
4212         start group entry number xrefs header)
4213     (while headers
4214       (setq header (car headers))
4215       (if (and (setq xrefs (header-xref header))
4216                (not (memq (header-number header) unreads)))
4217           (progn
4218             (setq start 0)
4219             (while (string-match "\\([^ :]+\\):\\([0-9]+\\)" xrefs start)
4220               (setq start (match-end 0))
4221               (setq group (concat prefix (substring xrefs (match-beginning 1) 
4222                                             (match-end 1))))
4223               (setq number 
4224                     (string-to-int (substring xrefs (match-beginning 2) 
4225                                               (match-end 2))))
4226               (if (setq entry (gnus-gethash group xref-hashtb))
4227                   (setcdr entry (cons number (cdr entry)))
4228                 (gnus-sethash group (cons number nil) xref-hashtb)))))
4229       (setq headers (cdr headers)))
4230     (if start xref-hashtb nil)))
4231
4232 (defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
4233   "Look through all the headers and mark the Xrefs as read."
4234   (let (name entry read info xref-hashtb idlist active num range)
4235     (set-buffer gnus-group-buffer)
4236     (if (setq xref-hashtb 
4237               (gnus-create-xref-hashtb from-newsgroup headers unreads))
4238         (mapatoms 
4239          (lambda (group)
4240            (if (string= from-newsgroup (setq name (symbol-name group)))
4241                ()
4242              (setq idlist (symbol-value group))
4243              ;; Dead groups are not updated.
4244              (if (and (setq entry (gnus-gethash name gnus-newsrc-hashtb))
4245                       ;; Only do the xrefs if the group has the same
4246                       ;; select method as the group we have just read.
4247                       (or (and (not (nth 4 (setq info (nth 2 entry))))
4248                                (eq gnus-current-select-method
4249                                    gnus-select-method))
4250                           (eq (car gnus-current-select-method) 'nnvirtual)
4251                           (equal (nth 4 info) 
4252                                  gnus-current-select-method)))
4253                  (progn
4254                    (setq num 0)
4255                    ;; Set the new list of read articles in this group.
4256                    (setq active (gnus-gethash name gnus-active-hashtb))
4257                    ;; First peel off all illegal article numbers.
4258                    (if active
4259                        (let ((id idlist))
4260                          (while id
4261                            (if (or (> (car id) (cdr active))
4262                                    (< (car id) (car active)))
4263                                (setq idlist (delq (car id) idlist)))
4264                            (setq id (cdr id)))))
4265                    (setcar (nthcdr 2 info)
4266                            (setq range
4267                                  (gnus-add-to-range 
4268                                   (nth 2 info) 
4269                                   (setq idlist (sort idlist '<)))))
4270                    ;; Then we have to re-compute how many unread
4271                    ;; articles there are in this group.
4272                    (if active
4273                        (progn
4274                          (if (atom (car range))
4275                              (if (not range)
4276                                  (setq num (- (1+ (cdr active)) (car active)))
4277                                (setq num (- (cdr active) (- (1+ (cdr range)) 
4278                                                             (car range)))))
4279                            (while range
4280                              (setq num (+ num (- (1+ (cdr (car range))) 
4281                                                  (car (car range)))))
4282                              (setq range (cdr range)))
4283                            (setq num (- (cdr active) num)))
4284                          ;; Update the number of unread articles.
4285                          (setcar entry (max 0 num))
4286                          ;; Update the Newsgroup buffer.
4287                          (gnus-group-update-group name t)))))))
4288          xref-hashtb))))
4289
4290 (defsubst gnus-header-value ()
4291   (buffer-substring (match-end 0) (save-excursion (end-of-line) (point))))
4292
4293 ;; Felix Lee function with jwz rewrites (and some lmi rewrites to boot).
4294 ;; Goes through the newsgroups headers and returns a list of arrays:
4295 (defun gnus-get-newsgroup-headers ()
4296   (setq gnus-article-internal-prepare-hook nil)
4297   (save-excursion
4298     (let ((cur nntp-server-buffer)
4299           (dependencies (gnus-make-hashtable (length gnus-newsgroup-unreads)))
4300           (none-id 0)
4301           headers header subject from char c article unreads in-reply-to
4302           references end-header id dep ref end)
4303       (set-buffer nntp-server-buffer)
4304       (goto-char 1)
4305       (while (re-search-forward "^[23][0-9]+ \\([0-9]+\\)" nil t)
4306         (setq from nil
4307               subject nil
4308               in-reply-to nil
4309               references nil
4310               ref nil
4311               header (make-vector 9 nil)
4312               c (following-char))
4313         (goto-char (match-beginning 1))
4314         (header-set-number 
4315          header (setq article (read cur)))
4316         (setq end-header (save-excursion (search-forward "\n.\n" nil t)))
4317         (while (re-search-forward "^\\(from\\|subject\\|message-id\\|date\\|lines\\|xref\\|references\\|in-reply-to\\): "
4318                                    end-header t)
4319           (beginning-of-line)
4320           (setq char (downcase (following-char))) 
4321           (cond
4322            ((eq char ?s)
4323             (header-set-subject header 
4324                                 (setq subject (gnus-header-value))))
4325            ((eq char ?f)
4326             (header-set-from header (setq from (gnus-header-value))))
4327            ((eq char ?x)
4328             (header-set-xref header (gnus-header-value)))
4329            ((eq char ?l)
4330             (header-set-lines header 
4331                                    (string-to-int (gnus-header-value))))
4332            ((eq char ?d)
4333             (header-set-date header (gnus-header-value)))
4334            ((eq char ?m)
4335             (header-set-id header (setq id (gnus-header-value))))
4336            ((eq char ?r)
4337             (setq references (gnus-header-value))
4338             (setq end (match-end 0))
4339             (save-excursion
4340               (setq ref 
4341                     (downcase
4342                      (buffer-substring
4343                       (progn 
4344                         (end-of-line)
4345                         (search-backward ">" end t)
4346                         (1+ (point)))
4347                       (progn
4348                         (search-backward "<" end t)
4349                         (point)))))))
4350            ((eq char ?i)
4351             (setq in-reply-to (gnus-header-value))))
4352           (forward-line 1))
4353         (if references
4354             (header-set-references header references)
4355           (and in-reply-to
4356                (string-match "<[^>]+>" in-reply-to)
4357                (header-set-references 
4358                 header
4359                 (setq ref (downcase (substring in-reply-to (match-beginning 0)
4360                                                (match-end 0)))))))
4361         (or subject (header-set-subject header "(none)"))
4362         (or from (header-set-from header "(nobody)"))
4363         ;; We build the thread tree.
4364         (if (boundp 
4365              (setq dep 
4366                    (intern 
4367                     (downcase
4368                      (or id
4369                          (concat "none+" (int-to-string 
4370                                           (setq none-id (1+ none-id))))))
4371                     dependencies)))
4372             (setcar (symbol-value dep) header)
4373           (set dep (list header)))
4374         (if (boundp (setq dep (intern (or ref "none") dependencies)))
4375             (setcdr (symbol-value dep) 
4376                     (cons header (cdr (symbol-value dep))))
4377           (set dep (list nil header)))
4378         (setq headers (cons header headers))
4379         (forward-line -1)
4380         (search-forward "\n.\n" nil t))
4381       (setq gnus-newsgroup-dependencies dependencies)
4382       (nreverse headers))))
4383
4384 ;; The following macros and functions were written by Felix Lee
4385 ;; <flee@cse.psu.edu>. 
4386
4387 ;; This is almost 4x faster than (string-to-int (buffer-substring ... ))
4388 ;; primarily because of garbage collection.  -jwz
4389 (defmacro gnus-read-integer (&optional point move-p)
4390   (` ((, (if move-p 'progn 'save-excursion))
4391       (,@ (if point (list (list 'goto-char point))))
4392       (if (and (<= (following-char) ?9)
4393                (>= (following-char) ?0))
4394           (read (current-buffer))
4395         0))))
4396
4397 (defmacro gnus-nov-skip-field ()
4398   '(search-forward "\t" eol 'end))
4399
4400 (defmacro gnus-nov-field ()
4401   '(buffer-substring
4402     (point)
4403     (progn (gnus-nov-skip-field) (1- (point)))))
4404
4405 ;; Goes through the xover lines and returns a list of vectors
4406 (defun gnus-get-newsgroup-headers-xover (sequence)
4407   "Parse the news overview data in the server buffer, and return a
4408 list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
4409   ;; Get the Xref when the users reads the articles since most/some
4410   ;; NNTP servers do not include Xrefs when using XOVER.
4411   (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
4412   (save-excursion
4413     (set-buffer nntp-server-buffer)
4414     (let ((cur (current-buffer))
4415           (dependencies (gnus-make-hashtable (length gnus-newsgroup-unreads)))
4416           (none 0)
4417           number header headers eol header id ref dep)
4418       (goto-char (point-min))
4419       (while (and sequence (not (eobp)))
4420         (setq number (read cur))
4421         (while (and sequence (< (car sequence) number))
4422           (setq sequence (cdr sequence)))
4423         (and sequence 
4424              (eq number (car sequence))
4425              (progn
4426                (setq sequence (cdr sequence))
4427                (save-excursion
4428                  (end-of-line)
4429                  (setq eol (point)))
4430                (forward-char)
4431                ;; overview: [num subject from date id refs chars lines misc]
4432                (setq header
4433                      (vector 
4434                       number           ; number
4435                       (gnus-nov-field) ; subject
4436                       (gnus-nov-field) ; from
4437                       (gnus-nov-field) ; date
4438                       (setq id (gnus-nov-field)) ; id
4439                       (progn
4440                         (save-excursion
4441                           (let ((beg (point)))
4442                           (search-forward "\t" eol)
4443                           (if (search-backward ">" beg t)
4444                               (setq ref 
4445                                     (downcase 
4446                                      (buffer-substring 
4447                                       (1+ (point))
4448                                       (progn
4449                                         (search-backward "<" beg t)
4450                                         (point)))))
4451                             (setq ref nil))))
4452                         (gnus-nov-field)) ; refs
4453                       (read cur)       ; chars
4454                       (read cur)       ; lines
4455                       (if (/= (following-char) ?\t)
4456                           nil
4457                         (forward-char 1)
4458                         (gnus-nov-field)) ; misc
4459                       ))
4460                ;; We build the thread tree.
4461                (if (boundp 
4462                     (setq dep 
4463                           (intern 
4464                            (downcase 
4465                             (or id (concat "none+"
4466                                            (int-to-string 
4467                                             (setq none (1+ none))))))
4468                            dependencies)))
4469                    (setcar (symbol-value dep) header)
4470                  (set dep (list header)))
4471                (if (boundp (setq dep (intern (or ref "none") dependencies)))
4472                    (setcdr (symbol-value dep) 
4473                            (cons header (cdr (symbol-value dep))))
4474                  (set dep (list nil header)))
4475                (setq headers (cons header headers))))
4476         (forward-line 1))
4477       (setq headers (nreverse headers))
4478       (setq gnus-newsgroup-dependencies dependencies)
4479       headers)))
4480
4481 (defun gnus-article-get-xrefs ()
4482   "Fill in the Xref value in `gnus-current-headers', if necessary.
4483 This is meant to be called in `gnus-article-internal-prepare-hook'."
4484   (or (not gnus-use-cross-reference)
4485       (let ((case-fold-search t)
4486             xref)
4487         (save-restriction
4488           (gnus-narrow-to-headers)
4489           (goto-char (point-min))
4490           (if (or (and (eq (downcase (following-char)) ?x)
4491                        (looking-at "Xref:"))
4492                   (search-forward "\nXref:" nil t))
4493               (progn
4494                 (goto-char (1+ (match-end 0)))
4495                 (setq xref (buffer-substring (point) 
4496                                              (progn (end-of-line) (point))))
4497                 (save-excursion
4498                   (set-buffer gnus-summary-buffer)
4499                   (header-set-xref gnus-current-headers xref))))))))
4500
4501 (defalias 'gnus-find-header-by-number 'gnus-get-header-by-number)
4502 (make-obsolete 'gnus-find-header-by-number 'gnus-get-header-by-number)
4503
4504 ;; Return a header specified by a NUMBER.
4505 (defun gnus-get-header-by-number (number)
4506   (or gnus-newsgroup-headers-hashtb-by-number
4507       (gnus-make-headers-hashtable-by-number))
4508   (gnus-gethash (int-to-string number)
4509                 gnus-newsgroup-headers-hashtb-by-number))
4510
4511 (defun gnus-make-headers-hashtable-by-number ()
4512   "Make hashtable for the variable gnus-newsgroup-headers by number."
4513   (let ((header nil)
4514         (headers gnus-newsgroup-headers))
4515     (setq gnus-newsgroup-headers-hashtb-by-number
4516           (gnus-make-hashtable (length headers)))
4517     (while headers
4518       (setq header (car headers))
4519       (gnus-sethash (int-to-string (header-number header))
4520                     header gnus-newsgroup-headers-hashtb-by-number)
4521       (setq headers (cdr headers))
4522       )))
4523
4524 (defun gnus-more-header-backward ()
4525   "Find new header backward."
4526   (let ((first (car (gnus-gethash gnus-newsgroup-name gnus-active-hashtb)))
4527         (artnum gnus-newsgroup-begin)
4528         (header nil))
4529     (while (and (not header)
4530                 (> artnum first))
4531       (setq artnum (1- artnum))
4532       (setq header (gnus-read-header artnum)))
4533     header))
4534
4535 (defun gnus-more-header-forward ()
4536   "Find new header forward."
4537   (let ((last (cdr (gnus-gethash gnus-newsgroup-name gnus-active-hashtb)))
4538         (artnum gnus-newsgroup-end)
4539         (header nil))
4540     (while (and (not header)
4541                 (< artnum last))
4542       (setq artnum (1+ artnum))
4543       (setq header (gnus-read-header artnum)))
4544     header))
4545
4546 (defun gnus-extend-newsgroup (header &optional backward)
4547   "Extend newsgroup selection with HEADER.
4548 Optional argument BACKWARD means extend toward backward."
4549   (if header
4550       (let ((artnum (header-number header)))
4551         (setq gnus-newsgroup-headers
4552               (if backward
4553                   (cons header gnus-newsgroup-headers)
4554                 (nconc gnus-newsgroup-headers (list header))))
4555         (setq gnus-newsgroup-unselected
4556               (delq artnum gnus-newsgroup-unselected))
4557         (setq gnus-newsgroup-begin (min gnus-newsgroup-begin artnum))
4558         (setq gnus-newsgroup-end (max gnus-newsgroup-end artnum)))))
4559
4560
4561 (defun gnus-summary-search-group (&optional backward use-level)
4562   "Search for next unread newsgroup.
4563 If optional argument BACKWARD is non-nil, search backward instead."
4564   (save-excursion
4565     (set-buffer gnus-group-buffer)
4566     (save-excursion
4567       ;; We don't want to alter current point of Group mode buffer.
4568       (if (gnus-group-search-forward 
4569            backward nil 
4570            (if use-level (gnus-group-group-level) nil))
4571           (gnus-group-group-name))
4572       )))
4573
4574 (defun gnus-summary-search-subject (&optional backward unread subject)
4575   "Search for article forward.
4576 If BACKWARD is non-nil, search backward.
4577 If UNREAD is non-nil, only unread articles are selected.
4578 If SUBJECT is non-nil, the article which has the same subject will be
4579 searched for." 
4580   (let ((func
4581          (if backward
4582              (function re-search-backward) (function re-search-forward)))
4583         ;; We have to take care of hidden lines.
4584         (regexp 
4585          (if subject 
4586              (format "%s [-0-9]+ %s \\([-0-9 ]+\\) [0-9]+[\n\r]"
4587                      (regexp-quote (gnus-simplify-subject-re subject))
4588                      (if unread gnus-unread-mark "."))
4589            (if unread (concat "^[" gnus-unread-mark "]") "^."))))
4590     (if backward
4591         (beginning-of-line)
4592       (end-of-line))
4593     (prog1
4594         (if (funcall func regexp nil t)
4595             (progn
4596               (goto-char (match-beginning 0))
4597               (gnus-summary-article-number))
4598           nil)
4599       ;; Adjust cursor point.
4600       (gnus-summary-position-cursor))))
4601
4602 (defun gnus-summary-search-forward (&optional unread subject backward)
4603   "Search for article forward.
4604 If UNREAD is non-nil, only unread articles are selected.
4605 If SUBJECT is non-nil, the article which has the same subject will be
4606 searched for. 
4607 If BACKWARD is non-nil, the search will be performed backwards instead."
4608   (gnus-summary-search-subject backward unread subject))
4609
4610 (defun gnus-summary-search-backward (&optional unread subject)
4611   "Search for article backward.
4612 If 1st optional argument UNREAD is non-nil, only unread article is selected.
4613 If 2nd optional argument SUBJECT is non-nil, the article which has
4614 the same subject will be searched for."
4615   (gnus-summary-search-forward unread subject t))
4616
4617 (defun gnus-summary-article-number (&optional number-or-nil)
4618   "The article number of the article on the current line.
4619 If there isn's an article number here, then we return the current
4620 article number."
4621   (save-excursion
4622     (beginning-of-line)
4623     (if (re-search-forward " [-0-9]+ [0-9]+[\n\r]" nil t)
4624         (progn
4625           ;; jwz: this is faster than string-to-int/buffer-substring
4626           (goto-char (match-beginning 0))
4627           (read (current-buffer)))
4628         ;; We return the current if we couldn't find anything.
4629         (if number-or-nil nil gnus-current-article))))
4630
4631 (defun gnus-summary-thread-level ()
4632   "The thread level of the article on the current line."
4633   (save-excursion
4634     (beginning-of-line)
4635     (if (re-search-forward " [0-9]+[\n\r]" nil t)
4636         (progn
4637           (goto-char (match-beginning 0))
4638           (read (current-buffer)))
4639       ;; We return zero if we couldn't find anything.
4640       0)))
4641
4642 (defun gnus-summary-article-mark ()
4643   "The mark on the current line."
4644   (save-excursion
4645     (beginning-of-line)
4646     (if (re-search-forward ". [-0-9]+ [0-9]+[\n\r]" nil t)
4647         (char-after (match-beginning 0)))))
4648
4649 (defun gnus-summary-subject-string ()
4650   "Return current subject string or nil if nothing."
4651   (save-excursion
4652     (beginning-of-line)
4653     (if (re-search-forward " [-0-9]+ . [-0-9]+ [0-9]+[\n\r]" nil t)
4654         (let ((beg (previous-property-change (match-beginning 0)))
4655               (end (match-beginning 0))
4656               (buffer-read-only nil))
4657           (set-text-properties beg end nil)
4658           (prog1
4659               (buffer-substring beg end)
4660             (set-text-properties beg end '(invisible t))))
4661       nil)))
4662
4663 (defun gnus-summary-interest ()
4664   "Return current article interest."
4665   (save-excursion
4666     (beginning-of-line)
4667     (if (re-search-forward " [-0-9]+ . [-0-9]+ [0-9]+[\n\r]" nil t)
4668         (progn
4669           (goto-char (match-beginning 0))
4670           (read (current-buffer)))
4671       ;; We return zero if we couldn't find anything.
4672       0)))
4673
4674 (defun gnus-summary-recenter ()
4675   "Center point in Summary window."
4676   ;; Scroll window so as to cursor comes center of Summary window
4677   ;;  only when article is displayed.
4678   ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
4679   ;; Recenter only when requested.
4680   ;; Subbested by popovich@park.cs.columbia.edu
4681   (and gnus-auto-center-summary
4682        (get-buffer-window gnus-article-buffer)
4683        (< (/ (- (window-height) 1) 2)
4684           (count-lines (point) (point-max)))
4685        (recenter (/ (- (window-height) 2) 2))))
4686
4687 (defun gnus-summary-jump-to-group (newsgroup)
4688   "Move point to NEWSGROUP in Group mode buffer."
4689   ;; Keep update point of Group mode buffer if visible.
4690   (if (eq (current-buffer)
4691           (get-buffer gnus-group-buffer))
4692       (save-window-excursion
4693         ;; Take care of tree window mode.
4694         (if (get-buffer-window gnus-group-buffer)
4695             (pop-to-buffer gnus-group-buffer))
4696         (gnus-group-jump-to-group newsgroup))
4697     (save-excursion
4698       ;; Take care of tree window mode.
4699       (if (get-buffer-window gnus-group-buffer)
4700           (pop-to-buffer gnus-group-buffer)
4701         (set-buffer gnus-group-buffer))
4702       (gnus-group-jump-to-group newsgroup))))
4703
4704 ;; This function returns a list of article numbers based on the
4705 ;; difference between the ranges of read articles in this group and
4706 ;; the range of active articles.
4707 (defun gnus-list-of-unread-articles (group)
4708   (let* ((read (nth 2 (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
4709          (active (gnus-gethash group gnus-active-hashtb))
4710          (last (cdr active))
4711          unread first nlast unread)
4712     ;; If none are read, then all are unread. 
4713     (if (not read)
4714           (setq first (car active))
4715       ;; If the range of read articles is a single range, then the
4716       ;; first unread article is the article after the last read
4717       ;; article. Sounds logical, doesn't it?
4718       (if (atom (car read))
4719           (setq first (1+ (cdr read)))
4720         ;; `read' is a list of ranges.
4721         (while read
4722           (if first 
4723               (while (< first nlast)
4724                 (setq unread (cons first unread))
4725                 (setq first (1+ first))))
4726           (setq first (1+ (cdr (car read))))
4727           (setq nlast (car (car (cdr read))))
4728           (setq read (cdr read)))))
4729     ;; And add the last unread articles.
4730     (while (<= first last)
4731       (setq unread (cons first unread))
4732       (setq first (1+ first)))
4733     ;; Return the list of unread articles.
4734     (nreverse unread)))
4735
4736
4737 ;; Gnus Summary mode commands.
4738
4739 ;; Various summary commands
4740
4741 (defun gnus-summary-toggle-truncation (arg)
4742   "Toggle truncation of summary lines.
4743 With arg, turn line truncation on iff arg is positive."
4744   (interactive "P")
4745   (setq truncate-lines
4746         (if (null arg) (not truncate-lines)
4747           (> (prefix-numeric-value arg) 0)))
4748   (redraw-display))
4749
4750 (defun gnus-summary-reselect-current-group (show-all)
4751   "Once exit and then reselect the current newsgroup.
4752 Prefix argument SHOW-ALL means to select all articles."
4753   (interactive "P")
4754   (let ((current-subject (gnus-summary-article-number)))
4755     (gnus-summary-exit t)
4756     ;; We have to adjust the point of Group mode buffer because the
4757     ;; current point was moved to the next unread newsgroup by
4758     ;; exiting.
4759     (gnus-summary-jump-to-group gnus-newsgroup-name)
4760     (gnus-group-read-group show-all t)
4761     (gnus-summary-goto-subject current-subject)
4762     ))
4763
4764 (defun gnus-summary-rescan-group (all)
4765   "Exit the newsgroup, ask for new articles, and select the newsgroup."
4766   (interactive "P")
4767   ;; Fix by Ilja Weis <kult@uni-paderborn.de>.
4768   (let ((group gnus-newsgroup-name))
4769     (gnus-summary-exit t)
4770     (gnus-summary-jump-to-group group)
4771     (save-excursion
4772       (set-buffer gnus-group-buffer)
4773       (gnus-group-get-new-news-this-group 1))
4774     (gnus-summary-jump-to-group group)
4775     (gnus-group-read-group all)))
4776
4777 (defun gnus-summary-exit (&optional temporary)
4778   "Exit reading current newsgroup, and then return to group selection mode.
4779 gnus-exit-group-hook is called with no arguments if that value is non-nil."
4780   (interactive)
4781   (gnus-kill-save-kill-buffer)
4782   (let ((group gnus-newsgroup-name)
4783         (mode major-mode)
4784         (buf (current-buffer)))
4785     (let ((updated nil)
4786           (headers gnus-newsgroup-headers)
4787           (unreads gnus-newsgroup-unreads)
4788           (unselected (setq gnus-newsgroup-unselected
4789                             (sort gnus-newsgroup-unselected '<)))
4790           (ticked gnus-newsgroup-marked))
4791       ;; Important internal variables are saved, so we can reenter
4792       ;; the Summary buffer even if the hook changes them.
4793       (run-hooks 'gnus-exit-group-hook)
4794       (gnus-update-read-articles group unreads unselected ticked
4795                                  t gnus-newsgroup-replied
4796                                  gnus-newsgroup-expirable
4797                                  gnus-newsgroup-killed
4798                                  gnus-newsgroup-dormant
4799                                  gnus-newsgroup-bookmarks)
4800       ;; t means ignore unsubscribed newsgroups.
4801       (if gnus-use-cross-reference
4802           (gnus-mark-xrefs-as-read group headers unreads))
4803       ;; Do not switch windows but change the buffer to work.
4804       (set-buffer gnus-group-buffer)
4805       (gnus-group-update-group group))
4806     ;; Make sure where I was, and go to next newsgroup.
4807     (gnus-group-jump-to-group group)
4808 ;    (gnus-group-next-unread-group 1)
4809     (if temporary
4810         ;; If exiting temporary, caller should adjust Group mode
4811         ;; buffer point by itself.
4812         nil                             ;Nothing to do.
4813       ;; Return to Group mode buffer.
4814       (if (and (get-buffer buf) 
4815                (eq mode 'gnus-summary-mode))
4816           (kill-buffer buf))
4817       (if (get-buffer gnus-article-buffer)
4818           (bury-buffer gnus-article-buffer))
4819       (setq gnus-current-select-method gnus-select-method)
4820       (gnus-configure-windows 'newsgroups t)
4821       (pop-to-buffer gnus-group-buffer))))
4822
4823 (defun gnus-summary-quit ()
4824   "Quit reading current newsgroup without updating read article info."
4825   (interactive)
4826   (if (y-or-n-p "Do you really wanna quit reading this group? ")
4827       (progn
4828         (message "")                    ;Erase "Yes or No" question.
4829         ;; Return to Group selection mode.
4830         (if (get-buffer gnus-summary-buffer)
4831             (bury-buffer gnus-summary-buffer))
4832         (if (get-buffer gnus-article-buffer)
4833             (bury-buffer gnus-article-buffer))
4834         (gnus-configure-windows 'newsgroups)
4835         (pop-to-buffer gnus-group-buffer)
4836         (gnus-group-jump-to-group gnus-newsgroup-name) ;Make sure where I was.
4837         (gnus-group-next-group 1))))
4838
4839 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
4840 (defun gnus-summary-describe-group ()
4841   "Describe the current newsgroup."
4842   (interactive)
4843   (gnus-group-describe-group gnus-newsgroup-name))
4844
4845 (defun gnus-summary-describe-briefly ()
4846   "Describe Summary mode commands briefly."
4847   (interactive)
4848   (message
4849     (substitute-command-keys "\\[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")))
4850
4851 ;; Walking around Group mode buffer from Summary mode.
4852
4853 (defun gnus-summary-next-group (&optional no-article group)
4854   "Exit current newsgroup and then select next unread newsgroup.
4855 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
4856   (interactive "P")
4857   (let ((ingroup gnus-newsgroup-name))
4858     (gnus-summary-exit t)               ;Update all information.
4859     (gnus-group-jump-to-group ingroup)
4860     (let ((group (or group (gnus-summary-search-group)))
4861           (buf gnus-summary-buffer))
4862       (if (null group)
4863           (gnus-summary-quit)
4864         (message "Selecting %s..." group)
4865         ;; We are now in Group mode buffer.
4866         ;; Make sure Group mode buffer point is on GROUP.
4867         (gnus-group-jump-to-group group)
4868         (gnus-summary-read-group group nil no-article buf)))))
4869
4870 (defun gnus-summary-prev-group (no-article)
4871   "Exit current newsgroup and then select previous unread newsgroup.
4872 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
4873   (interactive "P")
4874   ;; Make sure Group mode buffer point is on current newsgroup.
4875   (gnus-summary-jump-to-group gnus-newsgroup-name)
4876   (let ((group (gnus-summary-search-group t)))
4877     (if (null group)
4878         (progn
4879           (message "Exiting %s..." gnus-newsgroup-name)  
4880           (gnus-summary-exit)
4881           (message ""))
4882       (message "Selecting %s..." group)
4883       (gnus-summary-exit t)             ;Exit Summary mode temporary.
4884       ;; We are now in Group mode buffer.
4885       ;; We have to adjust point of Group mode buffer because current
4886       ;; point is moved to next unread newsgroup by exiting.
4887       (gnus-summary-jump-to-group group)
4888       (gnus-summary-read-group group nil no-article)
4889       (or (eq (current-buffer)
4890               (get-buffer gnus-summary-buffer))
4891           (eq gnus-auto-select-next t)
4892           ;; Expected newsgroup has nothing to read since the articles
4893           ;; are marked as read by cross-referencing. So, try next
4894           ;; newsgroup. (Make sure we are in Group mode buffer now.)
4895           (and (eq (current-buffer)
4896                    (get-buffer gnus-group-buffer))
4897                (gnus-summary-search-group t)
4898                (gnus-summary-read-group
4899                 (gnus-summary-search-group t) nil no-article))
4900           )
4901       )))
4902
4903 ;; Walking around summary lines.
4904
4905 (defun gnus-summary-first-subject (unread)
4906   "Go to the first unread subject.
4907 If UNREAD is non-nil, go to the first unread article.
4908 Returns nil if there are no unread articles."
4909   (let ((begin (point)))
4910     (goto-char 1)
4911     (if (re-search-forward 
4912          (concat (if unread " " ".") " [-0-9]+ [0-9]+[\n\r]") nil t)
4913         (progn
4914           (forward-char -1)
4915           ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
4916           (gnus-summary-position-cursor)
4917           t)
4918       ;; If there is no unread articles, stay where you are.
4919       (goto-char begin)
4920       (message "No more unread articles")
4921       nil)))
4922
4923 (defun gnus-summary-next-subject (n &optional unread)
4924   "Go to next N'th summary line.
4925 If N is negative, go to the previous N'th subject line.
4926 If UNREAD is non-nil, only unread articles are selected.
4927 The difference between N and the actual number of steps taken is
4928 returned."
4929   (interactive "p")
4930   (let ((backward (< n 0))
4931         (n (abs n)))
4932   (while (and (> n 0)
4933               (gnus-summary-search-forward unread nil backward))
4934     (setq n (1- n)))
4935   (gnus-summary-recenter)
4936   (if (/= 0 n) (message "No more%s articles" (if unread " unread" "")))
4937 ;  (gnus-summary-position-cursor)
4938  n))
4939
4940 (defun gnus-summary-next-unread-subject (n)
4941   "Go to next N'th unread summary line."
4942   (interactive "p")
4943   (gnus-summary-next-subject n t))
4944
4945 (defun gnus-summary-prev-subject (n &optional unread)
4946   "Go to previous N'th summary line.
4947 If optional argument UNREAD is non-nil, only unread article is selected."
4948   (interactive "p")
4949   (gnus-summary-next-subject (- n) unread))
4950
4951 (defun gnus-summary-prev-unread-subject (n)
4952   "Go to previous N'th unread summary line."
4953   (interactive "p")
4954   (gnus-summary-next-subject (- n) t))
4955
4956 (defun gnus-summary-goto-subject (article)
4957   "Go the subject line of ARTICLE."
4958   (interactive
4959    (list
4960     (string-to-int
4961      (completing-read "Article number: "
4962                       (mapcar
4963                        (lambda (headers)
4964                          (list
4965                           (int-to-string (header-number headers))))
4966                        gnus-newsgroup-headers)
4967                       nil 'require-match))))
4968   (if (not article)
4969       (error "No article number"))
4970   (if (or (eq article (gnus-summary-article-number t))
4971           (let ((org (point)))
4972             (goto-char 1)
4973             (if (re-search-forward 
4974                  (format "[^Z] %d [0-9]+[\n\r]" article) nil t)
4975                 (goto-char (match-beginning 0))
4976               (goto-char org)
4977               nil)))
4978       (progn
4979         (gnus-summary-position-cursor)
4980         article)))
4981
4982 ;; Walking around summary lines with displaying articles.
4983
4984 (defun gnus-summary-expand-window ()
4985   "Expand Summary window to show headers full window."
4986   (interactive)
4987   (gnus-configure-windows 'summary)
4988   (pop-to-buffer gnus-summary-buffer))
4989
4990 (defun gnus-summary-display-article (article &optional all-header)
4991   "Display ARTICLE in Article buffer."
4992   (setq gnus-summary-buffer (current-buffer))
4993   (if (null article)
4994       nil
4995     (gnus-configure-windows 'article)
4996     (pop-to-buffer gnus-summary-buffer)
4997     (gnus-article-prepare article all-header)
4998     (if (= (gnus-summary-article-mark) ?Z) 
4999         (progn
5000           (forward-line 1)
5001           (gnus-summary-position-cursor)))
5002     (gnus-summary-recenter)
5003     (gnus-set-mode-line 'summary)
5004     (run-hooks 'gnus-select-article-hook)
5005     ;; Successfully display article.
5006     t))
5007
5008 (defun gnus-summary-select-article (&optional all-headers force)
5009   "Select the current article.
5010 Optional first argument ALL-HEADERS is non-nil, show all header fields.
5011 Optional second argument FORCE is nil, the article is only selected
5012 again when current header does not match with ALL-HEADERS option."
5013   (let ((article (gnus-summary-article-number))
5014         (all-headers (not (not all-headers)))) ;Must be T or NIL.
5015     (if (or (null gnus-current-article)
5016             (null gnus-article-current)
5017             (/= article (cdr gnus-article-current))
5018             (not (equal (car gnus-article-current) gnus-newsgroup-name))
5019             force)
5020         ;; The requested article is different from the current article.
5021         (gnus-summary-display-article article all-headers)
5022       (if all-headers
5023           (gnus-article-show-all-headers))
5024       (gnus-configure-windows 'article)
5025       (pop-to-buffer gnus-summary-buffer))))
5026
5027 (defun gnus-summary-set-current-mark (&optional current-mark)
5028   "Obsolete function."
5029   nil)
5030
5031 (defun gnus-summary-next-article (unread &optional subject)
5032   "Select the article after the current one.
5033 If UNREAD is non-nil, only unread articles are selected."
5034   (interactive "P")
5035   (let ((header nil))
5036     (cond ((gnus-summary-display-article
5037             (gnus-summary-search-forward unread subject)))
5038           ((and subject
5039                 gnus-auto-select-same
5040                 (gnus-set-difference gnus-newsgroup-unreads
5041                                      (append gnus-newsgroup-marked
5042                                              gnus-newsgroup-dormant))
5043                 (memq this-command
5044                       '(gnus-summary-next-unread-article
5045                         gnus-summary-next-page
5046                         gnus-summary-kill-same-subject-and-select
5047                         ;;gnus-summary-next-article
5048                         ;;gnus-summary-next-same-subject
5049                         ;;gnus-summary-next-unread-same-subject
5050                         )))
5051            ;; Wrap article pointer if there are unread articles.
5052            ;; Hook function, such as gnus-summary-rmail-digest, may
5053            ;; change current buffer, so need check.
5054            (let ((buffer (current-buffer))
5055                  (last-point (point)))
5056              ;; No more articles with same subject, so jump to the first
5057              ;; unread article.
5058              (gnus-summary-first-unread-article)
5059              ;;(and (eq buffer (current-buffer))
5060              ;; (= (point) last-point)
5061              ;; ;; Ignore given SUBJECT, and try again.
5062              ;; (gnus-summary-next-article unread nil))
5063              (and (eq buffer (current-buffer))
5064                   (< (point) last-point)
5065                   (message "Wrapped"))
5066              ))
5067           ((and gnus-auto-extend-newsgroup
5068                 (not unread)            ;Not unread only
5069                 (not subject)           ;Only if subject is not specified.
5070                 (setq header (gnus-more-header-forward)))
5071            ;; Extend to next article if possible.
5072            ;; Basic ideas by himacdonald@watdragon.waterloo.edu
5073            (gnus-extend-newsgroup header nil)
5074            ;; Threads feature must be turned off.
5075            (let ((buffer-read-only nil))
5076              (goto-char (point-max))
5077              (gnus-summary-prepare-threads (list header) 0))
5078            (gnus-summary-goto-article gnus-newsgroup-end))
5079           (t
5080            ;; Select next newsgroup automatically if requested.
5081            (gnus-summary-jump-to-group gnus-newsgroup-name)
5082            (let ((cmd (aref (this-command-keys) 0))
5083                  (group (gnus-summary-search-group nil gnus-keep-same-level))
5084                  (auto-select
5085                   (and gnus-auto-select-next
5086                        ;;(null (gnus-set-difference gnus-newsgroup-unreads
5087                        ;;                               gnus-newsgroup-marked))
5088                        (memq this-command
5089                              '(gnus-summary-next-unread-article
5090                                gnus-summary-next-article
5091                                gnus-summary-next-page
5092                                gnus-summary-next-same-subject
5093                                gnus-summary-next-unread-same-subject
5094                                gnus-summary-kill-same-subject
5095                                gnus-summary-kill-same-subject-and-select
5096                                ))
5097                        ;; Ignore characters typed ahead.
5098                        (not (input-pending-p))
5099                        )))
5100              ;; Keep just the event type of CMD.
5101              (if (listp cmd)
5102                  (setq cmd (car cmd)))
5103              (message "No more%s articles%s"
5104                       (if unread " unread" "")
5105                       (if (and auto-select
5106                                (not (eq gnus-auto-select-next 'quietly)))
5107                           (if group
5108                               (format " (Type %s for %s [%s])"
5109                                       (single-key-description cmd)
5110                                       group
5111                                       (car (gnus-gethash 
5112                                             group gnus-newsrc-hashtb)))
5113                             (format " (Type %s to exit %s)"
5114                                     (single-key-description cmd)
5115                                     gnus-newsgroup-name))
5116                         ""))
5117              ;; Select next unread newsgroup automagically.
5118              (cond ((and auto-select
5119                          (eq gnus-auto-select-next 'quietly))
5120                     ;; Select quietly.
5121                     (gnus-summary-next-group))
5122                    (auto-select
5123                     ;; Confirm auto selection.
5124                     (let* ((event (read-event))
5125                            (type
5126                             (if (listp event)
5127                                 (car event)
5128                               event)))
5129                       (if (and (eq event type) (eq event cmd))
5130                           (gnus-summary-next-group)
5131                         (setq unread-command-events (list event)))))
5132                    )
5133              ))
5134           )))
5135
5136 (defun gnus-summary-next-unread-article ()
5137   "Select unread article after current one."
5138   (interactive)
5139   (gnus-summary-next-article t (and gnus-auto-select-same
5140                                     (gnus-summary-subject-string)))
5141   (gnus-summary-position-cursor))
5142
5143 (defun gnus-summary-prev-article (unread &optional subject)
5144   "Select article before current one.
5145 If argument UNREAD is non-nil, only unread article is selected."
5146   (interactive "P")
5147   (let ((header nil))
5148     (cond ((gnus-summary-display-article
5149             (gnus-summary-search-backward unread subject)))
5150           ((and subject
5151                 gnus-auto-select-same
5152                 (gnus-set-difference gnus-newsgroup-unreads
5153                                      (append gnus-newsgroup-marked
5154                                              gnus-newsgroup-dormant))
5155                 (memq this-command
5156                       '(gnus-summary-prev-unread-article
5157                         ;;gnus-summary-prev-page
5158                         ;;gnus-summary-prev-article
5159                         ;;gnus-summary-prev-same-subject
5160                         ;;gnus-summary-prev-unread-same-subject
5161                         )))
5162            ;; Ignore given SUBJECT, and try again.
5163            (gnus-summary-prev-article unread nil))
5164           (unread
5165            (message "No more unread articles"))
5166           ((and gnus-auto-extend-newsgroup
5167                 (not subject)           ;Only if subject is not specified.
5168                 (setq header (gnus-more-header-backward)))
5169            ;; Extend to previous article if possible.
5170            ;; Basic ideas by himacdonald@watdragon.waterloo.edu
5171            (gnus-extend-newsgroup header t)
5172            (let ((buffer-read-only nil))
5173              (goto-char (point-min))
5174              (gnus-summary-prepare-threads (list header) 0))
5175            (gnus-summary-goto-article gnus-newsgroup-begin)
5176            (gnus-summary-position-cursor))
5177           (t
5178            (message "No more articles"))
5179           )))
5180
5181 (defun gnus-summary-prev-unread-article ()
5182   "Select unred article before current one."
5183   (interactive)
5184   (gnus-summary-prev-article t (and gnus-auto-select-same
5185                                     (gnus-summary-subject-string))))
5186
5187 (defun gnus-summary-next-page (lines &optional circular)
5188   "Show next page of selected article.
5189 If end of article, select next article.
5190 Argument LINES specifies lines to be scrolled up.
5191 If CIRCULAR is non-nil, go to the start of the article instead of 
5192 instead of selecting the next article when reaching the end of the
5193 current article." 
5194   (interactive "P")
5195   (setq gnus-summary-buffer (current-buffer))
5196   (let ((article (gnus-summary-article-number))
5197         (endp nil))
5198     (if (or (null gnus-current-article)
5199             (null gnus-article-current)
5200             (/= article (cdr gnus-article-current))
5201             (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5202         ;; Selected subject is different from current article's.
5203           (gnus-summary-display-article article)
5204       (gnus-configure-windows 'article)
5205       (pop-to-buffer gnus-summary-buffer)
5206       (gnus-eval-in-buffer-window gnus-article-buffer
5207         (setq endp (gnus-article-next-page lines)))
5208       (if endp
5209           (cond (circular
5210                  (gnus-summary-beginning-of-article))
5211                 (lines
5212                  (message "End of message"))
5213                 ((null lines)
5214                  (gnus-summary-next-unread-article))))
5215       (gnus-summary-position-cursor))))
5216
5217 (defun gnus-summary-prev-page (lines)
5218   "Show previous page of selected article.
5219 Argument LINES specifies lines to be scrolled down."
5220   (interactive "P")
5221   (let ((article (gnus-summary-article-number)))
5222     (if (or (null gnus-current-article)
5223             (null gnus-article-current)
5224             (/= article (cdr gnus-article-current))
5225             (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5226         ;; Selected subject is different from current article's.
5227         (gnus-summary-display-article article)
5228       (gnus-configure-windows 'article)
5229       (pop-to-buffer gnus-summary-buffer)
5230       (gnus-eval-in-buffer-window gnus-article-buffer
5231         (gnus-article-prev-page lines))
5232       (gnus-summary-position-cursor))))
5233
5234 (defun gnus-summary-scroll-up (lines)
5235   "Scroll up (or down) one line current article.
5236 Argument LINES specifies lines to be scrolled up (or down if negative)."
5237   (interactive "p")
5238   (gnus-summary-select-article)
5239   (gnus-eval-in-buffer-window gnus-article-buffer
5240     (cond ((> lines 0)
5241            (if (gnus-article-next-page lines)
5242                (message "End of message")))
5243           ((< lines 0)
5244            (gnus-article-prev-page (- 0 lines))))
5245     ))
5246
5247 (defun gnus-summary-next-same-subject ()
5248   "Select next article which has the same subject as current one."
5249   (interactive)
5250   (gnus-summary-next-article nil (gnus-summary-subject-string)))
5251
5252 (defun gnus-summary-prev-same-subject ()
5253   "Select previous article which has the same subject as current one."
5254   (interactive)
5255   (gnus-summary-prev-article nil (gnus-summary-subject-string)))
5256
5257 (defun gnus-summary-next-unread-same-subject ()
5258   "Select next unread article which has the same subject as current one."
5259   (interactive)
5260   (gnus-summary-next-article t (gnus-summary-subject-string)))
5261
5262 (defun gnus-summary-prev-unread-same-subject ()
5263   "Select previous unread article which has the same subject as current one."
5264   (interactive)
5265   (gnus-summary-prev-article t (gnus-summary-subject-string)))
5266
5267 (defun gnus-summary-first-unread-article ()
5268   "Select the first unread article. 
5269 Return nil if there are no unread articles."
5270   (interactive)
5271   (if (gnus-summary-first-subject t)
5272       (gnus-summary-display-article (gnus-summary-article-number))))
5273
5274 (defun gnus-summary-goto-article (article &optional all-headers)
5275   "Fetch ARTICLE and display it if it exists.
5276 If ALL-HEADERS is non-nil, no header lines are hidden."
5277   (interactive
5278    (list
5279     (string-to-int
5280      (completing-read "Article number: "
5281                       (mapcar
5282                        (lambda (headers)
5283                          (list
5284                           (int-to-string (header-number headers))))
5285                        gnus-newsgroup-headers)
5286                       nil 'require-match))))
5287   (if (gnus-summary-goto-subject article)
5288       (gnus-summary-display-article article all-headers)))
5289
5290 (defun gnus-summary-goto-last-article ()
5291   "Go to last subject line."
5292   (interactive)
5293   (if gnus-last-article
5294       (gnus-summary-goto-article gnus-last-article)))
5295
5296
5297 ;; Summary article oriented commands
5298
5299 (defun gnus-summary-refer-parent-article ()
5300   "Refer parent article of current article."
5301   (interactive)
5302   (let ((ref (header-references (gnus-get-header-by-number
5303                                  (gnus-summary-article-number))))
5304         parent)
5305     (if (or (not ref) (equal ref ""))
5306         (error "No references in this article"))
5307     (and (string-match "<[^<>]*>[ \t]*$" ref)
5308          (setq parent 
5309                (substring ref (match-beginning 0) (match-end 0))))
5310     (if (stringp parent)
5311         (gnus-summary-refer-article parent)
5312       (error "Possibly malformed references"))))
5313
5314 (defun gnus-summary-refer-article (message-id)
5315   "Refer article specified by MESSAGE-ID.
5316 NOTE: This command only works with newsgroup that use NNTP."
5317   (interactive "sMessage-ID: ")
5318   ;; Make sure that this command depends on the fact that article
5319   ;; related information is not updated when an article is retrieved
5320   ;; by Message-ID.
5321   (gnus-summary-select-article t)       ;Request all headers.
5322   (if (and (stringp message-id)
5323            (> (length message-id) 0))
5324       (let ((current (header-id gnus-current-headers)))
5325         (gnus-eval-in-buffer-window 
5326          gnus-article-buffer
5327          ;; Construct the correct Message-ID if necessary.
5328          ;; Suggested by tale@pawl.rpi.edu.
5329          (or (string-match "^<" message-id)
5330              (setq message-id (concat "<" message-id)))
5331          (or (string-match ">$" message-id)
5332              (setq message-id (concat message-id ">"))))))
5333   (if (and (stringp message-id)
5334            (gnus-article-prepare message-id nil (gnus-read-header message-id)))
5335       (progn
5336         (gnus-summary-insert-line 
5337          nil gnus-current-headers 0 nil ?D nil nil 
5338          (header-subject gnus-current-headers))
5339         (forward-line -1)
5340         (gnus-summary-position-cursor)
5341         (run-hooks 'gnus-summary-update-hook)
5342         message-id)
5343     (error "No such references")))
5344
5345 (defun gnus-summary-next-digest (nth)
5346   "Move to head of NTH next digested message."
5347   (interactive "p")
5348   (gnus-summary-select-article)
5349   (gnus-eval-in-buffer-window gnus-article-buffer
5350     (gnus-article-next-digest (or nth 1))
5351     ))
5352
5353 (defun gnus-summary-prev-digest (nth)
5354   "Move to head of NTH previous digested message."
5355   (interactive "p")
5356   (gnus-summary-select-article)
5357   (gnus-eval-in-buffer-window gnus-article-buffer
5358     (gnus-article-prev-digest (or nth 1))
5359     ))
5360
5361 (defun gnus-summary-rmail-digest ()
5362   "Run RMAIL on current digest article.
5363 gnus-select-digest-hook will be called with no arguments, if that
5364 value is non-nil. It is possible to modify the article so that Rmail
5365 can work with it.
5366 gnus-rmail-digest-hook will be called with no arguments, if that value
5367 is non-nil. The hook is intended to customize Rmail mode."
5368   (interactive)
5369   (gnus-summary-select-article)
5370   (require 'rmail)
5371   (let ((artbuf gnus-article-buffer)
5372         (digbuf (get-buffer-create gnus-digest-buffer))
5373         (mail-header-separator ""))
5374     (set-buffer digbuf)
5375     (gnus-add-current-to-buffer-list)
5376     (buffer-disable-undo (current-buffer))
5377     (setq buffer-read-only nil)
5378     (erase-buffer)
5379     (insert-buffer-substring artbuf)
5380     (run-hooks 'gnus-select-digest-hook)
5381     (gnus-convert-article-to-rmail)
5382     (goto-char (point-min))
5383     ;; Rmail initializations.
5384     (rmail-insert-rmail-file-header)
5385     (rmail-mode)
5386     (rmail-set-message-counters)
5387     (rmail-show-message)
5388     (condition-case ()
5389         (progn
5390           (undigestify-rmail-message)
5391           (rmail-expunge)               ;Delete original message.
5392           ;; File name is meaningless but `save-buffer' requires it.
5393           (setq buffer-file-name "Gnus Digest")
5394           (setq mode-line-buffer-identification
5395                 (concat "Digest: "
5396                         (header-subject gnus-current-headers)))
5397           ;; There is no need to write this buffer to a file.
5398           (make-local-variable 'write-file-hooks)
5399           (setq write-file-hooks
5400                 (list (lambda ()
5401                         (set-buffer-modified-p nil)
5402                         (message "(No changes need to be saved)")
5403                         'no-need-to-write-this-buffer)))
5404           ;; Default file name saving digest messages.
5405           (setq rmail-default-rmail-file
5406                 (funcall gnus-rmail-save-name gnus-newsgroup-name
5407                          gnus-current-headers gnus-newsgroup-last-rmail))
5408           (setq rmail-default-file
5409                 (funcall gnus-mail-save-name gnus-newsgroup-name
5410                          gnus-current-headers gnus-newsgroup-last-mail))
5411           ;; Prevent generating new buffer named ***<N> each time.
5412           (setq rmail-summary-buffer
5413                 (get-buffer-create gnus-digest-summary-buffer))
5414           (run-hooks 'gnus-rmail-digest-hook)
5415           ;; Take all windows safely.
5416           (gnus-configure-windows '(1 0 0))
5417           (pop-to-buffer gnus-group-buffer)
5418           ;; Use Summary Article windows for Digest summary and
5419           ;; Digest buffers.
5420           (if gnus-digest-show-summary
5421               (let ((gnus-summary-buffer gnus-digest-summary-buffer)
5422                     (gnus-article-buffer gnus-digest-buffer))
5423                 (gnus-configure-windows 'article)
5424                 (pop-to-buffer gnus-digest-buffer)
5425                 (rmail-summary)
5426                 (pop-to-buffer gnus-digest-summary-buffer)
5427                 (message (substitute-command-keys
5428                           "Type \\[rmail-summary-quit] to return to Gnus")))
5429             (let ((gnus-summary-buffer gnus-digest-buffer))
5430               (gnus-configure-windows 'summary)
5431               (pop-to-buffer gnus-digest-buffer)
5432               (message (substitute-command-keys
5433                         "Type \\[rmail-quit] to return to Gnus")))
5434             )
5435           ;; Move the buffers to the end of buffer list.
5436           (bury-buffer gnus-article-buffer)
5437           (bury-buffer gnus-group-buffer)
5438           (bury-buffer gnus-digest-summary-buffer)
5439           (bury-buffer gnus-digest-buffer))
5440       (error (set-buffer-modified-p nil)
5441              (kill-buffer digbuf)
5442              ;; This command should not signal an error because the
5443              ;; command is called from hooks.
5444              (ding) (message "Article is not a digest")))
5445     ))
5446
5447 (defun gnus-summary-isearch-article ()
5448   "Do incremental search forward on current article."
5449   (interactive)
5450   (gnus-summary-select-article)
5451   (gnus-eval-in-buffer-window gnus-article-buffer
5452                               (isearch-forward)))
5453
5454 (defun gnus-summary-search-article-forward (regexp)
5455   "Search for an article containing REGEXP forward.
5456 gnus-select-article-hook is not called during the search."
5457   (interactive
5458    (list (read-string
5459           (concat "Search forward (regexp): "
5460                   (if gnus-last-search-regexp
5461                       (concat "(default " gnus-last-search-regexp ") "))))))
5462   (if (string-equal regexp "")
5463       (setq regexp (or gnus-last-search-regexp ""))
5464     (setq gnus-last-search-regexp regexp))
5465   (if (gnus-summary-search-article regexp nil)
5466       (gnus-eval-in-buffer-window gnus-article-buffer
5467         (recenter 0)
5468         ;;(sit-for 1)
5469         )
5470     (error "Search failed: \"%s\"" regexp)
5471     ))
5472
5473 (defun gnus-summary-search-article-backward (regexp)
5474   "Search for an article containing REGEXP backward.
5475 gnus-select-article-hook is not called during the search."
5476   (interactive
5477    (list (read-string
5478           (concat "Search backward (regexp): "
5479                   (if gnus-last-search-regexp
5480                       (concat "(default " gnus-last-search-regexp ") "))))))
5481   (if (string-equal regexp "")
5482       (setq regexp (or gnus-last-search-regexp ""))
5483     (setq gnus-last-search-regexp regexp))
5484   (if (gnus-summary-search-article regexp t)
5485       (gnus-eval-in-buffer-window gnus-article-buffer
5486         (recenter 0)
5487         ;;(sit-for 1)
5488         )
5489     (error "Search failed: \"%s\"" regexp)
5490     ))
5491
5492 (defun gnus-summary-search-article (regexp &optional backward)
5493   "Search for an article containing REGEXP.
5494 Optional argument BACKWARD means do search for backward.
5495 gnus-select-article-hook is not called during the search."
5496   (let ((gnus-select-article-hook nil)  ;Disable hook.
5497         (gnus-mark-article-hook nil)    ;Inhibit marking as read.
5498         (re-search
5499          (if backward
5500              (function re-search-backward) (function re-search-forward)))
5501         (found nil)
5502         (last nil))
5503     ;; Hidden thread subtrees must be searched for ,too.
5504     (gnus-summary-show-all-threads)
5505     ;; First of all, search current article.
5506     ;; We don't want to read article again from NNTP server nor reset
5507     ;; current point.
5508     (gnus-summary-select-article)
5509     (message "Searching article: %d..." gnus-current-article)
5510     (setq last gnus-current-article)
5511     (gnus-eval-in-buffer-window gnus-article-buffer
5512       (save-restriction
5513         (widen)
5514         ;; Begin search from current point.
5515         (setq found (funcall re-search regexp nil t))))
5516     ;; Then search next articles.
5517     (while (and (not found)
5518                 (gnus-summary-display-article 
5519                  (gnus-summary-search-subject backward nil nil)))
5520       (message "Searching article: %d..." gnus-current-article)
5521       (gnus-eval-in-buffer-window gnus-article-buffer
5522         (save-restriction
5523           (widen)
5524           (goto-char (if backward (point-max) (point-min)))
5525           (setq found (funcall re-search regexp nil t)))
5526         ))
5527     (message "")
5528     ;; Adjust article pointer.
5529     (or (eq last gnus-current-article)
5530         (setq gnus-last-article last))
5531     ;; Return T if found such article.
5532     found
5533     ))
5534
5535 (defun gnus-summary-execute-command (field regexp command &optional backward)
5536   "If FIELD of article header matches REGEXP, execute a COMMAND string.
5537 If FIELD is an empty string (or nil), entire article body is searched for.
5538 If optional (prefix) argument BACKWARD is non-nil, do backward instead."
5539   (interactive
5540    (list (let ((completion-ignore-case t))
5541            (completing-read "Field name: "
5542                             '(("Number")("Subject")("From")
5543                               ("Lines")("Date")("Id")
5544                               ("Xref")("References"))
5545                             nil 'require-match))
5546          (read-string "Regexp: ")
5547          (read-key-sequence "Command: ")
5548          current-prefix-arg))
5549   ;; Hidden thread subtrees must be searched for ,too.
5550   (gnus-summary-show-all-threads)
5551   ;; We don't want to change current point nor window configuration.
5552   (save-excursion
5553     (save-window-excursion
5554       (message "Executing %s..." (key-description command))
5555       ;; We'd like to execute COMMAND interactively so as to give arguments.
5556       (gnus-execute field regexp
5557                     (` (lambda ()
5558                          (call-interactively '(, (key-binding command)))))
5559                     backward)
5560       (message "Executing %s... done" (key-description command)))))
5561
5562 (defun gnus-summary-beginning-of-article ()
5563   "Scroll the article back to the beginning."
5564   (interactive)
5565   (gnus-summary-select-article)
5566   (gnus-eval-in-buffer-window gnus-article-buffer
5567     (widen)
5568     (goto-char (point-min))
5569     (if gnus-break-pages
5570         (gnus-narrow-to-page))
5571     ))
5572
5573 (defun gnus-summary-end-of-article ()
5574   "Scroll to the end of the article."
5575   (interactive)
5576   (gnus-summary-select-article)
5577   (gnus-eval-in-buffer-window gnus-article-buffer
5578     (widen)
5579     (goto-char (point-max))
5580     (if gnus-break-pages
5581         (gnus-narrow-to-page))
5582     ))
5583
5584 (defun gnus-summary-show-article ()
5585   "Force re-fetching of the current article."
5586   (interactive)
5587   (gnus-summary-select-article gnus-have-all-headers t))
5588
5589 (defun gnus-summary-toggle-header (arg)
5590   "Show the headers if they are hidden, or hide them if they are shown.
5591 If ARG is a positive number, show the entire header.
5592 If ARG is a negative number, hide the unwanted header lines."
5593   (interactive "P")
5594   (save-excursion
5595     (set-buffer gnus-article-buffer)
5596     (let ((buffer-read-only nil))
5597       (if (numberp arg) 
5598           (if (> arg 0) (remove-text-properties 1 (point-max) '(invisible t))
5599             (if (< arg 0) (run-hooks 'gnus-article-display-hook)))
5600         (if (text-property-any 1 (point-max) 'invisible t)
5601             (remove-text-properties 1 (point-max) '(invisible t))
5602           (run-hooks 'gnus-article-display-hook))))))
5603
5604 (defun gnus-summary-show-all-headers ()
5605   "Make all header lines visible."
5606   (interactive)
5607   (gnus-article-show-all-headers))
5608
5609 (defun gnus-summary-toggle-mime (arg)
5610   "Toggle MIME processing.
5611 If ARG is a positive number, turn MIME processing on."
5612   (interactive "P")
5613   (setq gnus-show-mime
5614         (if (null arg) (not gnus-show-mime)
5615           (> (prefix-numeric-value arg) 0)))
5616   (gnus-summary-select-article t 'force))
5617
5618 (defun gnus-summary-caesar-message (rotnum)
5619   "Caesar rotates all letters of current message by 13/47 places.
5620 With prefix arg, specifies the number of places to rotate each letter forward.
5621 Caesar rotates Japanese letters by 47 places in any case."
5622   (interactive "P")
5623   (gnus-summary-select-article)
5624   (gnus-overload-functions)
5625   (gnus-eval-in-buffer-window gnus-article-buffer
5626     (save-restriction
5627       (widen)
5628       ;; We don't want to jump to the beginning of the message.
5629       ;; `save-excursion' does not do its job.
5630       (move-to-window-line 0)
5631       (let ((last (point)))
5632         (news-caesar-buffer-body rotnum)
5633         (goto-char last)
5634         (recenter 0)
5635         ))
5636     ))
5637
5638 (defun gnus-summary-stop-page-breaking ()
5639   "Stop page breaking in the current article."
5640   (interactive)
5641   (gnus-summary-select-article)
5642   (gnus-eval-in-buffer-window gnus-article-buffer (widen)))
5643
5644 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
5645
5646 (defun gnus-summary-move-article (n &optional to-newsgroup select-method)
5647   "Move the current article to a different newsgroup.
5648 If N is a positive number, move the N next articles.
5649 If N is a negative number, move the N previous articles.
5650 If N is nil and any articles have been marked with the process mark,
5651 move those articles instead.
5652 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to. 
5653 If SELECT-METHOD is symbol, do not move to a specific newsgroup, but
5654 re-spool using this method.
5655 For this function to work, both the current newsgroup and the
5656 newsgroup that you want to move to have to support the `request-move'
5657 and `request-accept' functions. (Ie. mail newsgroups at present.)"
5658   (interactive "P")
5659   (or (gnus-check-backend-function 'request-move-article gnus-newsgroup-name)
5660       (error "The current newsgroup does not support article moving"))
5661   (let (articles art-group)
5662     (if (and n (numberp n))
5663         (let ((backward (< n 0))
5664               (n (abs n)))
5665           (save-excursion
5666             (while (and (> n 0)
5667                         (setq articles (cons (gnus-summary-article-number) 
5668                                              articles))
5669                         (gnus-summary-search-forward nil nil backward))
5670               (setq n (1- n))))
5671           (setq articles (sort articles (function <))))
5672       (setq articles (or (setq gnus-newsgroup-processable
5673                                (sort gnus-newsgroup-processable (function <)))
5674                          (list (gnus-summary-article-number)))))
5675     (if (and (not to-newsgroup) (not select-method))
5676         (setq to-newsgroup
5677               (completing-read 
5678                (format "Where do you want to move %s? "
5679                        (if (> (length articles) 1)
5680                            (format "these %d articles" (length articles))
5681                          "this article"))
5682                gnus-active-hashtb nil t)))
5683     (or (gnus-check-backend-function 'request-accept-article 
5684                                      (or select-method to-newsgroup))
5685         (error "%s does not support article moving" to-newsgroup))
5686     (message "Moving to %s: %s..." (or select-method to-newsgroup) articles)
5687     (while articles
5688       (if (setq art-group
5689                 (gnus-request-move-article 
5690                  (car articles)
5691                  gnus-newsgroup-name (nth 1 gnus-current-select-method)
5692                  (list 'gnus-request-accept-article 
5693                        (or select-method to-newsgroup))))
5694           (let* ((buffer-read-only nil)
5695                  (entry (or
5696                          (gnus-gethash (car art-group) gnus-newsrc-hashtb)
5697                          (gnus-gethash (concat gnus-foreign-group-prefix
5698                                                (car art-group) )
5699                                        gnus-newsrc-hashtb)))
5700                  (info (nth 2 entry))
5701                  (article (car articles))
5702                  (marked (nth 3 info)))
5703             (gnus-summary-goto-subject article)
5704             (delete-region (progn (beginning-of-line) (point))
5705                            (progn (forward-line 1) (point)))
5706             (if (not (memq article gnus-newsgroup-unreads))
5707                 (setcar (cdr (cdr info))
5708                         (gnus-add-to-range (nth 2 info) 
5709                                            (list (cdr art-group)))))
5710             ;; !!! Here one should copy all the marks over to the new
5711             ;; newsgroup, but I couldn't be bothered. nth on that!
5712             )
5713         (message "Couldn't move article %s" (car articles)))
5714       (setq articles (cdr articles)))))
5715
5716 (defun gnus-summary-respool-article (n &optional respool-method)
5717   "Respool the current article.
5718 The article will be squeezed through the mail spooling process again,
5719 which means that it will be put in some mail newsgroup or other
5720 depending on `nnmail-split-methods'.
5721 If N is a positive number, respool the N next articles.
5722 If N is a negative number, respool the N previous articles.
5723 If N is nil and any articles have been marked with the process mark,
5724 respool those articles instead.
5725 For this function to work, both the current newsgroup and the
5726 newsgroup that you want to move to have to support the `request-move'
5727 and `request-accept' functions. (Ie. mail newsgroups at present.)"
5728   (interactive "P")
5729   (or respool-method
5730       (setq respool-method
5731             (completing-read
5732              "What method do you want to use when respooling? "
5733              (gnus-methods-using 'respool) nil t)))
5734   (gnus-summary-move-article n nil respool-method))
5735
5736 ;; Summary interest commands.
5737
5738 ;; Suggested by boubaker@cenatls.cena.dgac.fr.
5739
5740 (defun gnus-summary-raise-interest (n)
5741   "Raise the interest of the current article by N."
5742   (interactive "p")
5743   (gnus-summary-set-interest (+ (gnus-summary-interest) n)))
5744
5745 (defun gnus-summary-lower-interest (n)
5746   "Lower the interest of the current article by N."
5747   (interactive "p")
5748   (gnus-summary-raise-interest (- n)))
5749
5750 (defun gnus-summary-set-interest (n)
5751   "Set the interest of the current article to N."
5752   (interactive "p")
5753   ;; Skip dummy header line.
5754   (save-excursion
5755     (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5756     (let ((buffer-read-only nil)
5757           beg)
5758       ;; Set visible interest.
5759       (beginning-of-line)
5760       (forward-char 3)
5761       (delete-char 1)
5762       (insert (int-to-string (max 1 (min 9 n))))
5763       ;; Set invisible interest.
5764       (re-search-forward " [-0-9]+ . [-0-9]+ [0-9]+[\n\r]")
5765       (goto-char (1+ (match-beginning 0)))
5766       (delete-region (setq beg (point)) (progn (search-forward " ") 
5767                                                (forward-char -1)
5768                                                (point)))
5769       (insert (int-to-string n))
5770       (set-text-properties beg (point) '(invisible t)))
5771     (run-hooks 'gnus-summary-update-hook)))
5772
5773 (defmacro gnus-raise (field expression level)
5774   (` (gnus-kill (, field) (, expression)
5775                 (function (gnus-summary-raise-interest (, level))) t)))
5776
5777 (defmacro gnus-lower (field expression level)
5778   (` (gnus-kill (, field) (, expression)
5779              (function (gnus-summary-raise-interest (- (, level)))) t)))
5780
5781 ;; Summary marking commands.
5782
5783 (defun gnus-summary-kill-same-subject-and-select (unmark)
5784   "Mark articles which has the same subject as read, and then select the next.
5785 If UNMARK is positive, remove any kind of mark.
5786 If UNMARK is negative, tick articles."
5787   (interactive "P")
5788   (if unmark
5789       (setq unmark (prefix-numeric-value unmark)))
5790   (let ((count
5791          (gnus-summary-mark-same-subject
5792           (gnus-summary-subject-string) unmark)))
5793     ;; Select next unread article. If auto-select-same mode, should
5794     ;; select the first unread article.
5795     (gnus-summary-next-article t (and gnus-auto-select-same
5796                                       (gnus-summary-subject-string)))
5797     (message "%d articles are marked as %s"
5798              count (if unmark "unread" "read"))
5799     ))
5800
5801 (defun gnus-summary-kill-same-subject (unmark)
5802   "Mark articles which has the same subject as read. 
5803 If UNMARK is positive, remove any kind of mark.
5804 If UNMARK is negative, tick articles."
5805   (interactive "P")
5806   (if unmark
5807       (setq unmark (prefix-numeric-value unmark)))
5808   (let ((count
5809          (gnus-summary-mark-same-subject
5810           (gnus-summary-subject-string) unmark)))
5811     ;; If marked as read, go to next unread subject.
5812     (if (null unmark)
5813         ;; Go to next unread subject.
5814         (gnus-summary-next-subject 1 t))
5815     (message "%d articles are marked as %s"
5816              count (if unmark "unread" "read"))
5817     ))
5818
5819 (defun gnus-summary-mark-same-subject (subject &optional unmark)
5820   "Mark articles with same SUBJECT as read, and return marked number.
5821 If optional argument UNMARK is positive, remove any kinds of marks.
5822 If optional argument UNMARK is negative, mark articles as unread instead."
5823   (let ((count 1))
5824     (save-excursion
5825       (cond ((null unmark)
5826              (gnus-summary-mark-as-read nil gnus-killed-mark))
5827             ((> unmark 0)
5828              (gnus-summary-tick-article nil t))
5829             (t
5830              (gnus-summary-tick-article)))
5831       (while (and subject
5832                   (gnus-summary-search-forward nil subject))
5833         (cond ((null unmark)
5834                (gnus-summary-mark-as-read nil gnus-killed-mark))
5835               ((> unmark 0)
5836                (gnus-summary-tick-article nil t))
5837               (t
5838                (gnus-summary-tick-article)))
5839         (setq count (1+ count))
5840         ))
5841     ;; Hide killed thread subtrees.  Does not work properly always.
5842     ;;(and (null unmark)
5843     ;;     gnus-thread-hide-killed
5844     ;;     (gnus-summary-hide-thread))
5845     ;; Return number of articles marked as read.
5846     count
5847     ))
5848
5849 (defun gnus-summary-mark-as-processable (n &optional unmark)
5850   "Set the process mark on the next N articles.
5851 If N is negative, mark backward instead.  If UNMARK is non-nil, remove
5852 the process mark instead.  The difference between N and the actual
5853 number of articles marked is returned."
5854   (interactive "p")
5855   (let ((backward (< n 0))
5856         (n (abs n)))
5857   (while (and (> n 0)
5858               (if unmark
5859                   (gnus-summary-remove-process-mark 
5860                    (gnus-summary-article-number))
5861                 (gnus-summary-set-process-mark 
5862                  (gnus-summary-article-number)))
5863               (= 0 (gnus-summary-next-subject (if backward -1 1))))
5864     (setq n (1- n)))
5865   (if (/= 0 n) (message "No more articles"))
5866   n))
5867
5868 (defun gnus-summary-unmark-as-processable (n)
5869   "Remove the process mark from the next N articles.
5870 If N is negative, mark backward instead.  The difference between N and
5871 the actual number of articles marked is returned."
5872   (interactive "p")
5873   (gnus-summary-mark-as-processable n t))
5874
5875 (defun gnus-summary-unmark-all-processable ()
5876   "Remove the process mark from all articles."
5877   (interactive)
5878   (save-excursion
5879     (while gnus-newsgroup-processable
5880       (gnus-summary-remove-process-mark (car gnus-newsgroup-processable))))
5881   (gnus-summary-position-cursor))
5882
5883 (defun gnus-summary-mark-as-expirable (n &optional unmark)
5884   "Mark N articles forward as expirable.
5885 If N is negative, mark backward instead.  If UNMARK is non-nil, remove
5886 the expirably mark instead.  The difference between N and the actual
5887 number of articles marked is returned."
5888   (interactive "p")
5889   (let ((backward (< n 0))
5890         (n (abs n)))
5891   (while (and (> n 0)
5892               (if unmark
5893                   (gnus-summary-remove-expirable-mark 
5894                    (gnus-summary-article-number))
5895                 (gnus-summary-set-expirable-mark 
5896                  (gnus-summary-article-number)))
5897               (= 0 (gnus-summary-next-subject (if backward -1 1))))
5898     (setq n (1- n)))
5899   (if (/= 0 n) (message "No more articles"))
5900   n))
5901
5902 (defun gnus-summary-unmark-as-expirable (n)
5903   "Mark N articles forward as expirable.
5904 If N is negative, mark backward instead.  The difference between N and
5905 the actual number of articles marked is returned."
5906   (interactive "p")
5907   (gnus-summary-mark-as-expirable n t))
5908
5909 (defun gnus-summary-set-expirable-mark (article)
5910   "Mark the current article as expirable and update the Summary line."
5911   (setq gnus-newsgroup-expirable (cons article gnus-newsgroup-expirable))
5912   (let ((buffer-read-only nil))
5913     (if (gnus-summary-goto-subject article)
5914         (progn
5915           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5916           (beginning-of-line)
5917           (forward-char 2)
5918           (delete-char 1)
5919           (insert "X")
5920           (run-hooks 'gnus-summary-update-hook)
5921           t))))
5922
5923 (defun gnus-summary-remove-expirable-mark (article)
5924   "Remove the expirable mark from ARTICLE as expirable and update the Summary line."
5925   (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable))
5926   (let ((buffer-read-only nil))
5927     (if (gnus-summary-goto-subject article)
5928         (progn
5929           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5930           (beginning-of-line)
5931           (forward-char 2)
5932           (delete-char 1)
5933           (insert 
5934            (if (memq article gnus-newsgroup-processable) ?# ? ))
5935           (run-hooks 'gnus-summary-update-hook)   
5936           t))))
5937
5938 (defun gnus-summary-expire-articles ()
5939   "Expire all articles that are marked as expirable in the current group."
5940   (interactive)
5941   (if (and gnus-newsgroup-expirable
5942            (gnus-check-backend-function 
5943             'gnus-request-expire-articles gnus-newsgroup-name))
5944       (setq gnus-newsgroup-expirable 
5945             (gnus-request-expire-articles gnus-newsgroup-expirable
5946                                           gnus-newsgroup-name))))
5947
5948 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
5949 (defun gnus-summary-delete-article (n)
5950   "Delete the N next (mail) articles.
5951 This command actually deletes articles. This is not a marking
5952 command. The article will disappear forever from you life, never to
5953 return. 
5954 If N is negative, delete backwards.
5955 If N is nil and articles have been marked with the process mark,
5956 delete these instead."
5957   (interactive "P")
5958   (or (gnus-check-backend-function 'request-expire-articles 
5959                                    gnus-newsgroup-name)
5960       (error "The current newsgroup does not support article deletion."))
5961   ;; Compute the list of articles to delete.
5962   (let (articles)
5963     (if (and n (numberp n))
5964         (let ((backward (< n 0))
5965               (n (abs n)))
5966           (save-excursion
5967             (while (and (> n 0)
5968                         (setq articles (cons (gnus-summary-article-number) 
5969                                              articles))
5970                         (gnus-summary-search-forward nil nil backward))
5971               (setq n (1- n))))
5972           (setq articles (sort articles (function <))))
5973       (setq articles (or (setq gnus-newsgroup-processable
5974                                (sort gnus-newsgroup-processable (function <)))
5975                          (list (gnus-summary-article-number)))))
5976     (if (and gnus-novice-user
5977              (not (y-or-n-p 
5978                    (format "Do you really want to delete %s forever?"
5979                            (if (> (length articles) 1) "these articles"
5980                              "this article")))))
5981         ()
5982       ;; Delete the articles.
5983       (setq gnus-newsgroup-expirable 
5984             (gnus-request-expire-articles 
5985              articles gnus-newsgroup-name 'force)))))
5986
5987 (defun gnus-summary-mark-article-as-replied (article)
5988   "Mark ARTICLE replied and update the Summary line."
5989   (setq gnus-newsgroup-replied (cons article gnus-newsgroup-replied))
5990   (let ((buffer-read-only nil))
5991     (if (gnus-summary-goto-subject article)
5992         (progn
5993           (beginning-of-line)
5994           (forward-char 1)
5995           (delete-char 1)
5996           (insert "R")
5997           t))))
5998
5999 (defun gnus-summary-set-bookmark (article)
6000   "Set a bookmark in current article."
6001   (interactive (list (gnus-summary-article-number)))
6002   (if (or (not (get-buffer gnus-article-buffer))
6003           (not gnus-current-article)
6004           (not gnus-article-current)
6005           (not (equal gnus-newsgroup-name (car gnus-article-current))))
6006       (error "No current article selected"))
6007   ;; Remove old bookmark, if one exists.
6008   (let ((old (assq article gnus-newsgroup-bookmarks)))
6009     (if old (setq gnus-newsgroup-bookmarks 
6010                   (delq old gnus-newsgroup-bookmarks))))
6011   ;; Set the new bookmark, which is on the form 
6012   ;; (article-number . line-number-in-body).
6013   (setq gnus-newsgroup-bookmarks 
6014         (cons 
6015          (cons article 
6016                (save-excursion
6017                  (set-buffer gnus-article-buffer)
6018                  (count-lines
6019                   (min (point)
6020                        (save-excursion
6021                          (goto-char 1)
6022                          (search-forward "\n\n" nil t)
6023                          (point)))
6024                   (point))))
6025          gnus-newsgroup-bookmarks))
6026   (message "A bookmark has been added to the current article."))
6027
6028 (defun gnus-summary-remove-bookmark (article)
6029   "Remove the bookmark from the current article."
6030   (interactive (list (gnus-summary-article-number)))
6031   ;; Remove old bookmark, if one exists.
6032   (let ((old (assq article gnus-newsgroup-bookmarks)))
6033     (if old 
6034         (progn
6035           (setq gnus-newsgroup-bookmarks 
6036                 (delq old gnus-newsgroup-bookmarks))
6037           (message "Removed bookmark."))
6038       (message "No bookmark in current article."))))
6039
6040 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
6041 (defun gnus-summary-mark-as-dormant (n)
6042   "Mark N articles forward as dormant.
6043 If N is negative, mark backward instead.  The difference between N and
6044 the actual number of articles marked is returned."
6045   (interactive "p")
6046   (gnus-summary-mark-forward n gnus-dormant-mark))
6047
6048 (defun gnus-summary-set-process-mark (article)
6049   "Set the process mark on ARTICLE and update the Summary line."
6050   (setq gnus-newsgroup-processable (cons article gnus-newsgroup-processable))
6051   (let ((buffer-read-only nil))
6052     (if (gnus-summary-goto-subject article)
6053         (progn
6054           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
6055           (beginning-of-line)
6056           (forward-char 2)
6057           (delete-char 1)
6058           (insert "#")
6059           (run-hooks 'gnus-summary-update-hook)
6060           t))))
6061
6062 (defun gnus-summary-remove-process-mark (article)
6063   "Remove the process mark from ARTICLE and update the Summary line."
6064   (setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable))
6065   (let ((buffer-read-only nil))
6066     (if (gnus-summary-goto-subject article)
6067         (progn
6068           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
6069           (beginning-of-line)
6070           (forward-char 2)
6071           (delete-char 1)
6072           (insert 
6073            (if (memq article gnus-newsgroup-expirable) ?X ? ))
6074           (run-hooks 'gnus-summary-update-hook)
6075           t))))
6076
6077 (defun gnus-summary-mark-forward (n &optional unread)
6078   "Mark N articles as read forwards.
6079 If N is negative, mark backwards instead.
6080 If UNREAD is non-nil, mark articles as unread. In that case, UNREAD
6081 must either be \" \", \"-\" or \"I\".
6082 The difference between N and the actual number of articles marked is
6083 returned."
6084   (interactive "p")
6085   (let ((backward (< n 0))
6086         (n (abs n)))
6087   (while (and (> n 0)
6088               (gnus-summary-mark-article nil unread)
6089               (= 0 (gnus-summary-next-subject (if backward -1 1))))
6090     (setq n (1- n)))
6091   (if (/= 0 n) (message "No more %sarticles" (if unread "" "unread ")))
6092   (gnus-set-mode-line 'summary)
6093   n))
6094
6095 (defun gnus-summary-mark-article (&optional article mark)
6096   "Mark ARTICLE with MARK.
6097 MARK can be any string (but it should just be one character long). 
6098 Four MARK strings are reserved: \" \" (unread), 
6099 \"-\" (ticked), \"I\" (dormant), \"D\" (read).
6100 If MARK is nil, then the default string \"D\" is used.
6101 If ARTICLE is nil, then the article on the current line will be
6102 marked." 
6103   (let* ((buffer-read-only nil)
6104          (mark (or mark "D"))
6105          (article (or article (gnus-summary-article-number))))
6106     (if (numberp mark) (setq mark (format "%c" mark)))
6107     (prog1
6108         (if (gnus-summary-goto-subject article)
6109             (progn
6110               (gnus-summary-show-thread)
6111               (beginning-of-line)
6112               (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
6113               ;; Fix the invisible mark.
6114               (re-search-forward ". [-0-9]+ [0-9]+[\n\r]")
6115               (goto-char (match-beginning 0))
6116               (delete-char 1)
6117               (insert mark)
6118               (set-text-properties (1- (point)) (point) '(invisible t))
6119               ;; Fix the visible mark.
6120               (beginning-of-line)
6121               (delete-char 1)
6122               (insert mark)
6123               (run-hooks 'gnus-summary-update-hook)
6124               t))
6125       (if (or (string= mark gnus-unread-mark) 
6126               (string= mark gnus-ticked-mark) 
6127               (string= mark gnus-dormant-mark))
6128           (gnus-mark-article-as-unread article mark)
6129         (gnus-mark-article-as-read article)))))
6130
6131 (defun gnus-mark-article-as-read (article)
6132   "Remember that ARTICLE is marked as read."
6133   ;; Make the article expirable.
6134   (if gnus-newsgroup-auto-expire
6135     (gnus-summary-set-expirable-mark article))
6136   ;; Remove from unread and marked list.
6137   (setq gnus-newsgroup-unreads
6138         (delq article gnus-newsgroup-unreads))
6139   (setq gnus-newsgroup-marked
6140         (delq article gnus-newsgroup-marked))
6141   (setq gnus-newsgroup-dormant
6142         (delq article gnus-newsgroup-dormant)))
6143
6144 (defun gnus-mark-article-as-unread (article &optional mark)
6145   "Remember that ARTICLE is marked as unread.
6146 MARK is the mark type: \" \", \"-\" or \"I\"."
6147   ;; Add to unread list.
6148   (or (memq article gnus-newsgroup-unreads)
6149       (setq gnus-newsgroup-unreads
6150             (cons article gnus-newsgroup-unreads)))
6151   ;; Update the expired list.
6152   (gnus-summary-remove-expirable-mark article)
6153   ;; If CLEAR-MARK is non-nil, the article must be removed from marked
6154   ;; list.  Otherwise, it must be added to the list.
6155   (setq gnus-newsgroup-marked
6156         (delq article gnus-newsgroup-marked))
6157   (setq gnus-newsgroup-dormant
6158         (delq article gnus-newsgroup-dormant))
6159   (if (equal mark gnus-ticked-mark)
6160       (setq gnus-newsgroup-marked 
6161             (cons article gnus-newsgroup-marked)))
6162   (if (equal mark gnus-dormant-mark)
6163       (setq gnus-newsgroup-dormant 
6164             (cons article gnus-newsgroup-dormant))))
6165
6166 (defalias 'gnus-summary-mark-as-unread-forward 
6167   'gnus-summary-tick-article-forward)
6168 (make-obsolete 'gnus-summary-mark-as-unread-forward 
6169                'gnus-summary-tick-article--forward)
6170 (defun gnus-summary-tick-article-forward (n)
6171   "Tick N articles forwards.
6172 If N is negative, tick backwards instead.
6173 The difference between N and the number of articles ticked is returned."
6174   (interactive "p")
6175   (gnus-summary-mark-forward n gnus-ticked-mark))
6176
6177 (defalias 'gnus-summary-mark-as-unread-backward 'gnus-summary-tick-article-backward)
6178 (make-obsolete 'gnus-summary-mark-as-unread-backward 'gnus-summary-tick-article-backward)
6179 (defun gnus-summary-tick-article-backward (n)
6180   "Tick N articles backwards.
6181 The difference between N and the number of articles ticked is returned."
6182   (interactive "p")
6183   (gnus-summary-mark-forward (- n) gnus-ticked-mark))
6184
6185 (defalias 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
6186 (make-obsolete 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
6187 (defun gnus-summary-tick-article (&optional article clear-mark)
6188   "Mark current article as unread.
6189 Optional 1st argument ARTICLE specifies article number to be marked as unread.
6190 Optional 2nd argument CLEAR-MARK remove any kinds of mark."
6191   (gnus-summary-mark-article article (if clear-mark gnus-unread-mark
6192                                        gnus-ticked-mark)))
6193
6194 (defun gnus-summary-mark-as-read-forward (n)
6195   "Mark N articles as read forwards.
6196 If N is negative, mark backwards instead.
6197 The difference between N and the actual number of articles marked is
6198 returned."
6199   (interactive "p")
6200   (gnus-summary-mark-forward n))
6201
6202 (defun gnus-summary-mark-as-read-backward (n)
6203   "Mark the N articles as read backwards.
6204 The difference between N and the actual number of articles marked is
6205 returned."
6206   (interactive "p")
6207   (gnus-summary-mark-forward (- n)))
6208
6209 (defun gnus-summary-mark-as-read (&optional article mark)
6210   "Mark current article as read.
6211 ARTICLE specifies the article to be marked as read.
6212 MARK specifies a string to be inserted at the beginning of the line.
6213 Any kind of string (length 1) except for a space and `-' is ok."
6214   (gnus-summary-mark-article article mark))
6215
6216 (defun gnus-summary-clear-mark-forward (n)
6217   "Clear marks from N articles forward.
6218 If N is negative, clear backward instead.
6219 The difference between N and the number of marks cleared is returned."
6220   (interactive "p")
6221   (gnus-summary-mark-forward n gnus-unread-mark))
6222
6223 (defun gnus-summary-clear-mark-backward (n)
6224   "Clear marks from N articles backward.
6225 The difference between N and the number of marks cleared is returned."
6226   (interactive "p")
6227   (gnus-summary-mark-forward (- n) gnus-unread-mark))
6228
6229 ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
6230 (defun gnus-summary-delete-marked-as-read ()
6231   "Delete lines that are marked as read."
6232   (interactive)
6233   (gnus-summary-delete-marked-with 
6234    (concat gnus-read-mark gnus-killed-mark gnus-kill-file-mark)))
6235
6236 (defun gnus-summary-delete-marked-with (marks)
6237   "Delete lines that are marked with MARKS (e.g. \"DK\")."
6238   (interactive "sMarks: ")
6239   ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
6240   (save-excursion
6241     (set-buffer gnus-summary-buffer)
6242     (let ((buffer-read-only nil)
6243           (marks (concat "[" marks "]"))
6244           beg)
6245       (goto-char (point-min))
6246       (while (not (eobp))
6247         (if (looking-at marks)
6248             (progn
6249               (setq beg (point))
6250               (forward-line 1)
6251               ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
6252               (setq gnus-newsgroup-expunged-lines
6253                     (concat (or gnus-newsgroup-expunged-lines "")
6254                             (buffer-substring beg (point))))
6255               (delete-region beg (point)))
6256           (forward-line 1))))
6257     ;; Adjust point.
6258     (or (zerop (buffer-size))
6259         (if (eobp)
6260             (gnus-summary-prev-subject 1)
6261           (gnus-summary-position-cursor)))))
6262
6263 (defun gnus-summary-expunge-below (score)
6264   "Delete articles with score less than SCORE."
6265   (interactive "P")
6266   (setq score (if score
6267                   (prefix-numeric-value score)
6268                 gnus-summary-default-interest))
6269   (save-excursion
6270     (set-buffer gnus-summary-buffer)
6271     (goto-char (point-min))
6272     (let ((buffer-read-only nil)
6273           beg)
6274       (while (not (eobp))
6275         (if (< (gnus-summary-interest) score)
6276             (progn
6277               (setq beg (point))
6278               (forward-line 1)
6279               (setq gnus-newsgroup-expunged-lines 
6280                     (buffer-substring beg (point)))
6281               (delete-region beg (point)))
6282           (forward-line 1)))
6283       ;; Adjust point.
6284       (or (zerop (buffer-size))
6285           (if (eobp)
6286               (gnus-summary-prev-subject 1)
6287             (gnus-summary-position-cursor))))))
6288
6289 (defun gnus-summary-mark-below (score mark)
6290   "Mark articles with score less than SCORE with MARK."
6291   (interactive "P\ncMark: ")
6292   (setq score (if score
6293                   (prefix-numeric-value score)
6294                 gnus-summary-default-interest))
6295   (save-excursion
6296     (set-buffer gnus-summary-buffer)
6297     (goto-char (point-min))
6298     (while (not (eobp))
6299       (if (< (gnus-summary-interest) score)
6300           (progn
6301             (gnus-summary-mark-article nil (char-to-string mark))
6302             (forward-line 1))
6303         (forward-line 1)))))
6304
6305 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
6306 (defun gnus-summary-kill-below (score)
6307   "Kill articles with score below SCORE."
6308   (interactive "P")
6309   (gnus-summary-mark-below score ?K))
6310
6311 (defun gnus-summary-clear-above (score)
6312   "Clear all marks from articles with score above SCORE."
6313   (interactive "P")
6314   (gnus-summary-mark-above score ? ))
6315
6316 (defun gnus-summary-tick-above (score)
6317   "Tick all articles with score above SCORE."
6318   (interactive "P")
6319   (gnus-summary-mark-above score ?-))
6320
6321 (defun gnus-summary-mark-above (score mark)
6322   "Mark articles with score less than SCORE with MARK."
6323   (interactive "P\ncMark: ")
6324   (setq score (if score
6325                   (prefix-numeric-value score)
6326                 gnus-summary-default-interest))
6327   (save-excursion
6328     (set-buffer gnus-summary-buffer)
6329     (goto-char (point-min))
6330     (while (not (eobp))
6331       (if (> (gnus-summary-interest) score)
6332           (progn
6333             (gnus-summary-mark-article nil (char-to-string mark))
6334             (forward-line 1))
6335         (forward-line 1)))))
6336
6337 ;; Suggested by Daniel Quinlan <quinlan@best.com>.  
6338 (defun gnus-summary-show-all-expunged ()
6339   "Show all previously expunge articles."
6340   (interactive)
6341   (if (not gnus-newsgroup-expunged-lines)
6342       (error "No lines expunged."))
6343   (let ((buffer-read-only nil))
6344     (goto-char (point-min))
6345     (save-excursion
6346       (insert gnus-newsgroup-expunged-lines))
6347     (setq gnus-newsgroup-expunged-lines nil)))
6348
6349 (defun gnus-summary-show-all-dormant ()
6350   "Display all the hidden articles that are marked as dormant."
6351   (interactive)
6352   (let ((int gnus-newsgroup-dormant-subjects)
6353         (buffer-read-only nil))
6354     (if (not int)
6355         (error "No dormant articles hidden."))
6356     (goto-char (point-min))
6357     (save-excursion
6358       (while int
6359         (insert (cdr (car int)))
6360         (setq int (cdr int))))
6361     (gnus-summary-position-cursor)
6362     (setq gnus-newsgroup-dormant-subjects nil)))
6363
6364 (defun gnus-summary-catchup (all &optional quietly to-here)
6365   "Mark all articles not marked as unread in this newsgroup as read.
6366 If prefix argument ALL is non-nil, all articles are marked as read.
6367 If QUIETLY is non-nil, no questions will be asked.
6368 If TO-HERE is non-nil, it should be a point in the buffer. All
6369 articles before this point will be marked as read.
6370 The number of articles marked as read is returned."
6371   (interactive "P")
6372   (if (or quietly
6373           (not gnus-interactive-catchup) ;Without confirmation?
6374           gnus-expert-user
6375           (y-or-n-p
6376            (if all
6377                "Do you really want to mark everything as read? "
6378              "Delete all articles not marked as unread? ")))
6379       (let ((unreads (length gnus-newsgroup-unreads)))
6380         (if (gnus-summary-first-subject (not all))
6381             (while (and (gnus-summary-mark-as-read nil gnus-catchup-mark)
6382                         (if to-here (< (point) to-here) t)
6383                         (gnus-summary-search-subject nil (not all)))))
6384         (- unreads (length gnus-newsgroup-unreads)))))
6385
6386 (defun gnus-summary-catchup-to-here (&optional all)
6387   "Mark all unticked articles before the current one as read.
6388 If ALL is non-nil, also mark ticked and dormant articles as read."
6389   (interactive)
6390   (beginning-of-line)
6391   (gnus-summary-catchup all nil (point))
6392   (gnus-summary-position-cursor))
6393
6394 (defun gnus-summary-catchup-all (&optional quietly)
6395   "Mark all articles in this newsgroup as read."
6396   (interactive)
6397   (gnus-summary-catchup t quietly))
6398
6399 (defun gnus-summary-catchup-and-exit (all &optional quietly)
6400   "Mark all articles not marked as unread in this newsgroup as read, then exit.
6401 If prefix argument ALL is non-nil, all articles are marked as read."
6402   (interactive "P")
6403   (gnus-summary-catchup all quietly)
6404   ;; Select next newsgroup or exit.
6405   (if (eq gnus-auto-select-next 'quietly)
6406       (gnus-summary-next-group nil)
6407     (gnus-summary-exit)))
6408
6409 (defun gnus-summary-catchup-all-and-exit (&optional quietly)
6410   "Mark all articles in this newsgroup as read, and then exit."
6411   (interactive)
6412   (gnus-summary-catchup-and-exit t quietly))
6413
6414 ;; Thread-based commands.
6415
6416 (defun gnus-summary-toggle-threads (arg)
6417   "Toggle showing conversation threads.
6418 If ARG is positive number, turn showing conversation threads on."
6419   (interactive "P")
6420   (let ((current (gnus-summary-article-number)))
6421     (setq gnus-show-threads
6422           (if (null arg) (not gnus-show-threads)
6423             (> (prefix-numeric-value arg) 0)))
6424     (gnus-summary-prepare)
6425     (gnus-summary-goto-subject current)))
6426
6427 (defun gnus-summary-show-all-threads ()
6428   "Show all threads."
6429   (interactive)
6430   (if gnus-show-threads
6431       (save-excursion
6432         (let ((buffer-read-only nil))
6433           (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)))))
6434
6435 (defun gnus-summary-show-thread ()
6436   "Show thread subtrees."
6437   (interactive)
6438   (if gnus-show-threads
6439       (save-excursion
6440         (let ((buffer-read-only nil))
6441           (subst-char-in-region 
6442            (progn (beginning-of-line) (point))
6443            (progn (end-of-line) (point)) ?\^M ?\n t)))))
6444
6445 (defun gnus-summary-hide-all-threads ()
6446   "Hide all thread subtrees."
6447   (interactive)
6448   (if gnus-show-threads
6449       (save-excursion
6450         (goto-char (point-min))
6451         (gnus-summary-hide-thread)
6452         (while (gnus-summary-search-forward)
6453           (gnus-summary-hide-thread)))))
6454
6455 (defun gnus-summary-hide-thread ()
6456   "Hide thread subtrees."
6457   (interactive)
6458   (if gnus-show-threads
6459       (save-excursion
6460         (let ((buffer-read-only nil)
6461               (start (point))
6462               (level (gnus-summary-thread-level))
6463               (end (point)))
6464           ;; Go forward until either the buffer ends or the subthread
6465           ;; ends. 
6466           (while (and (= 0 (forward-line 1))
6467                       (> (gnus-summary-thread-level) level))
6468             (setq end (point)))
6469           (subst-char-in-region start end ?\n ?\^M t)))))
6470
6471 (defun gnus-summary-go-to-next-thread (&optional previous)
6472   "Go to the same level (or less) next thread.
6473 If PREVIOUS is non-nil, go to previous thread instead.
6474 Return the article number moved to, or nil if moving was impossible."
6475   (let ((level (gnus-summary-thread-level))
6476         (article (gnus-summary-article-number)))
6477     (if previous 
6478         (while (and (zerop (gnus-summary-prev-subject 1))
6479                     (> (gnus-summary-thread-level) level)))
6480       (while (and (zerop (gnus-summary-next-subject 1))
6481                   (> (gnus-summary-thread-level) level))))
6482     (let ((oart (gnus-summary-article-number)))
6483       (and (/= oart article) oart))))
6484
6485 (defun gnus-summary-next-thread (n)
6486   "Go to the same level next N'th thread.
6487 If N is negative, search backward instead.
6488 Returns the difference between N and the number of skips actually
6489 done."
6490   (interactive "p")
6491   (let ((backward (< n 0))
6492         (n (abs n)))
6493   (while (and (> n 0)
6494               (gnus-summary-go-to-next-thread backward))
6495     (setq n (1- n)))
6496   (gnus-summary-position-cursor)
6497   (if (/= 0 n) (message "No more threads" ))
6498   n))
6499
6500 (defun gnus-summary-prev-thread (n)
6501   "Go to the same level previous N'th thread.
6502 Returns the difference between N and the number of skips actually
6503 done."
6504   (interactive "p")
6505   (gnus-summary-next-thread (- n)))
6506
6507 (defun gnus-summary-go-down-thread (&optional up same)
6508   "Go down one level in the current thread.
6509 If UP is non-nil, go up instead.
6510 If SAME is non-nil, also move to articles of the same level."
6511   (let ((level (gnus-summary-thread-level))
6512         (start (point))
6513         (level-diff (if up -1 1))
6514         l)
6515     (if (not (and (= 0 (forward-line level-diff))
6516                   (or (= (+ level level-diff) 
6517                          (setq l (gnus-summary-thread-level)))
6518                       (and same (= level l)))))
6519         (goto-char start))
6520     (/= start (point))))
6521
6522 (defun gnus-summary-down-thread (n)
6523   "Go down thread N steps.
6524 If N is negative, go up instead.
6525 Returns the difference between N and how many steps down that were
6526 taken."
6527   (interactive "p")
6528   (let ((up (< n 0))
6529         (n (abs n)))
6530   (while (and (> n 0)
6531               (gnus-summary-go-down-thread up))
6532     (setq n (1- n)))
6533   (gnus-summary-position-cursor)
6534   (if (/= 0 n) (message "Can't go further" ))
6535   n))
6536
6537 (defun gnus-summary-up-thread (n)
6538   "Go up thread N steps.
6539 If N is negative, go up instead.
6540 Returns the difference between N and how many steps down that were
6541 taken."
6542   (interactive "p")
6543   (gnus-summary-down-thread (- n)))
6544
6545 (defun gnus-summary-kill-thread (unmark)
6546   "Mark articles under current thread as read.
6547 If the prefix argument is positive, remove any kinds of marks.
6548 If the prefix argument is negative, tick articles instead."
6549   (interactive "P")
6550   (if unmark
6551       (setq unmark (prefix-numeric-value unmark)))
6552   (let ((killing t)
6553         (level (gnus-summary-thread-level)))
6554     (save-excursion
6555       (while killing
6556         ;; Mark the article...
6557         (cond ((null unmark) (gnus-summary-mark-as-read nil gnus-killed-mark))
6558               ((> unmark 0) (gnus-summary-tick-article nil t))
6559               (t (gnus-summary-tick-article)))
6560         ;; ...and go forward until either the buffer ends or the subtree
6561         ;; ends. 
6562         (if (not (and (= 0 (forward-line 1))
6563                       (> (gnus-summary-thread-level) level)))
6564             (setq killing nil))))
6565     ;; Hide killed subtrees.
6566     (and (null unmark)
6567          gnus-thread-hide-killed
6568          (gnus-summary-hide-thread))
6569     ;; If marked as read, go to next unread subject.
6570     (if (null unmark)
6571         ;; Go to next unread subject.
6572         (gnus-summary-next-subject 1 t)))
6573   (gnus-set-mode-line 'summary))
6574
6575 ;; Summary sorting commands
6576
6577 (defun gnus-summary-sort-by-number (reverse)
6578   "Sort Summary buffer by article number.
6579 Argument REVERSE means reverse order."
6580   (interactive "P")
6581   (gnus-summary-keysort-summary
6582    (function <)
6583    (lambda (a)
6584      (header-number a))
6585    reverse
6586    ))
6587
6588 (defun gnus-summary-sort-by-author (reverse)
6589   "Sort Summary buffer by author name alphabetically.
6590 If case-fold-search is non-nil, case of letters is ignored.
6591 Argument REVERSE means reverse order."
6592   (interactive "P")
6593   (gnus-summary-keysort-summary
6594    (function string-lessp)
6595    (lambda (a)
6596      (if case-fold-search
6597          (downcase (header-from a))
6598        (header-from a)))
6599    reverse
6600    ))
6601
6602 (defun gnus-summary-sort-by-subject (reverse)
6603   "Sort Summary buffer by subject alphabetically. `Re:'s are ignored.
6604 If case-fold-search is non-nil, case of letters is ignored.
6605 Argument REVERSE means reverse order."
6606   (interactive "P")
6607   (gnus-summary-keysort-summary
6608    (function string-lessp)
6609    (lambda (a)
6610      (if case-fold-search
6611          (downcase (gnus-simplify-subject (header-subject a) 're-only))
6612        (gnus-simplify-subject (header-subject a) 're-only)))
6613    reverse
6614    ))
6615
6616 (defun gnus-summary-sort-by-date (reverse)
6617   "Sort Summary buffer by date.
6618 Argument REVERSE means reverse order."
6619   (interactive "P")
6620   (gnus-summary-keysort-summary
6621    (function string-lessp)
6622    (lambda (a)
6623      (gnus-sortable-date (header-date a)))
6624    reverse
6625    ))
6626
6627 (defun gnus-summary-keysort-summary (predicate key &optional reverse)
6628   "Sort Summary buffer by PREDICATE using a value passed by KEY.
6629 Optional argument REVERSE means reverse order."
6630   (let ((current (gnus-summary-article-number)))
6631     (gnus-keysort-headers predicate key reverse)
6632     (gnus-summary-prepare)
6633     (gnus-summary-goto-subject current)
6634     ))
6635
6636 (defun gnus-summary-sort-summary (predicate &optional reverse)
6637   "Sort Summary buffer by PREDICATE.
6638 Optional argument REVERSE means reverse order."
6639   (let ((current (gnus-summary-article-number)))
6640     (gnus-sort-headers predicate reverse)
6641     (gnus-summary-prepare)
6642     (gnus-summary-goto-subject current)
6643     ))
6644
6645 ;; Basic ideas by flee@cs.psu.edu (Felix Lee)
6646
6647 (defun gnus-keysort-headers (predicate key &optional reverse)
6648   "Sort current headers by PREDICATE using a value passed by KEY safely.
6649 *Safely* means C-g quitting is disabled during sort.
6650 Optional argument REVERSE means reverse order."
6651   (let ((inhibit-quit t))
6652     (setq gnus-newsgroup-headers
6653           (if reverse
6654               (nreverse
6655                (gnus-keysort (nreverse gnus-newsgroup-headers) predicate key))
6656             (gnus-keysort gnus-newsgroup-headers predicate key)))
6657     ))
6658
6659 (defun gnus-keysort (list predicate key)
6660   "Sort LIST by PREDICATE using a value passed by KEY."
6661   (mapcar (function cdr)
6662           (sort (mapcar (lambda (a) (cons (funcall key a) a)) list)
6663                 (lambda (a b)
6664                   (funcall predicate (car a) (car b))))))
6665
6666 (defun gnus-sort-headers (predicate &optional reverse)
6667   "Sort current headers by PREDICATE safely.
6668 *Safely* means C-g quitting is disabled during sort.
6669 Optional argument REVERSE means reverse order."
6670   (let ((inhibit-quit t))
6671     (setq gnus-newsgroup-headers
6672           (if reverse
6673               (nreverse (sort (nreverse gnus-newsgroup-headers) predicate))
6674             (sort gnus-newsgroup-headers predicate)))
6675     ))
6676
6677 (defun gnus-string-lessp (a b)
6678   "Return T if first arg string is less than second in lexicographic order.
6679 If case-fold-search is non-nil, case of letters is ignored."
6680   (if case-fold-search
6681       (string-lessp (downcase a) (downcase b))
6682     (string-lessp a b)))
6683
6684 (defun gnus-date-lessp (date1 date2)
6685   "Return T if DATE1 is earlyer than DATE2."
6686   (string-lessp (gnus-sortable-date date1)
6687                 (gnus-sortable-date date2)))
6688
6689 (defun gnus-sortable-date (date)
6690   "Make sortable string by string-lessp from DATE.
6691 Timezone package is used."
6692   (let* ((date   (timezone-fix-time date nil nil)) ;[Y M D H M S]
6693          (year   (aref date 0))
6694          (month  (aref date 1))
6695          (day    (aref date 2)))
6696     (timezone-make-sortable-date year month day 
6697                                  (timezone-make-time-string
6698                                   (aref date 3) (aref date 4) (aref date 5)))
6699     ))
6700
6701
6702 ;; Summary saving commands.
6703
6704 (defun gnus-summary-save-article (n)
6705   "Save the current article using the default saver function.
6706 If N is a positive number, save the N next articles.
6707 If N is a negative number, save the N previous articles.
6708 If N is nil and any articles have been marked with the process mark,
6709 save those articles instead.
6710 The variable `gnus-default-article-saver' specifies the saver function."
6711   (interactive "P")
6712   (let (articles process)
6713     (if (and n (numberp n))
6714         (let ((backward (< n 0))
6715               (n (abs n)))
6716           (save-excursion
6717             (while (and (> n 0)
6718                         (setq articles (cons (gnus-summary-article-number) 
6719                                              articles))
6720                         (gnus-summary-search-forward nil nil backward))
6721               (setq n (1- n))))
6722           (setq articles (sort articles (function <))))
6723       (if gnus-newsgroup-processable
6724           (progn
6725             (setq articles (setq gnus-newsgroup-processable
6726                                  (nreverse gnus-newsgroup-processable)))
6727             (setq process t))
6728         (setq articles (list (gnus-summary-article-number)))))
6729     (while articles
6730       (gnus-summary-display-article (car articles) t)
6731       (if (not gnus-save-all-headers)
6732           (gnus-article-hide-headers t))
6733       (if gnus-default-article-saver
6734           (funcall gnus-default-article-saver)
6735         (error "No default saver is defined."))
6736       (if process
6737           (gnus-summary-remove-process-mark (car articles)))
6738       (setq articles (cdr articles)))
6739     (if process (setq gnus-newsgroup-processable 
6740                       (nreverse gnus-newsgroup-processable)))
6741     n))
6742
6743 (defun gnus-summary-pipe-output (arg)
6744   "Pipe the current article to a subprocess.
6745 If N is a positive number, pipe the N next articles.
6746 If N is a negative number, pipe the N previous articles.
6747 If N is nil and any articles have been marked with the process mark,
6748 pipe those articles instead."
6749   (interactive "P")
6750   (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
6751     (gnus-summary-save-article arg)))
6752
6753 (defun gnus-summary-save-article-rmail (arg)
6754   "Append the current article to an Rmail file.
6755 If N is a positive number, save the N next articles.
6756 If N is a negative number, save the N previous articles.
6757 If N is nil and any articles have been marked with the process mark,
6758 save those articles instead."
6759   (interactive "P")
6760   (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail))
6761     (gnus-summary-save-article arg)))
6762
6763 (defun gnus-summary-save-in-rmail (&optional filename)
6764   "Append this article to Rmail file.
6765 Optional argument FILENAME specifies file name.
6766 Directory to save to is default to `gnus-article-save-directory' which
6767 is initialized from the SAVEDIR environment variable."
6768   (interactive)
6769   (let ((default-name
6770           (funcall gnus-rmail-save-name gnus-newsgroup-name
6771                    gnus-current-headers gnus-newsgroup-last-rmail)))
6772     (or filename
6773         (setq filename
6774               (read-file-name
6775                (concat "Save article in rmail file: (default "
6776                        (file-name-nondirectory default-name) ") ")
6777                (file-name-directory default-name)
6778                default-name)))
6779     (gnus-make-directory (file-name-directory filename))
6780     (gnus-eval-in-buffer-window 
6781      gnus-article-buffer
6782      (save-excursion
6783        (save-restriction
6784          (widen)
6785          (gnus-output-to-rmail filename))))
6786     ;; Remember the directory name to save articles.
6787     (setq gnus-newsgroup-last-rmail filename)))
6788
6789 (defun gnus-summary-save-in-mail (&optional filename)
6790   "Append this article to Unix mail file.
6791 Optional argument FILENAME specifies file name.
6792 Directory to save to is default to `gnus-article-save-directory' which
6793 is initialized from the SAVEDIR environment variable."
6794   (let ((default-name
6795           (funcall gnus-mail-save-name gnus-newsgroup-name
6796                    gnus-current-headers gnus-newsgroup-last-mail)))
6797     (or filename
6798         (setq filename
6799               (read-file-name
6800                (concat "Save article in Unix mail file: (default "
6801                        (file-name-nondirectory default-name) ") ")
6802                (file-name-directory default-name)
6803                default-name)))
6804     (setq filename
6805           (expand-file-name filename
6806                             (and default-name
6807                                  (file-name-directory default-name))))
6808     (gnus-make-directory (file-name-directory filename))
6809     (gnus-eval-in-buffer-window 
6810      gnus-article-buffer
6811      (save-excursion
6812        (save-restriction
6813          (widen)
6814          (if (and (file-readable-p filename) (rmail-file-p filename))
6815              (gnus-output-to-rmail filename)
6816            (rmail-output filename 1 t t)))))
6817     ;; Remember the directory name to save articles.
6818     (setq gnus-newsgroup-last-mail filename)))
6819
6820 (defun gnus-summary-save-in-file (&optional filename)
6821   "Append this article to file.
6822 Optional argument FILENAME specifies file name.
6823 Directory to save to is default to `gnus-article-save-directory' which
6824 is initialized from the SAVEDIR environment variable."
6825   (let ((default-name
6826           (funcall gnus-file-save-name gnus-newsgroup-name
6827                    gnus-current-headers gnus-newsgroup-last-file)))
6828     (or filename
6829         (setq filename
6830               (read-file-name
6831                (concat "Save article in file: (default "
6832                        (file-name-nondirectory default-name) ") ")
6833                (file-name-directory default-name)
6834                default-name)))
6835     (gnus-make-directory (file-name-directory filename))
6836     (gnus-eval-in-buffer-window 
6837      gnus-article-buffer
6838      (save-excursion
6839        (save-restriction
6840          (widen)
6841          (gnus-output-to-file filename))))
6842     ;; Remember the directory name to save articles.
6843     (setq gnus-newsgroup-last-file filename)))
6844
6845 (defun gnus-summary-save-in-pipe (&optional command)
6846   "Pipe this article to subprocess."
6847   (let ((command (read-string "Shell command on article: "
6848                               gnus-last-shell-command)))
6849     (if (string-equal command "")
6850         (setq command gnus-last-shell-command))
6851     (gnus-eval-in-buffer-window 
6852      gnus-article-buffer
6853      (save-restriction
6854        (widen)
6855        (shell-command-on-region (point-min) (point-max) command nil)))
6856     (setq gnus-last-shell-command command)))
6857
6858 ;; Summary killfile commands
6859
6860 ;; Much modification of the kill code and some of the functions are
6861 ;; written by Per Abrahamsen <amanda@iesd.auc.dk>.
6862
6863 (defun gnus-summary-temporarily-lower-by-subject (level)
6864   "Temporarily lower score by LEVEL for current subject.
6865 See `gnus-kill-expiry-days'."
6866   (interactive "p")
6867   (gnus-kill-file-temporarily-lower-by-subject
6868    level
6869    (let ((article (gnus-summary-article-number)))
6870      (if article (gnus-get-header-by-number article)
6871        (error "No article on current line")))))
6872
6873 (defun gnus-summary-temporarily-lower-by-author (level)
6874   "Temporarily lower score by LEVEL for current author.
6875 See `gnus-kill-expiry-days'."
6876   (interactive "p")
6877   (gnus-kill-file-temporarily-lower-by-author
6878    level
6879    (let ((article (gnus-summary-article-number)))
6880      (if article (gnus-get-header-by-number article)
6881        (error "No article on current line")))))
6882
6883 (defun gnus-summary-temporarily-lower-by-xref (level)
6884   "Temporarily lower score by LEVEL for current xref.
6885 See `gnus-kill-expiry-days'."
6886   (interactive "p")
6887   (gnus-kill-file-temporarily-lower-by-xref
6888    level
6889    (let ((article (gnus-summary-article-number)))
6890      (if article (gnus-get-header-by-number article)
6891        (error "No article on current line")))))
6892
6893 (defun gnus-summary-temporarily-lower-by-thread (level)
6894   "Temporarily lower score by LEVEL for current thread.
6895 See `gnus-kill-expiry-days'."
6896   (interactive "p")
6897   (gnus-kill-file-temporarily-lower-by-thread
6898    level
6899    (let ((article (gnus-summary-article-number)))
6900      (if article (gnus-get-header-by-number article)
6901        (error "No article on current line")))))
6902
6903 (defun gnus-summary-lower-by-subject (level)
6904   "Lower score by LEVEL for current subject."
6905   (interactive "p")
6906   (gnus-kill-file-lower-by-subject
6907    level
6908    (let ((article (gnus-summary-article-number)))
6909      (if article (gnus-get-header-by-number article)
6910        (error "No article on current line")))))
6911
6912 (defun gnus-summary-lower-by-author (level)
6913   "Lower score by LEVEL for current author."
6914   (interactive "p")
6915   (gnus-kill-file-lower-by-author
6916    level
6917    (let ((article (gnus-summary-article-number)))
6918      (if article (gnus-get-header-by-number article)
6919        (error "No article on current line")))))
6920
6921 (defun gnus-summary-lower-by-xref (level)
6922   "Lower score by LEVEL for current xref."
6923   (interactive "p")
6924   (gnus-kill-file-lower-by-xref
6925    level
6926    (let ((article (gnus-summary-article-number)))
6927      (if article (gnus-get-header-by-number article)
6928        (error "No article on current line")))))
6929
6930 (defun gnus-summary-lower-followups-to-author (level)
6931   "Lower score by LEVEL for all followups to the current author."
6932   (interactive "p")
6933   (gnus-kill-file-lower-followups-to-author
6934    level
6935    (let ((article (gnus-summary-article-number)))
6936      (if article (gnus-get-header-by-number article)
6937        (error "No article on current line")))))
6938
6939 (defun gnus-summary-temporarily-raise-by-subject (level)
6940   "Temporarily raise score by LEVEL for current subject.
6941 See `gnus-kill-expiry-days'."
6942   (interactive "p")
6943   (gnus-kill-file-temporarily-raise-by-subject
6944    level
6945    (let ((article (gnus-summary-article-number)))
6946      (if article (gnus-get-header-by-number article)
6947        (error "No article on current line")))))
6948
6949 (defun gnus-summary-temporarily-raise-by-author (level)
6950   "Temporarily raise score by LEVEL for current author.
6951 See `gnus-kill-expiry-days'."
6952   (interactive "p")
6953   (gnus-kill-file-temporarily-raise-by-author
6954    level
6955    (let ((article (gnus-summary-article-number)))
6956      (if article (gnus-get-header-by-number article)
6957        (error "No article on current line")))))
6958
6959 (defun gnus-summary-temporarily-raise-by-xref (level)
6960   "Temporarily raise score by LEVEL for current xref.
6961 See `gnus-kill-expiry-days'."
6962   (interactive "p")
6963   (gnus-kill-file-temporarily-raise-by-xref
6964    level
6965    (let ((article (gnus-summary-article-number)))
6966      (if article (gnus-get-header-by-number article)
6967        (error "No article on current line")))))
6968
6969 (defun gnus-summary-temporarily-raise-by-thread (level)
6970   "Temporarily raise score by LEVEL for current thread.
6971 See `gnus-kill-expiry-days'."
6972   (interactive "p")
6973   (gnus-kill-file-temporarily-raise-by-thread
6974    level
6975    (let ((article (gnus-summary-article-number)))
6976      (if article (gnus-get-header-by-number article)
6977        (error "No article on current line")))))
6978
6979 (defun gnus-summary-raise-by-subject (level)
6980   "Raise score by LEVEL for current subject."
6981   (interactive "p")
6982   (gnus-kill-file-raise-by-subject
6983    level
6984    (let ((article (gnus-summary-article-number)))
6985      (if article (gnus-get-header-by-number article)
6986        (error "No article on current line")))))
6987
6988 (defun gnus-summary-raise-by-author (level)
6989   "Raise score by LEVEL for current author."
6990   (interactive "p")
6991   (gnus-kill-file-raise-by-author
6992    level
6993    (let ((article (gnus-summary-article-number)))
6994      (if article (gnus-get-header-by-number article)
6995        (error "No article on current line")))))
6996
6997 (defun gnus-summary-raise-by-xref (level)
6998   "Raise score by LEVEL for current xref."
6999   (interactive "p")
7000   (gnus-kill-file-raise-by-xref
7001    level
7002    (let ((article (gnus-summary-article-number)))
7003      (if article (gnus-get-header-by-number article)
7004        (error "No article on current line")))))
7005
7006 (defun gnus-summary-edit-global-kill ()
7007   "Edit a global KILL file."
7008   (interactive)
7009   (setq gnus-current-kill-article (gnus-summary-article-number))
7010   (gnus-kill-file-edit-file nil)        ;Nil stands for global KILL file.
7011   (message
7012    (substitute-command-keys
7013     "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
7014
7015 (defun gnus-summary-raise-followups-to-author (level)
7016   "Raise score by LEVEL for all followups to the current author."
7017   (interactive "p")
7018   (gnus-kill-file-raise-followups-to-author
7019    level
7020    (let ((article (gnus-summary-article-number)))
7021      (if article (gnus-get-header-by-number article)
7022        (error "No article on current line")))))
7023
7024 (defun gnus-summary-edit-local-kill ()
7025   "Edit a local KILL file applied to the current newsgroup."
7026   (interactive)
7027   (setq gnus-current-kill-article (gnus-summary-article-number))
7028   (gnus-kill-file-edit-file gnus-newsgroup-name)
7029   (message
7030    (substitute-command-keys
7031     "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
7032
7033
7034 \f
7035 ;;;
7036 ;;; Gnus Article Mode
7037 ;;;
7038
7039 (if gnus-article-mode-map
7040     nil
7041   (setq gnus-article-mode-map (make-keymap))
7042   (suppress-keymap gnus-article-mode-map)
7043   (define-key gnus-article-mode-map " " 'gnus-article-next-page)
7044   (define-key gnus-article-mode-map "\177" 'gnus-article-prev-page)
7045   (define-key gnus-article-mode-map "r" 'gnus-article-refer-article)
7046   (define-key gnus-article-mode-map "h" 'gnus-article-show-summary)
7047   (define-key gnus-article-mode-map "s" 'gnus-article-show-summary)
7048   (define-key gnus-article-mode-map "m" 'gnus-article-mail)
7049   (define-key gnus-article-mode-map "M" 'gnus-article-mail-with-original)
7050   (define-key gnus-article-mode-map "?" 'gnus-article-describe-briefly)
7051   (define-key gnus-article-mode-map "\C-c\C-i" 'gnus-info-find-node))
7052
7053 (defun gnus-article-mode ()
7054   "Major mode for browsing through an article.
7055 All normal editing commands are switched off.
7056 The following commands are available:
7057
7058 \\<gnus-article-mode-map>
7059 \\[gnus-article-next-page]\t Scroll the article one page forwards
7060 \\[gnus-article-prev-page]\t Scroll the article one page backwards
7061 \\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
7062 \\[gnus-article-show-summary]\t Display the Summary buffer
7063 \\[gnus-article-mail]\t Send a reply to the address near point
7064 \\[gnus-article-mail-with-original]\t Send a reply to the address near point; include the original article
7065 \\[gnus-article-describe-briefly]\t Describe the current mode briefly
7066 \\[gnus-info-find-node]\t Go to the Gnus info node
7067
7068 "
7069   (interactive)
7070   (kill-all-local-variables)
7071   (setq mode-line-modified "--- ")
7072   (setq major-mode 'gnus-article-mode)
7073   (setq mode-name "Article")
7074   (make-local-variable 'minor-mode-alist)
7075   (or (assq 'gnus-show-mime minor-mode-alist)
7076       (setq minor-mode-alist
7077             (cons (list 'gnus-show-mime " MIME") minor-mode-alist)))
7078   (use-local-map gnus-article-mode-map)
7079   (make-local-variable 'page-delimiter)
7080   (setq page-delimiter gnus-page-delimiter)
7081   (make-local-variable 'mail-header-separator)
7082   (setq mail-header-separator "")       ;For caesar function.
7083   (buffer-disable-undo (current-buffer))
7084   (setq buffer-read-only t)             ;Disable modification
7085   (run-hooks 'gnus-article-mode-hook))
7086
7087 (defun gnus-article-setup-buffer ()
7088   "Initialize Article mode buffer."
7089   (or (get-buffer gnus-article-buffer)
7090       (save-excursion
7091         (set-buffer (get-buffer-create gnus-article-buffer))
7092         (gnus-add-current-to-buffer-list)
7093         (gnus-article-mode))
7094       ))
7095
7096 (defun gnus-request-article-this-buffer (article &optional group)
7097   "Get an article and insert it into this buffer."
7098   ;; Using `gnus-request-article' directly will insert the article into
7099   ;; `nntp-server-buffer' - so we'll save some time by not having to
7100   ;; copy it from the server buffer into the article buffer.
7101
7102   ;; We only request an article by message-id when we do not have the
7103   ;; headers for it, so we'll have to get those.
7104   (if (stringp article) (gnus-read-header article))
7105   ;; If the article number is negative, that means that this article
7106   ;; doesn't belong in this newsgroup (possibly), so we find its
7107   ;; message-id and request it by id instead of number.
7108   (if (and (numberp article) (< article 0))
7109       (save-excursion
7110         (set-buffer gnus-summary-buffer)
7111         (setq article 
7112               (header-id 
7113                (gnus-gethash (int-to-string article)
7114                              gnus-newsgroup-headers-hashtb-by-number)))))
7115   ;; Get the article and into the article buffer.
7116   (gnus-request-article article group (current-buffer)))
7117
7118 (defun gnus-read-header (id)
7119   "Read the headers of article ID and enter them into the Gnus system."
7120   (or gnus-newsgroup-headers-hashtb-by-number
7121       (gnus-make-headers-hashtable-by-number))
7122   (let (header)
7123     (if (not (setq header 
7124                    (car (if (let ((nntp-xover-is-evil t))
7125                               (gnus-retrieve-headers (list id) 
7126                                                      gnus-newsgroup-name))
7127                             (gnus-get-newsgroup-headers)))))
7128         nil
7129       (if (stringp id)
7130           (header-set-number header gnus-reffed-article-number))
7131       (setq gnus-newsgroup-headers (cons header gnus-newsgroup-headers))
7132       (gnus-sethash (int-to-string (header-number header)) header
7133                     gnus-newsgroup-headers-hashtb-by-number)
7134       (if (stringp id)
7135           (setq gnus-reffed-article-number (1- gnus-reffed-article-number)))
7136       (setq gnus-current-headers header)
7137       header)))
7138
7139 (defun gnus-article-prepare (article &optional all-headers header)
7140   "Prepare ARTICLE in Article mode buffer.
7141 ARTICLE can be either a article number or Message-ID.
7142 If ARTICLE is an id, HEADER should be the article headers.
7143 If ALL-HEADERS is non-nil, no headers are hidden."
7144   (save-excursion
7145     ;; Make sure we start are in a Summary buffer.
7146     (if (eq major-mode 'gnus-summary-mode)
7147         (setq gnus-summary-buffer (current-buffer))
7148       (set-buffer gnus-summary-buffer))
7149     ;; Make sure the connection to the server is alive.
7150     (if (not (gnus-server-opened gnus-current-select-method))
7151         (progn
7152           (gnus-check-news-server gnus-current-select-method)
7153           (gnus-request-group gnus-newsgroup-name t)))
7154     (or gnus-newsgroup-headers-hashtb-by-number
7155         (gnus-make-headers-hashtable-by-number))
7156     (let* ((article (if header (header-number header) article))
7157            (summary-buffer (current-buffer))
7158            (internal-hook gnus-article-internal-prepare-hook)
7159            (bookmark (cdr (assq article gnus-newsgroup-bookmarks)))
7160            (group gnus-newsgroup-name))
7161       (save-excursion
7162         (set-buffer gnus-article-buffer)
7163         (let ((buffer-read-only nil))
7164           (erase-buffer)
7165           (prog1
7166               (if (gnus-request-article-this-buffer article group)
7167                   (progn 
7168                     ;; gnus-have-all-headers must be either T or NIL.
7169                     (setq gnus-have-all-headers
7170                           (not (not (or all-headers gnus-show-all-headers))))
7171                     (if (and (numberp article)
7172                              (not (eq article gnus-current-article)))
7173                         ;; Seems like a new article has been selected.
7174                         ;; `gnus-current-article' must be an article number.
7175                         (save-excursion
7176                           (set-buffer summary-buffer)
7177                           (setq gnus-last-article gnus-current-article)
7178                           (setq gnus-current-article article)
7179                           (setq gnus-current-headers
7180                                 (gnus-get-header-by-number 
7181                                  gnus-current-article))
7182                           (setq gnus-article-current 
7183                                 (cons gnus-newsgroup-name 
7184                                       (header-number gnus-current-headers)))
7185                           (run-hooks 'gnus-mark-article-hook)
7186                           (and gnus-visual
7187                                (run-hooks 'gnus-visual-mark-article-hook))
7188                           ;; Set the global newsgroup variables here.
7189                           ;; Suggested by Jim Sisolak
7190                           ;; <sisolak@trans4.neep.wisc.edu>.
7191                           (gnus-set-global-variables)))
7192                     ;; Hooks for getting information from the article.
7193                     ;; This hook must be called before being narrowed.
7194                     (run-hooks 'internal-hook)
7195                     (run-hooks 'gnus-article-prepare-hook)
7196                     ;; Decode MIME message.
7197                     (if (and gnus-show-mime
7198                              (gnus-fetch-field "Mime-Version"))
7199                         (funcall gnus-show-mime-method))
7200                     ;; Perform the article display hooks.
7201                     (let ((buffer-read-only nil))
7202                       (run-hooks 'gnus-article-display-hook))
7203                     ;; Do page break.
7204                     (goto-char (point-min))
7205                     (if gnus-break-pages
7206                         (gnus-narrow-to-page))
7207                     (gnus-set-mode-line 'article)
7208                     t)
7209                 ;; There is no such article.
7210                 (if (numberp article)
7211                     (gnus-summary-mark-as-read article))
7212                 (ding) 
7213                 (message "No such article (may be canceled)")
7214                 nil)
7215             (goto-char 1)
7216             (if bookmark
7217                 (progn
7218                   (message "Moved to bookmark.")
7219                   (search-forward "\n\n" nil t)
7220                   (forward-line bookmark)))
7221             (set-window-start 
7222              (get-buffer-window gnus-article-buffer) (point))))))))
7223
7224 (defun gnus-set-global-variables ()
7225   ;; Set the global equivalents of the Summary buffer-local variables
7226   ;; to the latest values they had. These reflect the Summary buffer
7227   ;; that was in action when the last article was fetched.
7228   (let ((name gnus-newsgroup-name)
7229         (marked gnus-newsgroup-marked)
7230         (unread gnus-newsgroup-unreads)
7231         (headers gnus-current-headers))
7232     (save-excursion
7233       (set-buffer gnus-group-buffer)
7234       (setq gnus-newsgroup-name name)
7235       (setq gnus-newsgroup-marked marked)
7236       (setq gnus-newsgroup-unreads unread)
7237       (setq gnus-current-headers headers))))
7238
7239 (defun gnus-article-show-all-headers ()
7240   "Show all article headers in Article mode buffer."
7241   (save-excursion 
7242     (setq gnus-have-all-headers t)
7243     (gnus-article-setup-buffer)
7244     (set-buffer gnus-article-buffer)
7245     (let ((buffer-read-only nil))
7246       (remove-text-properties 1 (point-max) '(invisible t)))))
7247
7248 (defun gnus-article-hide-headers-if-wanted ()
7249   "Hide unwanted headers if `gnus-have-all-headers' is nil.
7250 Provided for backwards compatability."
7251   (or gnus-have-all-headers
7252       (gnus-article-hide-headers)))
7253
7254 (defun gnus-article-hide-headers (&optional delete)
7255   "Hide unwanted headers and possibly sort them as well."
7256   (save-excursion
7257     (save-restriction
7258       (let ((sorted gnus-sorted-header-list)
7259             (buffer-read-only nil)
7260             want want-list beg want-l)
7261         ;; First we narrow to just the headers.
7262         (widen)
7263         (goto-char 1)
7264         ;; Hide any "From " lines at the beginning of (mail) articles. 
7265         (while (looking-at rmail-unix-mail-delimiter)
7266           (forward-line 1))
7267         (if (/= (point) 1) 
7268             (add-text-properties 1 (point) '(invisible t)))
7269         ;; Then treat the rest of the header lines.
7270         (narrow-to-region 
7271          (point) 
7272          (progn (search-forward "\n\n" nil t) (forward-line -1) (point)))
7273         ;; Then we use the two regular expressions
7274         ;; `gnus-ignored-headers' and `gnus-visible-headers' to
7275         ;; select which header lines is to remain visible in the
7276         ;; article buffer.
7277         (goto-char 1)
7278         (while (re-search-forward "^[^ \t]*:" nil t)
7279           (beginning-of-line)
7280           ;; We add the headers we want to keep to a list and delete
7281           ;; them from the buffer.
7282           (if (or (and (stringp gnus-visible-headers)
7283                        (looking-at gnus-visible-headers))
7284                   (and (not (stringp gnus-visible-headers))
7285                        (stringp gnus-ignored-headers)
7286                        (not (looking-at gnus-ignored-headers))))
7287               (progn
7288                 (setq beg (point))
7289                 (forward-line 1)
7290                 ;; Be sure to get multi-line headers...
7291                 (re-search-forward "^[^ \t]*:" nil t)
7292                 (beginning-of-line)
7293                 (setq want-list 
7294                       (cons (buffer-substring beg (point)) want-list))
7295                 (delete-region beg (point))
7296                 (goto-char beg))
7297             (forward-line 1)))
7298         ;; Next we perform the sorting by looking at
7299         ;; `gnus-sorted-header-list'. 
7300         (goto-char 1)
7301         (while (and sorted want-list)
7302           (setq want-l want-list)
7303           (while (and want-l
7304                       (not (string-match (car sorted) (car want-l))))
7305             (setq want-l (cdr want-l)))
7306           (if want-l 
7307               (progn
7308                 (insert (car want-l))
7309                 (setq want-list (delq (car want-l) want-list))))
7310           (setq sorted (cdr sorted)))
7311         ;; Any headers that were not matched by the sorted list we
7312         ;; just tack on the end of the visible header list.
7313         (while want-list
7314           (insert (car want-list))
7315           (setq want-list (cdr want-list)))
7316         ;; And finally we make the unwanted headers invisible.
7317         (if delete
7318             (delete-region (point) (point-max))
7319           ;; Suggested by Sudish Joseph <joseph@cis.ohio-state.edu>.
7320           (add-text-properties (point) (point-max) '(invisible t)))))))
7321
7322 (defun gnus-article-hide-signature ()
7323   "Hides the signature in an article.
7324 It does this by hiding everyting after "^-- *$", which is what all
7325 signatures should be preceded by. Note that this may mean that parts
7326 of an article may disappear if the article has such a line in the
7327 middle of the text."
7328   (save-excursion
7329     (goto-char (point-max))
7330     (if (re-search-backward "^-- *$" nil t)
7331         (progn
7332           (add-text-properties (point) (point-max) '(invisible t))))))
7333
7334 (defun gnus-article-hide-citation ()
7335   "Hide all cited text.
7336 This function uses the famous, extremely intelligent \"shoot in foot\"
7337 algorithm - which is simply deleting all lines that start with
7338 \">\". Your mileage may vary. If you come up with anything better,
7339 please do mail it to me."
7340   (save-excursion
7341     (goto-char 1)
7342     (search-forward "\n\n" nil t)
7343     (while (not (eobp))
7344       (if (looking-at ">")
7345           (add-text-properties 
7346            (point) (save-excursion (forward-line 1) (point))
7347            '(invisible t)))
7348       (forward-line 1))))
7349
7350 ;; Written by Per Abrahamsen <amanda@iesd.auc.dk>.
7351 (defun gnus-article-treat-overstrike ()
7352   ;; Prepare article for overstrike commands.
7353   (save-excursion
7354     (set-buffer gnus-article-buffer)
7355     (let ((buffer-read-only nil))
7356       (while (search-forward "\b" nil t)
7357         (let ((next (following-char))
7358               (previous (char-after (- (point) 2))))
7359           (cond ((eq next previous)
7360                  (delete-region (- (point) 2) (point))
7361                  (put-text-property (point) (1+ (point))
7362                                     'face 'bold))
7363                 ((eq next ?_)
7364                  (delete-region (1- (point)) (1+ (point)))
7365                  (put-text-property (1- (point)) (point)
7366                                     'face 'underline))
7367                 ((eq previous ?_)
7368                  (delete-region (- (point) 2) (point))
7369                  (put-text-property (point) (1+ (point))
7370                                     'face 'underline))))))))
7371
7372 ;; Article savers.
7373
7374 (defun gnus-output-to-rmail (file-name)
7375   "Append the current article to an Rmail file named FILE-NAME."
7376   (require 'rmail)
7377   ;; Most of these codes are borrowed from rmailout.el.
7378   (setq file-name (expand-file-name file-name))
7379   (setq rmail-default-rmail-file file-name)
7380   (let ((artbuf (current-buffer))
7381         (tmpbuf (get-buffer-create " *Gnus-output*")))
7382     (save-excursion
7383       (or (get-file-buffer file-name)
7384           (file-exists-p file-name)
7385           (if (yes-or-no-p
7386                (concat "\"" file-name "\" does not exist, create it? "))
7387               (let ((file-buffer (create-file-buffer file-name)))
7388                 (save-excursion
7389                   (set-buffer file-buffer)
7390                   (rmail-insert-rmail-file-header)
7391                   (let ((require-final-newline nil))
7392                     (write-region (point-min) (point-max) file-name t 1)))
7393                 (kill-buffer file-buffer))
7394             (error "Output file does not exist")))
7395       (set-buffer tmpbuf)
7396       (buffer-disable-undo (current-buffer))
7397       (erase-buffer)
7398       (insert-buffer-substring artbuf)
7399       (gnus-convert-article-to-rmail)
7400       ;; Decide whether to append to a file or to an Emacs buffer.
7401       (let ((outbuf (get-file-buffer file-name)))
7402         (if (not outbuf)
7403             (append-to-file (point-min) (point-max) file-name)
7404           ;; File has been visited, in buffer OUTBUF.
7405           (set-buffer outbuf)
7406           (let ((buffer-read-only nil)
7407                 (msg (and (boundp 'rmail-current-message)
7408                           rmail-current-message)))
7409             ;; If MSG is non-nil, buffer is in RMAIL mode.
7410             (if msg
7411                 (progn (widen)
7412                        (narrow-to-region (point-max) (point-max))))
7413             (insert-buffer-substring tmpbuf)
7414             (if msg
7415                 (progn
7416                   (goto-char (point-min))
7417                   (widen)
7418                   (search-backward "\^_")
7419                   (narrow-to-region (point) (point-max))
7420                   (goto-char (1+ (point-min)))
7421                   (rmail-count-new-messages t)
7422                   (rmail-show-message msg))))))
7423       )
7424     (kill-buffer tmpbuf)
7425     ))
7426
7427 (defun gnus-output-to-file (file-name)
7428   "Append the current article to a file named FILE-NAME."
7429   (setq file-name (expand-file-name file-name))
7430   (let ((artbuf (current-buffer))
7431         (tmpbuf (get-buffer-create " *Gnus-output*")))
7432     (save-excursion
7433       (set-buffer tmpbuf)
7434       (buffer-disable-undo (current-buffer))
7435       (erase-buffer)
7436       (insert-buffer-substring artbuf)
7437       ;; Append newline at end of the buffer as separator, and then
7438       ;; save it to file.
7439       (goto-char (point-max))
7440       (insert "\n")
7441       (append-to-file (point-min) (point-max) file-name))
7442     (kill-buffer tmpbuf)
7443     ))
7444
7445 (defun gnus-convert-article-to-rmail ()
7446   "Convert article in current buffer to Rmail message format."
7447   (let ((buffer-read-only nil))
7448     ;; Convert article directly into Babyl format.
7449     ;; Suggested by Rob Austein <sra@lcs.mit.edu>
7450     (goto-char (point-min))
7451     (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
7452     (while (search-forward "\n\^_" nil t) ;single char
7453       (replace-match "\n^_"))           ;2 chars: "^" and "_"
7454     (goto-char (point-max))
7455     (insert "\^_")))
7456
7457 (defun gnus-narrow-to-page (&optional arg)
7458   "Make text outside current page invisible except for page delimiter.
7459 A numeric arg specifies to move forward or backward by that many pages,
7460 thus showing a page other than the one point was originally in."
7461   (interactive "P")
7462   (setq arg (if arg (prefix-numeric-value arg) 0))
7463   (save-excursion
7464     (forward-page -1)                   ;Beginning of current page.
7465     (widen)
7466     (if (> arg 0)
7467         (forward-page arg)
7468       (if (< arg 0)
7469           (forward-page (1- arg))))
7470     ;; Find the end of the page.
7471     (forward-page)
7472     ;; If we stopped due to end of buffer, stay there.
7473     ;; If we stopped after a page delimiter, put end of restriction
7474     ;; at the beginning of that line.
7475     ;; These are commented out.
7476     ;;    (if (save-excursion (beginning-of-line)
7477     ;;                  (looking-at page-delimiter))
7478     ;;  (beginning-of-line))
7479     (narrow-to-region (point)
7480                       (progn
7481                         ;; Find the top of the page.
7482                         (forward-page -1)
7483                         ;; If we found beginning of buffer, stay there.
7484                         ;; If extra text follows page delimiter on same line,
7485                         ;; include it.
7486                         ;; Otherwise, show text starting with following line.
7487                         (if (and (eolp) (not (bobp)))
7488                             (forward-line 1))
7489                         (point)))
7490     ))
7491
7492 (defun gnus-gmt-to-local ()
7493   "Rewrite Date: field described in GMT to local in current buffer.
7494 The variable gnus-local-timezone is used for local time zone.
7495 Intended to be used with gnus-article-prepare-hook."
7496   (save-excursion
7497     (save-restriction
7498       (widen)
7499       (goto-char (point-min))
7500       (narrow-to-region (point-min)
7501                         (progn (search-forward "\n\n" nil 'move) (point)))
7502       (goto-char (point-min))
7503       (if (re-search-forward "^Date:[ \t]\\(.*\\)$" nil t)
7504           (let ((buffer-read-only nil)
7505                 (date (buffer-substring (match-beginning 1) (match-end 1))))
7506             (delete-region (match-beginning 1) (match-end 1))
7507             (insert
7508              (timezone-make-date-arpa-standard date nil gnus-local-timezone))
7509             ))
7510       )))
7511
7512
7513 ;; Article mode commands
7514
7515 (defun gnus-article-next-page (lines)
7516   "Show next page of current article.
7517 If end of article, return non-nil. Otherwise return nil.
7518 Argument LINES specifies lines to be scrolled up."
7519   (interactive "P")
7520   (move-to-window-line -1)
7521   ;; Fixed by enami@ptgd.sony.co.jp (enami tsugutomo)
7522   (if (save-excursion
7523         (end-of-line)
7524         (and (pos-visible-in-window-p)  ;Not continuation line.
7525              (eobp)))
7526       ;; Nothing in this page.
7527       (if (or (not gnus-break-pages)
7528               (save-excursion
7529                 (save-restriction
7530                   (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer?
7531           t                             ;Nothing more.
7532         (gnus-narrow-to-page 1)         ;Go to next page.
7533         nil
7534         )
7535     ;; More in this page.
7536     (condition-case ()
7537         (scroll-up lines)
7538       (end-of-buffer
7539        ;; Long lines may cause an end-of-buffer error.
7540        (goto-char (point-max))))
7541     nil
7542     ))
7543
7544 (defun gnus-article-prev-page (lines)
7545   "Show previous page of current article.
7546 Argument LINES specifies lines to be scrolled down."
7547   (interactive "P")
7548   (move-to-window-line 0)
7549   (if (and gnus-break-pages
7550            (bobp)
7551            (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
7552       (progn
7553         (gnus-narrow-to-page -1) ;Go to previous page.
7554         (goto-char (point-max))
7555         (recenter -1))
7556     (scroll-down lines)))
7557
7558 (defun gnus-article-next-digest (nth)
7559   "Move to head of NTH next digested message.
7560 Set mark at end of digested message."
7561   ;; Stop page breaking in digest mode.
7562   (widen)
7563   (end-of-line)
7564   ;; Skip NTH - 1 digest.
7565   ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
7566   ;; Digest separator is customizable.
7567   ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
7568   (while (and (> nth 1)
7569               (re-search-forward gnus-digest-separator nil 'move))
7570     (setq nth (1- nth)))
7571   (if (re-search-forward gnus-digest-separator nil t)
7572       (let ((begin (point)))
7573         ;; Search for end of this message.
7574         (end-of-line)
7575         (if (re-search-forward gnus-digest-separator nil t)
7576             (progn
7577               (search-backward "\n\n")  ;This may be incorrect.
7578               (forward-line 1))
7579           (goto-char (point-max)))
7580         (push-mark)                     ;Set mark at end of digested message.
7581         (goto-char begin)
7582         (beginning-of-line)
7583         ;; Show From: and Subject: fields.
7584         (recenter 1))
7585     (message "End of message")
7586     ))
7587
7588 (defun gnus-article-prev-digest (nth)
7589   "Move to head of NTH previous digested message."
7590   ;; Stop page breaking in digest mode.
7591   (widen)
7592   (beginning-of-line)
7593   ;; Skip NTH - 1 digest.
7594   ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
7595   ;; Digest separator is customizable.
7596   ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
7597   (while (and (> nth 1)
7598               (re-search-backward gnus-digest-separator nil 'move))
7599     (setq nth (1- nth)))
7600   (if (re-search-backward gnus-digest-separator nil t)
7601       (let ((begin (point)))
7602         ;; Search for end of this message.
7603         (end-of-line)
7604         (if (re-search-forward gnus-digest-separator nil t)
7605             (progn
7606               (search-backward "\n\n")  ;This may be incorrect.
7607               (forward-line 1))
7608           (goto-char (point-max)))
7609         (push-mark)                     ;Set mark at end of digested message.
7610         (goto-char begin)
7611         ;; Show From: and Subject: fields.
7612         (recenter 1))
7613     (goto-char (point-min))
7614     (message "Top of message")
7615     ))
7616
7617 (defun gnus-article-refer-article ()
7618   "Read article specified by message-id around point."
7619   (interactive)
7620   (search-forward ">" nil t)    ;Move point to end of "<....>".
7621   (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
7622       (let ((message-id
7623              (buffer-substring (match-beginning 1) (match-end 1))))
7624         (set-buffer gnus-summary-buffer)
7625         (gnus-summary-refer-article message-id))
7626     (error "No references around point")))
7627
7628 (defun gnus-article-mail (yank)
7629   "Send a reply to the address near point.
7630 If YANK is non-nil, include the original article."
7631   (interactive "P")
7632   (let ((address 
7633          (buffer-substring
7634           (save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
7635           (save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
7636     (and address
7637          (progn
7638            (switch-to-buffer gnus-summary-buffer)
7639            (funcall gnus-mail-reply-method yank address)))))
7640
7641 (defun gnus-article-mail-with-original ()
7642   "Send a reply to the address near point and include the original article."
7643   (interactive)
7644   (gnus-article-mail 'yank))
7645
7646 (defun gnus-article-show-summary ()
7647   "Reconfigure windows to show Summary buffer."
7648   (interactive)
7649   (gnus-configure-windows 'article)
7650   (pop-to-buffer gnus-summary-buffer)
7651   (gnus-summary-goto-subject gnus-current-article))
7652
7653 (defun gnus-article-describe-briefly ()
7654   "Describe Article mode commands briefly."
7655   (interactive)
7656   (message
7657    (substitute-command-keys "\\[gnus-article-next-page]:Next page  \\[gnus-article-prev-page]:Prev page  \\[gnus-article-show-summary]:Show Summary  \\[gnus-info-find-node]:Run Info  \\[gnus-article-describe-briefly]:This help")))
7658
7659 ;; caesar-region written by phr@prep.ai.mit.edu  Nov 86
7660 ;; Modified by tower@prep Nov 86
7661 ;; Modified by umerin@flab.flab.Fujitsu.JUNET for ROT47.
7662
7663 (defun gnus-caesar-region (&optional n)
7664   "Caesar rotation of region by N, default 13, for decrypting netnews.
7665 ROT47 will be performed for Japanese text in any case."
7666   (interactive (if current-prefix-arg   ; Was there a prefix arg?
7667                    (list (prefix-numeric-value current-prefix-arg))
7668                  (list nil)))
7669   (cond ((not (numberp n)) (setq n 13))
7670         (t (setq n (mod n 26))))        ;canonicalize N
7671   (if (not (zerop n))           ; no action needed for a rot of 0
7672       (progn
7673         (if (or (not (boundp 'caesar-translate-table))
7674                 (not caesar-translate-table)
7675                 (/= (aref caesar-translate-table ?a) (+ ?a n)))
7676             (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
7677               (message "Building caesar-translate-table...")
7678               (setq caesar-translate-table (make-vector 256 0))
7679               (while (< i 256)
7680                 (aset caesar-translate-table i i)
7681                 (setq i (1+ i)))
7682               (setq lower (concat lower lower) upper (upcase lower) i 0)
7683               (while (< i 26)
7684                 (aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
7685                 (aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
7686                 (setq i (1+ i)))
7687               ;; ROT47 for Japanese text.
7688               ;; Thanks to ichikawa@flab.fujitsu.junet.
7689               (setq i 161)
7690               (let ((t1 (logior ?O 128))
7691                     (t2 (logior ?! 128))
7692                     (t3 (logior ?~ 128)))
7693                 (while (< i 256)
7694                   (aset caesar-translate-table i
7695                         (let ((v (aref caesar-translate-table i)))
7696                           (if (<= v t1) (if (< v t2) v (+ v 47))
7697                             (if (<= v t3) (- v 47) v))))
7698                   (setq i (1+ i))))
7699               (message "Building caesar-translate-table... done")))
7700         (let ((from (region-beginning))
7701               (to (region-end))
7702               (i 0) str len)
7703           (setq str (buffer-substring from to))
7704           (setq len (length str))
7705           (while (< i len)
7706             (aset str i (aref caesar-translate-table (aref str i)))
7707             (setq i (1+ i)))
7708           (goto-char from)
7709           (delete-region from to)
7710           (insert str)))))
7711
7712 \f
7713 ;;;
7714 ;;; Gnus KILL-File Mode
7715 ;;;
7716
7717 (if gnus-kill-file-mode-map
7718     nil
7719   (setq gnus-kill-file-mode-map (copy-keymap emacs-lisp-mode-map))
7720   (define-key gnus-kill-file-mode-map "\C-c\C-x"
7721     'gnus-kill-file-set-expunge-below)
7722   (define-key gnus-kill-file-mode-map "\C-c\C-m"
7723     'gnus-kill-file-set-mark-below)
7724   (define-key gnus-kill-file-mode-map "\C-c\C-k\C-s"
7725     'gnus-kill-file-temporarily-lower-by-subject)
7726   (define-key gnus-kill-file-mode-map "\C-c\C-k\C-a"
7727     'gnus-kill-file-temporarily-lower-by-author)
7728   (define-key gnus-kill-file-mode-map "\C-c\C-k\C-x"
7729     'gnus-kill-file-temporarily-lower-by-xref)
7730   (define-key gnus-kill-file-mode-map "\C-c\C-ks"
7731     'gnus-kill-file-lower-by-subject)
7732   (define-key gnus-kill-file-mode-map "\C-c\C-ka"
7733     'gnus-kill-file-lower-by-author)
7734   (define-key gnus-kill-file-mode-map "\C-c\C-kt"
7735     'gnus-kill-file-lower-by-thread)
7736   (define-key gnus-kill-file-mode-map "\C-c\C-kx"
7737     'gnus-kill-file-lower-by-xref)
7738   (define-key gnus-kill-file-mode-map "\C-c\C-kf"
7739     'gnus-kill-file-lower-followups-to-author)
7740   (define-key gnus-kill-file-mode-map "\C-c\C-i\C-s"
7741     'gnus-kill-file-temporarily-raise-by-subject)
7742   (define-key gnus-kill-file-mode-map "\C-c\C-i\C-a"
7743     'gnus-kill-file-temporarily-raise-by-author)
7744   (define-key gnus-kill-file-mode-map "\C-c\C-i\C-t"
7745     'gnus-kill-file-temporarily-raise-by-thread)
7746   (define-key gnus-kill-file-mode-map "\C-c\C-i\C-x"
7747     'gnus-kill-file-temporarily-raise-by-xref)
7748   (define-key gnus-kill-file-mode-map "\C-c\C-is"
7749     'gnus-kill-file-raise-by-subject)
7750   (define-key gnus-kill-file-mode-map "\C-c\C-ia"
7751     'gnus-kill-file-raise-by-author)
7752   (define-key gnus-kill-file-mode-map "\C-c\C-ix"
7753     'gnus-kill-file-raise-by-xref)
7754   (define-key gnus-kill-file-mode-map "\C-c\C-if"
7755     'gnus-kill-file-raise-followups-to-author)
7756   (define-key gnus-kill-file-mode-map "\C-c\C-a" 'gnus-kill-file-apply-buffer)
7757   (define-key gnus-kill-file-mode-map "\C-c\C-e" 'gnus-kill-file-apply-last-sexp)
7758   (define-key gnus-kill-file-mode-map "\C-c\C-c" 'gnus-kill-file-exit)
7759   (define-key gnus-kill-file-mode-map "\C-c\C-i" 'gnus-info-find-node))
7760
7761 (defun gnus-kill-file-mode ()
7762   "Major mode for editing KILL file.
7763
7764 In addition to Emacs-Lisp Mode, the following commands are available:
7765
7766 \\[gnus-kill-file-set-expunge-below]    Automatically expunge articles below LEVEL.
7767 \\[gnus-kill-file-set-mark-below]       Automatically mark articles below LEVEL.
7768 \\[gnus-kill-file-temporarily-lower-by-author]  Insert temporary lower command for current author.
7769 \\[gnus-kill-file-temporarily-lower-by-thread]  Insert temporary lower command for current thread.
7770 \\[gnus-kill-file-temporarily-lower-by-xref]            Insert temporary lower command for current cross-posting.
7771 \\[gnus-kill-file-lower-by-subject]     Insert permanent lower command for current subject.
7772 \\[gnus-kill-file-lower-by-author]      Insert permanent lower command for current author.
7773 \\[gnus-kill-file-lower-followups-to-author]    Insert permanent lower command for followups to the current author.
7774 \\[gnus-kill-file-lower-by-xref]                Insert permanent lower command for current cross-posting.
7775 \\[gnus-kill-file-temporarily-raise-by-subject] Insert temporary raise command for current subject.
7776 \\[gnus-kill-file-temporarily-raise-by-author]  Insert temporary raise command for current author.
7777 \\[gnus-kill-file-temporarily-raise-by-thread]  Insert temporary raise command for current thread.
7778 \\[gnus-kill-file-temporarily-raise-by-xref]            Insert temporary raise command for current cross-posting.
7779 \\[gnus-kill-file-raise-by-subject]     Insert permanent raise command for current subject.
7780 \\[gnus-kill-file-raise-by-author]      Insert permanent raise command for current author.
7781 \\[gnus-kill-file-raise-followups-to-author]    Insert permanent raise command for followups to the current author.
7782 \\[gnus-kill-file-raise-by-xref]                Insert permanent raise command for current cross-posting.
7783 \\[gnus-kill-file-apply-buffer] Apply current buffer to selected newsgroup.
7784 \\[gnus-kill-file-apply-last-sexp]      Apply sexp before point to selected newsgroup.
7785 \\[gnus-kill-file-exit] Save file and exit editing KILL file.
7786 \\[gnus-info-find-node] Read Info about KILL file.
7787
7788   A KILL file contains Lisp expressions to be applied to a selected
7789 newsgroup.  The purpose is to mark articles as read on the basis of
7790 some set of regexps.  A global KILL file is applied to every newsgroup,
7791 and a local KILL file is applied to a specified newsgroup.  Since a
7792 global KILL file is applied to every newsgroup, for better performance
7793 use a local one.
7794
7795   A KILL file can contain any kind of Emacs Lisp expressions expected
7796 to be evaluated in the Summary buffer.  Writing Lisp programs for this
7797 purpose is not so easy because the internal working of Gnus must be
7798 well-known.  For this reason, Gnus provides a general function which
7799 does this easily for non-Lisp programmers.
7800
7801   The `gnus-kill' function executes commands available in Summary Mode
7802 by their key sequences. `gnus-kill' should be called with FIELD,
7803 REGEXP and optional COMMAND and ALL.  FIELD is a string representing
7804 the header field or an empty string.  If FIELD is an empty string, the
7805 entire article body is searched for.  REGEXP is a string which is
7806 compared with FIELD value. COMMAND is a string representing a valid
7807 key sequence in Summary mode or Lisp expression. COMMAND defaults to
7808 '(gnus-summary-mark-as-read nil \"X\").  Make sure that COMMAND is
7809 executed in the Summary buffer.  If the second optional argument ALL
7810 is non-nil, the COMMAND is applied to articles which are already
7811 marked as read or unread.  Articles which are marked are skipped over
7812 by default.
7813
7814   For example, if you want to mark articles of which subjects contain
7815 the string `AI' as read, a possible KILL file may look like:
7816
7817         (gnus-kill \"Subject\" \"AI\")
7818
7819   If you want to mark articles with `D' instead of `X', you can use
7820 the following expression:
7821
7822         (gnus-kill \"Subject\" \"AI\" \"d\")
7823
7824 In this example it is assumed that the command
7825 `gnus-summary-mark-as-read-forward' is assigned to `d' in Summary Mode.
7826
7827   It is possible to delete unnecessary headers which are marked with
7828 `X' in a KILL file as follows:
7829
7830         (gnus-expunge \"X\")
7831
7832   If the Summary buffer is empty after applying KILL files, Gnus will
7833 exit the selected newsgroup normally.  If headers which are marked
7834 with `D' are deleted in a KILL file, it is impossible to read articles
7835 which are marked as read in the previous Gnus sessions.  Marks other
7836 than `D' should be used for articles which should really be deleted.
7837
7838 Entry to this mode calls emacs-lisp-mode-hook and
7839 gnus-kill-file-mode-hook with no arguments, if that value is non-nil."
7840   (interactive)
7841   (kill-all-local-variables)
7842   (use-local-map gnus-kill-file-mode-map)
7843   (set-syntax-table emacs-lisp-mode-syntax-table)
7844   (setq major-mode 'gnus-kill-file-mode)
7845   (setq mode-name "KILL-File")
7846   (lisp-mode-variables nil)
7847   (run-hooks 'emacs-lisp-mode-hook 'gnus-kill-file-mode-hook))
7848
7849 (defun gnus-kill-file-edit-file (newsgroup)
7850   "Begin editing a KILL file of NEWSGROUP.
7851 If NEWSGROUP is nil, the global KILL file is selected."
7852   (interactive "sNewsgroup: ")
7853   (let ((file (gnus-newsgroup-kill-file newsgroup)))
7854     (gnus-make-directory (file-name-directory file))
7855     ;; Save current window configuration if this is first invocation.
7856     (or (and (get-file-buffer file)
7857              (get-buffer-window (get-file-buffer file)))
7858         (setq gnus-winconf-kill-file (current-window-configuration)))
7859     ;; Hack windows.
7860     (let ((buffer (find-file-noselect file)))
7861       (cond ((get-buffer-window buffer)
7862              (pop-to-buffer buffer))
7863             ((eq major-mode 'gnus-group-mode)
7864              (gnus-configure-windows '(1 0 0)) ;Take all windows.
7865              (pop-to-buffer gnus-group-buffer)
7866              (let ((gnus-summary-buffer buffer))
7867                (gnus-configure-windows '(1 1 0)) ;Split into two.
7868                (pop-to-buffer buffer)))
7869             ((eq major-mode 'gnus-summary-mode)
7870              (gnus-configure-windows 'article)
7871              (pop-to-buffer gnus-article-buffer)
7872              (bury-buffer gnus-article-buffer)
7873              (switch-to-buffer buffer))
7874             (t                          ;No good rules.
7875              (find-file-other-window file))
7876             ))
7877     (gnus-kill-file-mode)
7878     ))
7879
7880 (defun gnus-kill-set-kill-buffer ()
7881   (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
7882     (if (get-file-buffer file)
7883         (set-buffer (get-file-buffer file))
7884       (set-buffer (find-file-noselect file))
7885       (bury-buffer))))
7886
7887 (defun gnus-kill-save-kill-buffer ()
7888   (save-excursion
7889     (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
7890       (if (get-file-buffer file)
7891           (progn
7892             (set-buffer (get-file-buffer file))
7893             (if (buffer-modified-p)
7894                 (save-buffer))
7895             (kill-buffer (current-buffer)))))))
7896
7897 (defun gnus-article-fetch-field (field)
7898   (save-excursion
7899     (set-buffer gnus-article-buffer)
7900     (save-restriction
7901       (widen)
7902       (goto-char 1)
7903       (narrow-to-region 1 (save-excursion 
7904                             (search-forward "\n\n" nil t) (point)))
7905       (goto-char 1)
7906       (prog1
7907           (mail-fetch-field field)
7908         (widen)))))
7909
7910  (defun gnus-kill-file-enter-kill (field regexp level date edit)
7911    ;; Enter kill file entry.
7912    ;; FIELD: String containing the name of the header field to kill.
7913    ;; REGEXP: The string to kill.
7914    ;; LEVEL: How much to raise the score by.
7915    ;; DATE: A date string for expire kill or nil for permanent kills.
7916    ;; EDIT: Allow the user to edit REGEXP iff non-nil.
7917     (save-excursion
7918       (gnus-kill-set-kill-buffer)
7919       (goto-char (point-min))
7920      (let ((regexp (if edit
7921                       (read-string (concat "Add " level " to articles with "
7922                                            (downcase field) " matching: ")
7923                                    (cons regexp 1))
7924                     regexp)) 
7925           entry string kill beg)
7926        (setq entry (if date (cons regexp date) regexp)
7927             string (format "(gnus-raise %S (quote %S) %S)\n"
7928                            field entry level))
7929        (while (and (setq beg (point))
7930                   (condition-case nil
7931                       (setq kill (read (current-buffer)))
7932                     (error nil))
7933                   (or (not (eq (nth 0 kill) 'gnus-raise))
7934                       (not (string= (downcase (nth 1 kill)) (downcase field)))
7935                       (not (eq (nth 3 kill) level))))
7936          (setq kill nil))
7937         (if (not kill)
7938           (progn
7939             (goto-char (point-min))
7940             (insert string))
7941         (let ((list (nth 2 kill)))
7942           (if (and (listp list) (eq 'quote (car list)))
7943               (setq list (car (cdr list))))
7944           (setcar 
7945            (nthcdr 2 kill) 
7946            (if (and (listp list) (listp (cdr list)))
7947                (list 'quote (cons entry list))
7948              (list 'quote (list entry list)))))
7949         (delete-region beg (point))
7950         (insert (gnus-pp-gnus-kill kill)))
7951        (gnus-kill-file-apply-string string))
7952       ;; Added by by Sudish Joseph <joseph@cis.ohio-state.edu>.
7953      (or edit 
7954         (message "Added kill file entry %s: %s" (downcase field) regexp))))
7955     
7956  (defun gnus-kill-file-set-variable (symbol value)
7957    ;; Set SYMBOL to VALUE in the kill file.
7958     (save-excursion
7959       (gnus-kill-set-kill-buffer)
7960       (goto-char (point-min))
7961      (let ((string (format "(setq %S %S)\n" symbol value))
7962           kill beg)
7963        (while (and (setq beg (point))
7964                   (condition-case nil
7965                       (setq kill (read (current-buffer)))
7966                     (error nil))
7967                   (or (not (eq (nth 0 kill) 'setq))
7968                       (not (eq (nth 1 kill) symbol))))
7969         (setq kill nil))
7970         (if (not kill)
7971           (progn
7972             (goto-char (point-min))
7973             (insert string))
7974         (delete-region beg (point))
7975         (insert string)))))
7976     
7977  (defun gnus-kill-file-set-expunge-below (level)
7978    "Automatically expunge articles with score below LEVEL."
7979    (interactive "P")
7980    (setq level (if level
7981                   (prefix-numeric-value level)
7982                 gnus-summary-default-interest))
7983    (gnus-kill-file-set-variable 'expunge-below level)
7984    (message "Set expunge below level to %d." level))
7985   
7986  (defun gnus-kill-file-set-mark-below (level)
7987    "Automatically mark articles with score below LEVEL as killed."
7988    (interactive "P")
7989    (setq level (if level
7990                   (prefix-numeric-value level)
7991                 gnus-summary-default-interest))
7992    (gnus-kill-file-set-variable 'mark-below level)
7993    (message "Set mark below level to %d." level))
7994  
7995  (defun gnus-kill-file-temporarily-raise-by-subject (level &optional header)
7996      "Temporarily raise score by LEVEL for current subject.
7997  See `gnus-kill-expiry-days'."
7998    (interactive "p")
7999    (gnus-kill-file-raise-by-subject level header (current-time-string)))
8000   
8001  (defun gnus-kill-file-temporarily-raise-by-author (level &optional header)
8002    "Temporarily raise score by LEVEL for current author.
8003  See `gnus-kill-expiry-days'."
8004    (interactive "p")
8005    (gnus-kill-file-raise-by-author level header (current-time-string)))
8006   
8007  (defun gnus-kill-file-temporarily-raise-by-thread (level &optional header)
8008    "Temporarily raise score by LEVEL for current thread.
8009  See `gnus-kill-expiry-days'."
8010    (interactive "p")
8011    (gnus-kill-file-enter-kill 
8012     "References"
8013     (regexp-quote (header-id (or header gnus-current-headers)))
8014     level
8015     (current-time-string)
8016     nil))
8017   
8018  (defun gnus-kill-file-temporarily-raise-by-xref (level &optional header)
8019    "Insert temporary KILL commands for articles that have been crossposted.
8020  By default use the current crossposted groups.
8021  See `gnus-kill-expiry-days'."
8022    (interactive "p")
8023    (gnus-kill-file-raise-by-xref level header (current-time-string)))
8024   
8025  (defun gnus-kill-file-raise-by-subject (level &optional header date)
8026      "Raise score by LEVEL for current subject."
8027    (interactive "p")
8028    (gnus-kill-file-enter-kill
8029     "Subject"
8030     (regexp-quote 
8031      (gnus-simplify-subject 
8032       (header-subject (or header gnus-current-headers))))
8033     level
8034     date
8035     t))
8036   
8037  (defun gnus-kill-file-raise-by-author (level &optional header date)
8038    "Raise score by LEVEL for current author."
8039    (interactive "p")
8040    (gnus-kill-file-enter-kill
8041     "From"
8042     (regexp-quote (header-from (or header gnus-current-headers)))
8043     level
8044     date
8045     t))
8046  
8047  (defun gnus-kill-file-raise-by-xref (level &optional header date)
8048    "Raise score by LEVEL for articles that have been crossposted.
8049  By default use the current crossposted groups."
8050    (interactive "p")
8051     (let ((xref (header-xref (or header gnus-current-headers)))
8052         (start 0)
8053         group)
8054     (if xref
8055         (while (string-match " \\([^ \t]+\\):" xref start)
8056           (setq start (match-end 0))
8057           (if (not (string= 
8058                     (setq group 
8059                           (substring xref (match-beginning 1) (match-end 1)))
8060                     gnus-newsgroup-name))
8061               (gnus-kill-file-enter-kill 
8062                "Xref"
8063                (concat " " (regexp-quote group) ":")
8064                level
8065                date
8066                t))))))
8067
8068 (defun gnus-kill-file-raise-followups-to-author
8069   (level &optional header)
8070   "Raise score for all followups to the current author."
8071   (interactive)
8072   (let ((name (header-from (or header gnus-current-headers)))
8073         (string))
8074     (save-excursion
8075       (gnus-kill-set-kill-buffer)
8076       (goto-char (point-min))
8077       (setq name (read-string (concat "Add " level
8078                                       " to followup articles to: ")
8079                               (regexp-quote name)))
8080       (setq string
8081             (format "(gnus-kill %S %S '(gnus-summary-temporarily-raise-by-thread %S))\n"
8082                     "From" name level))
8083       (insert string)
8084       (gnus-kill-file-apply-string string))
8085     (message "Added permanent kill file entry for followups to %s." name)))
8086
8087 (defun gnus-kill-file-temporarily-lower-by-subject (level &optional header)
8088     "Temporarily lower score by LEVEL for current subject.
8089 See `gnus-kill-expiry-days'."
8090   (interactive "p")
8091   (gnus-kill-file-lower-by-subject level header (current-time-string)))
8092
8093 (defun gnus-kill-file-temporarily-lower-by-author (level &optional header)
8094   "Temporarily lower score by LEVEL for current author.
8095 See `gnus-kill-expiry-days'."
8096   (interactive "p")
8097   (gnus-kill-file-lower-by-author level header (current-time-string)))
8098
8099 (defun gnus-kill-file-temporarily-lower-by-thread (level &optional header)
8100   "Temporarily lower score by LEVEL for current thread.
8101 See `gnus-kill-expiry-days'."
8102   (interactive "p")
8103   (gnus-kill-file-temporarily-raise-by-thread (- level) header))
8104
8105 (defun gnus-kill-file-temporarily-lower-by-xref (level &optional header)
8106   "Insert temporary KILL commands for articles that have been crossposted.
8107 By default use the current crossposted groups.
8108 See `gnus-kill-expiry-days'."
8109   (interactive "p")
8110   (gnus-kill-file-lower-by-xref level header (current-time-string)))
8111
8112 (defun gnus-kill-file-lower-by-subject (level &optional header date)
8113     "Lower score by LEVEL for current subject."
8114   (interactive "p")
8115   (gnus-kill-file-raise-by-subject (- level) header date))
8116
8117 (defun gnus-kill-file-lower-by-author (level &optional header date)
8118   "Lower score by LEVEL for current author."
8119   (interactive "p")
8120   (gnus-kill-file-raise-by-author (- level) header date))
8121
8122 (defun gnus-kill-file-lower-by-xref (level &optional header date)
8123   "Lower score by LEVEL for articles that have been crossposted.
8124 By default use the current crossposted groups."
8125   (gnus-kill-file-raise-by-xref (- level) header date))
8126
8127 (defun gnus-kill-file-lower-followups-to-author
8128   (level &optional header)
8129   "Lower score for all followups to the current author."
8130   (interactive "p")
8131   (gnus-kill-file-raise-followups-to-author (- level) header))
8132
8133 (defun gnus-kill-file-apply-buffer ()
8134   "Apply current buffer to current newsgroup."
8135   (interactive)
8136   (if (and gnus-current-kill-article
8137            (get-buffer gnus-summary-buffer))
8138       ;; Assume newsgroup is selected.
8139       (gnus-kill-file-apply-string (buffer-string))
8140     (ding) (message "No newsgroup is selected.")))
8141
8142 (defun gnus-kill-file-apply-string (string)
8143   "Apply STRING to current newsgroup."
8144   (interactive)
8145   (let ((string (concat "(progn \n" string "\n)" )))
8146     (save-excursion
8147       (save-window-excursion
8148         (pop-to-buffer gnus-summary-buffer)
8149         (eval (car (read-from-string string)))))))
8150
8151 (defun gnus-kill-file-apply-last-sexp ()
8152   "Apply sexp before point in current buffer to current newsgroup."
8153   (interactive)
8154   (if (and gnus-current-kill-article
8155            (get-buffer gnus-summary-buffer))
8156       ;; Assume newsgroup is selected.
8157       (let ((string
8158              (buffer-substring
8159               (save-excursion (forward-sexp -1) (point)) (point))))
8160         (save-excursion
8161           (save-window-excursion
8162             (pop-to-buffer gnus-summary-buffer)
8163             (eval (car (read-from-string string))))))
8164     (ding) (message "No newsgroup is selected.")))
8165
8166 (defun gnus-kill-file-exit ()
8167   "Save a KILL file, then return to the previous buffer."
8168   (interactive)
8169   (save-buffer)
8170   (let ((killbuf (current-buffer)))
8171     ;; We don't want to return to Article buffer.
8172     (and (get-buffer gnus-article-buffer)
8173          (bury-buffer gnus-article-buffer))
8174     ;; Delete the KILL file windows.
8175     (delete-windows-on killbuf)
8176     ;; Restore last window configuration if available.
8177     (and gnus-winconf-kill-file
8178          (set-window-configuration gnus-winconf-kill-file))
8179     (setq gnus-winconf-kill-file nil)
8180     ;; Kill the KILL file buffer.  Suggested by tale@pawl.rpi.edu.
8181     (kill-buffer killbuf)))
8182
8183 ;; Basic ideas by emv@math.lsa.umich.edu (Edward Vielmetti)
8184
8185 (defun gnus-batch-kill ()
8186   "Run batched KILL.
8187 Usage: emacs -batch -l gnus -f gnus-batch-kill NEWSGROUP ..."
8188   (if (not noninteractive)
8189       (error "gnus-batch-kill is to be used only with -batch"))
8190   (let* ((group nil)
8191          (subscribed nil)
8192          (newsrc nil)
8193          (yes-and-no
8194           (gnus-parse-n-options
8195            (apply (function concat)
8196                   (mapcar (lambda (g) (concat g " "))
8197                           command-line-args-left))))
8198          (yes (car yes-and-no))
8199          (no  (cdr yes-and-no))
8200          ;; Disable verbose message.
8201          (gnus-novice-user nil)
8202          (gnus-large-newsgroup nil))
8203     ;; Eat all arguments.
8204     (setq command-line-args-left nil)
8205     ;; Startup Gnus.
8206     (gnus)
8207     ;; Apply kills to specified newsgroups in command line arguments.
8208     (setq newsrc (copy-sequence gnus-newsrc-assoc))
8209     (while newsrc
8210       (setq group (car (car newsrc)))
8211       (setq subscribed (nth 1 (car newsrc)))
8212       (setq newsrc (cdr newsrc))
8213       (if (and subscribed
8214                (not (zerop (car (gnus-gethash group gnus-newsrc-hashtb))))
8215                (if yes
8216                    (string-match yes group) t)
8217                (or (null no)
8218                    (not (string-match no group))))
8219           (progn
8220             (gnus-summary-read-group group nil t)
8221             (if (eq (current-buffer) (get-buffer gnus-summary-buffer))
8222                 (gnus-summary-exit t))
8223             ))
8224       )
8225     ;; Finally, exit Emacs.
8226     (set-buffer gnus-group-buffer)
8227     (gnus-group-exit)
8228     ))
8229
8230 ;; For KILL files
8231
8232 (defun gnus-Newsgroup-kill-file (newsgroup)
8233   "Return the name of a KILL file of NEWSGROUP.
8234 If NEWSGROUP is nil, return the global KILL file instead."
8235   (cond ((or (null newsgroup)
8236              (string-equal newsgroup ""))
8237          ;; The global KILL file is placed at top of the directory.
8238          (expand-file-name gnus-kill-file-name
8239                            (or gnus-kill-files-directory "~/News")))
8240         (gnus-use-long-file-name
8241          ;; Append ".KILL" to capitalized newsgroup name.
8242          (expand-file-name (concat (gnus-capitalize-newsgroup newsgroup)
8243                                    "." gnus-kill-file-name)
8244                            (or gnus-kill-files-directory "~/News")))
8245         (t
8246          ;; Place "KILL" under the hierarchical directory.
8247          (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
8248                                    "/" gnus-kill-file-name)
8249                            (or gnus-kill-files-directory "~/News")))
8250         ))
8251
8252 (defun gnus-newsgroup-kill-file (newsgroup)
8253   "Return the name of a KILL file of NEWSGROUP.
8254 If NEWSGROUP is nil, return the global KILL file instead."
8255   (cond ((or (null newsgroup)
8256              (string-equal newsgroup ""))
8257          ;; The global KILL file is placed at top of the directory.
8258          (expand-file-name gnus-kill-file-name
8259                            (or gnus-kill-files-directory "~/News")))
8260         (gnus-use-long-file-name
8261          ;; Append ".KILL" to newsgroup name.
8262          (expand-file-name (concat newsgroup "." gnus-kill-file-name)
8263                            (or gnus-kill-files-directory "~/News")))
8264         (t
8265          ;; Place "KILL" under the hierarchical directory.
8266          (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
8267                                    "/" gnus-kill-file-name)
8268                            (or gnus-kill-files-directory "~/News")))
8269         ))
8270
8271
8272 (defalias 'gnus-expunge 'gnus-summary-delete-marked-with)
8273
8274 (defun gnus-apply-kill-file ()
8275   "Apply KILL file to the current newsgroup.
8276 Returns the number of articles killed."
8277   (let ((kill-files (list (gnus-newsgroup-kill-file nil)
8278                           (gnus-newsgroup-kill-file gnus-newsgroup-name)))
8279         (unreads (length gnus-newsgroup-unreads))
8280         (mark-below gnus-summary-default-interest)
8281         (gnus-summary-inhibit-highlight t)
8282         (expunge-below nil)
8283         form beg)
8284     (save-excursion
8285       (while kill-files
8286         (if (file-exists-p (car kill-files))
8287             (progn
8288               (find-file (car kill-files))
8289               (goto-char (point-min))
8290               (while (progn
8291                        (setq beg (point))
8292                        (setq form (condition-case nil 
8293                                       (read (current-buffer)) (error nil))))
8294                 (if (eq (car form) 'gnus-kill)
8295                     (progn
8296                       (delete-region beg (point))
8297                       (insert (or (eval form) "")))
8298                   (eval form)))
8299               (save-buffer)))
8300         (setq kill-files (cdr kill-files))))
8301     (if expunge-below (gnus-summary-expunge-below expunge-below))
8302     (if mark-below (gnus-summary-mark-below mark-below ?X) )
8303     (let (gnus-summary-inhibit-highlight)
8304       (gnus-summary-update-lines))
8305     (if beg
8306         (let ((nunreads (- unreads (length gnus-newsgroup-unreads))))
8307           (or (eq nunreads 0)
8308               (message "Killed %d articles" nunreads))
8309           nunreads)
8310       0)))
8311
8312 ;; Kill changes and new format by suggested by JWZ and Sudish Joseph
8313 ;; <joseph@cis.ohio-state.edu>.  
8314 (defun gnus-kill (field regexp &optional exe-command all)
8315   "If FIELD of an article matches REGEXP, execute COMMAND.
8316 Optional 1st argument COMMAND is default to
8317         (gnus-summary-mark-as-read nil \"X\").
8318 If optional 2nd argument ALL is non-nil, articles marked are also applied to.
8319 If FIELD is an empty string (or nil), entire article body is searched for.
8320 COMMAND must be a lisp expression or a string representing a key sequence."
8321   ;; We don't want to change current point nor window configuration.
8322   (save-excursion
8323     (save-window-excursion
8324       ;; Selected window must be Summary buffer to execute keyboard
8325       ;; macros correctly. See command_loop_1.
8326       (switch-to-buffer gnus-summary-buffer 'norecord)
8327       (goto-char (point-min))           ;From the beginning.
8328       (let ((kill-list regexp)
8329             (date (current-time-string))
8330             (command (or exe-command '(gnus-summary-mark-as-read 
8331                                        nil gnus-kill-file-mark)))
8332             kill kdate prev)
8333         (if (listp kill-list)
8334             ;; It is a list.
8335             (if (not (consp (cdr kill-list)))
8336                 ;; It's on the form (regexp . date).
8337                 (if (= 0 (gnus-execute field (car kill-list) 
8338                                        command nil (not all)))
8339                     (if (> (gnus-days-between (cdr kill-list) date)
8340                            gnus-kill-expiry-days)
8341                         (setq regexp nil))
8342                   (setcdr kill-list date))
8343               (while (setq kill (car kill-list))
8344                 (if (consp kill)
8345                     ;; It's a temporary kill.
8346                     (progn
8347                       (setq kdate (cdr kill))
8348                       (if (= 0 (gnus-execute field (car kill) command 
8349                                              nil (not all)))
8350                           (if (> (gnus-days-between kdate date)
8351                                  gnus-kill-expiry-days)
8352                               ;; Time limit has been exceeded, so we
8353                               ;; remove the match.
8354                               (if prev
8355                                   (setcdr prev (cdr kill-list))
8356                                 (setq regexp (cdr regexp))))
8357                         ;; Successful kill. Set the date to today.
8358                         (setcdr kill date)))
8359                   ;; It's a permanent kill.
8360                   (gnus-execute field kill command nil (not all)))
8361                 (setq prev kill-list)
8362                 (setq kill-list (cdr kill-list))))
8363           (gnus-execute field kill-list command nil (not all)))
8364         )))
8365   (if regexp
8366       (gnus-pp-gnus-kill
8367        (nconc (list 'gnus-kill field 
8368                     (if (consp regexp) (list 'quote regexp) regexp))
8369               (if (or exe-command all) (list (list 'quote exe-command)))
8370               (if all (list t) nil)))))
8371
8372 (defun gnus-pp-gnus-kill (object)
8373   (if (or (not (consp (nth 2 object)))
8374           (not (consp (cdr (nth 2 object))))
8375           (and (eq 'quote (car (nth 2 object)))
8376                (not (consp (cdr (car (cdr (nth 2 object))))))))
8377       (concat "\n" (prin1-to-string object))
8378     (save-excursion
8379       (set-buffer (get-buffer-create "*Gnus PP*"))
8380       (buffer-disable-undo (current-buffer))
8381       (erase-buffer)
8382       (insert (format "\n(gnus-kill %S\n  '(" (nth 1 object)))
8383       (let ((klist (car (cdr (nth 2 object))))
8384             (first t))
8385         (while klist
8386           (insert (if first (progn (setq first nil) "")  "\n   ")
8387                   (prin1-to-string (car klist)))
8388           (setq klist (cdr klist))))
8389       (insert ")")
8390       (if (nth 3 object)
8391           (insert "\n  '" (prin1-to-string (nth 3 object))))
8392       (if (nth 4 object)
8393           (insert "\n  t"))
8394       (insert ")")
8395       (prog1
8396           (buffer-substring (point-min) (point-max))
8397         (kill-buffer (current-buffer))))))
8398
8399 (defun gnus-days-between (date1 date2)
8400   ;; Return the number of days between date1 and date2.
8401   (let ((d1 (mapcar (lambda (s) (and s (string-to-int s)) )
8402                     (timezone-parse-date date1)))
8403         (d2 (mapcar (lambda (s) (and s (string-to-int s)) )
8404                     (timezone-parse-date date2))))
8405     (- (timezone-absolute-from-gregorian 
8406         (nth 1 d1) (nth 2 d1) (car d1))
8407        (timezone-absolute-from-gregorian 
8408         (nth 1 d2) (nth 2 d2) (car d2)))))
8409
8410 (defun gnus-execute (field regexp form &optional backward ignore-marked)
8411   "If FIELD of article header matches REGEXP, execute lisp FORM (or a string).
8412 If FIELD is an empty string (or nil), entire article body is searched for.
8413 If optional 1st argument BACKWARD is non-nil, do backward instead.
8414 If optional 2nd argument IGNORE-MARKED is non-nil, articles which are
8415 marked as read or ticked are ignored."
8416   (save-excursion
8417     (let ((killed-no 0)
8418           function header article)
8419       (if (or (null field) (string-equal field ""))
8420           (setq field nil)
8421         ;; Get access function of header filed.
8422         (setq function (intern-soft (concat "gnus-header-" (downcase field))))
8423         (if (and function (fboundp function))
8424             (setq function (symbol-function function))
8425           (error "Unknown header field: \"%s\"" field))
8426         ;; The function is a macro, so we have to check whether is has
8427         ;; been compiled or not, and make a real function out of it. 
8428         ;;      (if (consp (cdr function))
8429         ;;        (setq function (nth 3 function))
8430         ;;      (setq function (list 'byte-code (aref function 1) (aref function 2)
8431         ;;                           (aref function 3)))))
8432         ;; Make FORM funcallable.
8433         (if (and (listp form) (not (eq (car form) 'lambda)))
8434             (setq form (list 'lambda nil form)))
8435         ;; Starting from the current article.
8436         (while (or (not article) ; Do the first line.
8437                    (gnus-summary-search-subject backward ignore-marked))
8438           (setq article (gnus-summary-article-number))
8439           (or (gnus-member-of-range article gnus-newsgroup-killed)
8440               (and ignore-marked
8441                    ;; Articles marked as read, ticked and dormant
8442                    ;; should be ignored. 
8443                    (or (not (memq article gnus-newsgroup-unreads))
8444                        (memq article gnus-newsgroup-marked)
8445                        (memq article gnus-newsgroup-dormant)))
8446               (gnus-execute-1 function regexp form article) 
8447               (setq killed-no (1+ killed-no)))))
8448       killed-no)))
8449
8450 (defun gnus-execute-1 (function regexp form article)
8451   (save-excursion
8452     (let (did-kill)
8453       (if (null article)
8454           nil                           ;Nothing to do.
8455         (if function
8456             ;; Compare with header field.
8457             (let ((header (gnus-get-header-by-number article))
8458                   value)
8459               (and header
8460                    (progn
8461                      (setq value (funcall function header))
8462                      ;; Number (Lines:) or symbol must be converted to string.
8463                      (or (stringp value)
8464                          (setq value (prin1-to-string value)))
8465                      (setq did-kill (string-match regexp value)))
8466                    (if (stringp form)   ;Keyboard macro.
8467                        (execute-kbd-macro form)
8468                      (funcall form))))
8469           ;; Search article body.
8470           (let ((gnus-current-article nil) ;Save article pointer.
8471                 (gnus-last-article nil)
8472                 (gnus-break-pages nil)  ;No need to break pages.
8473                 (gnus-mark-article-hook nil)) ;Inhibit marking as read.
8474             (message "Searching for article: %d..." article)
8475             (gnus-article-setup-buffer)
8476             (gnus-article-prepare article t)
8477             (if (save-excursion
8478                   (set-buffer gnus-article-buffer)
8479                   (goto-char (point-min))
8480                   (setq did-kill (re-search-forward regexp nil t)))
8481                 (if (stringp form)      ;Keyboard macro.
8482                     (execute-kbd-macro form)
8483                   (funcall form))))))
8484       did-kill)))
8485
8486 \f
8487 ;;; 
8488 ;;; Gnus Posting Functions
8489 ;;;
8490
8491 (defvar gnus-organization-file "/usr/lib/news/organization"
8492   "*Local news organization file.")
8493
8494 (defvar gnus-post-news-buffer "*post-news*")
8495 (defvar gnus-winconf-post-news nil)
8496
8497 (autoload 'news-reply-mode "rnewspost")
8498
8499 ;;; Post news commands of Gnus Group Mode and Summary Mode
8500
8501 (defun gnus-group-post-news ()
8502   "Post an article."
8503   (interactive)
8504   ;; Save window configuration.
8505   (setq gnus-winconf-post-news (current-window-configuration))
8506   ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
8507   (or gnus-newsgroup-name (setq gnus-newsgroup-name (gnus-group-group-name)))
8508   (unwind-protect
8509       (gnus-post-news 'post nil)
8510     (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
8511              (not (zerop (buffer-size))))
8512         ;; Restore last window configuration.
8513         (set-window-configuration gnus-winconf-post-news)))
8514   ;; We don't want to return to Summary buffer nor Article buffer later.
8515   (if (get-buffer gnus-summary-buffer)
8516       (bury-buffer gnus-summary-buffer))
8517   (if (get-buffer gnus-article-buffer)
8518       (bury-buffer gnus-article-buffer)))
8519
8520 (defun gnus-summary-post-news ()
8521   "Post an article."
8522   (interactive)
8523   ;; Save window configuration.
8524   (setq gnus-winconf-post-news (current-window-configuration))
8525   (unwind-protect
8526       (gnus-post-news 'post gnus-newsgroup-name)
8527     (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
8528              (not (zerop (buffer-size))))
8529         ;; Restore last window configuration.
8530         (set-window-configuration gnus-winconf-post-news)))
8531   ;; We don't want to return to Article buffer later.
8532   (if (get-buffer gnus-article-buffer)
8533       (bury-buffer gnus-article-buffer)))
8534
8535 (defun gnus-summary-followup (yank)
8536   "Compose a followup to an article.
8537 If prefix argument YANK is non-nil, original article is yanked automatically."
8538   (interactive "P")
8539   (gnus-summary-select-article t)
8540   (let ((headers gnus-current-headers)
8541         (gnus-newsgroup-name gnus-newsgroup-name))
8542     ;; Check Followup-To: poster.
8543     (set-buffer gnus-article-buffer)
8544     (if (and gnus-use-followup-to
8545              (string-equal "poster" (gnus-fetch-field "followup-to"))
8546              (or (not (eq gnus-use-followup-to t))
8547                  (not (y-or-n-p 
8548                        "Do you want to ignore `Followup-To: poster'? "))))
8549         ;; Mail to the poster.  Gnus is now RFC1036 compliant.
8550         (gnus-summary-reply yank)
8551       ;; Save window configuration.
8552       (setq gnus-winconf-post-news (current-window-configuration))
8553       (unwind-protect
8554           (gnus-post-news 'followup headers gnus-article-buffer yank)
8555         (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
8556                  (not (zerop (buffer-size))))
8557             ;; Restore last window configuration.
8558             (set-window-configuration gnus-winconf-post-news)))
8559       ;; We don't want to return to Article buffer later.
8560       (bury-buffer gnus-article-buffer))))
8561
8562 (defun gnus-summary-followup-with-original ()
8563   "Compose a followup to an article and include the original article."
8564   (interactive)
8565   (gnus-summary-followup t))
8566
8567 (defun gnus-summary-cancel-article ()
8568   "Cancel an article you posted."
8569   (interactive)
8570   (gnus-summary-select-article t)
8571   (gnus-eval-in-buffer-window gnus-article-buffer
8572     (gnus-cancel-news)))
8573
8574 (defun gnus-summary-supersede-article ()
8575   "Compose an article that will supersede a previous article.
8576 This is done simply by taking the old article and adding a Supersedes
8577 header line with the old Message-ID."
8578   (interactive)
8579   (if (not
8580        (string-equal
8581         (downcase (mail-strip-quoted-names 
8582                    (header-from gnus-current-headers)))
8583         (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
8584       (error "This article is not yours."))
8585   (gnus-summary-select-article t)
8586   (save-excursion
8587     (set-buffer gnus-article-buffer)
8588     (let ((buffer-read-only nil))
8589       (goto-char (point-min))
8590       (search-forward "\n\n" nil t)
8591       (if (not (re-search-backward "^Message-ID: " nil t))
8592           (error "No Message-ID in this article"))))
8593   (if (gnus-post-news 'post gnus-newsgroup-name)
8594       (progn
8595         (erase-buffer)
8596         (insert-buffer gnus-article-buffer)
8597         (goto-char (point-min))
8598         (search-forward "\n\n" nil t)
8599         (if (not (re-search-backward "^Message-ID: " nil t))
8600             (error "No Message-ID in this article")
8601           (replace-match "Supersedes: "))
8602         (search-forward "\n\n")
8603         (forward-line -1)
8604         (insert mail-header-separator))))
8605
8606 \f
8607 ;;; Post a News using NNTP
8608
8609 ;;;###autoload
8610 (fset 'sendnews 'gnus-post-news)
8611
8612 ;;;###autoload
8613 (fset 'postnews 'gnus-post-news)
8614
8615 (defun gnus-post-news (method &optional header article-buffer yank)
8616   "Begin editing a new USENET news article to be posted.
8617 Type \\[describe-mode] in the buffer to get a list of commands."
8618   (interactive)
8619   (if (or (not gnus-novice-user)
8620           gnus-expert-user
8621           (not (eq 'post 
8622                    (nth 1 (assoc 
8623                            (format "%s" (car gnus-current-select-method))
8624                            gnus-valid-select-methods))))
8625           (y-or-n-p "Are you sure you want to post to all of USENET? "))
8626       (let ((sumart (if (eq method 'followup)
8627                         (save-excursion
8628                           (set-buffer gnus-summary-buffer)
8629                           (cons (current-buffer) gnus-current-article))))
8630             post-buf)
8631         (if (and gnus-interactive-post
8632                  (not gnus-expert-user)
8633                  (eq method 'post)
8634                  (not header))
8635             (setq header 
8636                   (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
8637         (setq mail-reply-buffer article-buffer)
8638         (setq gnus-post-news-buffer 
8639               (setq post-buf
8640                     (gnus-request-post-buffer 
8641                      method (if (stringp header) 
8642                                 (gnus-group-real-name header) header)
8643                      article-buffer)))
8644         (if (eq method 'post)
8645             (progn
8646               (delete-other-windows)
8647               (switch-to-buffer post-buf))
8648           (delete-other-windows)
8649           (if (not yank)
8650               (progn
8651                 (switch-to-buffer article-buffer)
8652                 (pop-to-buffer post-buf))
8653             (switch-to-buffer post-buf)))
8654         (gnus-overload-functions)
8655         (make-local-variable 'gnus-article-reply)
8656         (make-local-variable 'gnus-article-check-size)
8657         (setq gnus-article-reply sumart)
8658         ;; Handle author copy using FCC field.
8659         (if gnus-author-copy
8660             (progn
8661               (mail-position-on-field "FCC")
8662               (insert gnus-author-copy)))
8663         (goto-char (point-min))
8664         (if (and (eq method 'post) (not header))
8665             (end-of-line)
8666           (search-forward (concat "\n" mail-header-separator "\n"))
8667           (if yank 
8668               (save-excursion
8669                 (run-hooks 'news-reply-header-hook)
8670                 (mail-yank-original nil)))
8671           (if gnus-post-prepare-function
8672               (funcall gnus-post-prepare-function 
8673                        (if (stringp header) header gnus-newsgroup-name))))))
8674   (setq gnus-article-check-size (cons (buffer-size) (gnus-article-checksum)))
8675   (message "")
8676   t)
8677
8678 (defun gnus-inews-news ()
8679   "Send a news message."
8680   (interactive)
8681   ;; Check whether the article is a Good Net Citizen.
8682   (if (not (gnus-inews-check-post))
8683       ;; Aber nein!
8684       ()
8685     ;; Looks ok, so we do the nasty.
8686     (let* ((case-fold-search nil)
8687            (server-running (gnus-server-opened gnus-select-method))
8688            (reply gnus-article-reply))
8689       (save-excursion
8690         ;; Connect to default NNTP server if necessary.
8691         ;; Suggested by yuki@flab.fujitsu.junet.
8692         (gnus-start-news-server)        ;Use default server.
8693         ;; NNTP server must be opened before current buffer is modified.
8694         (widen)
8695         (goto-char (point-min))
8696         (run-hooks 'news-inews-hook)
8697         (save-restriction
8698           (narrow-to-region
8699            (point-min)
8700            (progn
8701              (goto-char (point-min))
8702              (search-forward (concat "\n" mail-header-separator "\n"))
8703              (point)))
8704
8705           ;; Correct newsgroups field: change sequence of spaces to comma and 
8706           ;; eliminate spaces around commas.  Eliminate imbedded line breaks.
8707           (goto-char (point-min))
8708           (if (search-forward-regexp "^Newsgroups: +" nil t)
8709               (save-restriction
8710                 (narrow-to-region
8711                  (point)
8712                  (if (re-search-forward "^[^ \t]" nil 'end)
8713                      (match-beginning 0)
8714                    (point-max)))
8715                 (goto-char (point-min))
8716                 (replace-regexp "\n[ \t]+" " ") ;No line breaks (too confusing)
8717                 (goto-char (point-min))
8718                 (replace-regexp "[ \t\n]*,[ \t\n]*\\|[ \t]+" ",")
8719                 ))
8720
8721           ;; Mail the message too if To: or Cc: exists.
8722           (if (or (mail-fetch-field "to" nil t)
8723                   (mail-fetch-field "cc" nil t))
8724               (if gnus-mail-send-method
8725                   (progn
8726                     (message "Sending via mail...")
8727                     (widen)
8728                     (funcall gnus-mail-send-method)
8729                     (message "Sending via mail... done"))
8730                 (ding)
8731                 (message "No mailer defined.  To: and/or Cc: fields ignored.")
8732                 (sit-for 1))))
8733
8734         ;; Send to NNTP server. 
8735         (message "Posting to USENET...")
8736         (if (gnus-inews-article)
8737             (progn
8738               (message "Posting to USENET... done")
8739               (if (and reply
8740                        (get-buffer (car reply))
8741                        (buffer-name (car reply)))
8742                   (progn
8743                     (save-excursion
8744                       (set-buffer gnus-summary-buffer)
8745                       (gnus-summary-mark-article-as-replied 
8746                        (cdr reply))))))
8747           ;; We cannot signal an error.
8748           (ding) (message "Article rejected: %s" 
8749                           (gnus-status-message gnus-select-method)))
8750         (set-buffer-modified-p nil))
8751       ;; If NNTP server is opened by gnus-inews-news, close it by myself.
8752       (or server-running
8753           (gnus-close-server gnus-current-select-method))
8754       (and (fboundp 'bury-buffer) (bury-buffer))
8755       ;; Restore last window configuration.
8756       (and gnus-winconf-post-news
8757            (set-window-configuration gnus-winconf-post-news))
8758       (setq gnus-winconf-post-news nil))))
8759
8760 (defun gnus-inews-check-post ()
8761   "Check whether the post looks ok."
8762   (and 
8763    ;; First check for an empty Subject line. 
8764    (save-excursion
8765      (save-restriction
8766        (narrow-to-region 
8767         (goto-char (point-min))
8768         (progn (search-forward (concat "\n" mail-header-separator "\n")) 
8769                (point)))
8770        (goto-char (point-min))
8771        (if (or (not (re-search-forward "^Subject: +" nil t))
8772                (eolp))
8773            (yes-or-no-p
8774             "The Subject field is empty. Do you really want to post this article? ")
8775          t)))
8776    ;; Then use the (size . checksum) variable to see whether the
8777    ;; article is empty or has only quoted text.
8778    (if (and (= (buffer-size) (car gnus-article-check-size))
8779             (= (gnus-article-checksum) (cdr gnus-article-check-size)))
8780        (yes-or-no-p "It looks like there's no new text in your article. Really post it? ")
8781      t)))
8782
8783 (defun gnus-article-checksum ()
8784   (let ((sum 0))
8785     (save-excursion
8786       (while (not (eobp))
8787         (setq sum (logxor sum (following-char)))
8788         (forward-char 1)))
8789     sum))
8790
8791 (defun gnus-cancel-news ()
8792   "Cancel an article you posted."
8793   (interactive)
8794   (if (yes-or-no-p "Do you really want to cancel this article? ")
8795       (let ((from nil)
8796             (newsgroups nil)
8797             (message-id nil)
8798             (distribution nil))
8799         (save-excursion
8800           ;; Get header info. from original article.
8801           (save-restriction
8802             (gnus-article-show-all-headers)
8803             (goto-char (point-min))
8804             (search-forward "\n\n" nil 'move)
8805             (narrow-to-region (point-min) (point))
8806             (setq from (mail-fetch-field "from"))
8807             (setq newsgroups (mail-fetch-field "newsgroups"))
8808             (setq message-id (mail-fetch-field "message-id"))
8809             (setq distribution (mail-fetch-field "distribution")))
8810           ;; Verify if the article is absolutely user's by comparing
8811           ;; user id with value of its From: field.
8812           (if (not
8813                (string-equal
8814                 (downcase (mail-strip-quoted-names from))
8815                 (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
8816               (progn
8817                 (ding) (message "This article is not yours."))
8818             ;; Make control article.
8819             (set-buffer (get-buffer-create " *Gnus-canceling*"))
8820             (buffer-disable-undo (current-buffer))
8821             (erase-buffer)
8822             (insert "Newsgroups: " newsgroups "\n"
8823                     "Subject: cancel " message-id "\n"
8824                     "Control: cancel " message-id "\n"
8825                     mail-header-separator "\n"
8826                     )
8827             ;; Send the control article to NNTP server.
8828             (message "Canceling your article...")
8829             (if (gnus-inews-article)
8830                 (message "Canceling your article... done")
8831               (ding) (message "Failed to cancel your article"))
8832             ;; Kill the article buffer.
8833             (kill-buffer (current-buffer))
8834             )))
8835     ))
8836
8837 \f
8838 ;;; Lowlevel inews interface
8839
8840 (defun gnus-inews-article ()
8841   "Post an article in current buffer using NNTP protocol."
8842   (let ((artbuf (current-buffer))
8843         (tmpbuf (get-buffer-create " *Gnus-posting*")))
8844     (widen)
8845     (goto-char (point-max))
8846     ;; require a newline at the end for inews to append .signature to
8847     (or (= (preceding-char) ?\n)
8848         (insert ?\n))
8849     ;; Prepare article headers.  All message body such as signature
8850     ;; must be inserted before Lines: field is prepared.
8851     (save-restriction
8852       (goto-char (point-min))
8853       (narrow-to-region 
8854        (point-min) 
8855        (save-excursion
8856          (search-forward (concat "\n" mail-header-separator "\n")) 
8857          (forward-line -1) 
8858          (point)))
8859       (gnus-inews-insert-headers)
8860       (widen))
8861     (save-excursion
8862       (set-buffer tmpbuf)
8863       (buffer-disable-undo (current-buffer))
8864       (erase-buffer)
8865       (insert-buffer-substring artbuf)
8866       ;; Remove the header separator.
8867       (goto-char (point-min))
8868       (search-forward (concat "\n" mail-header-separator "\n"))
8869       (replace-match "\n\n")
8870       ;; This hook may insert a signature.
8871       (run-hooks 'gnus-prepare-article-hook)
8872       ;; Run final inews hooks.  This hook may do FCC.
8873       ;; The article must be saved before being posted because
8874       ;; `gnus-request-post' modifies the buffer.
8875       (run-hooks 'gnus-inews-article-hook)
8876       ;; Post an article to NNTP server.
8877       ;; Return NIL if post failed.
8878       (prog1
8879           (gnus-request-post gnus-current-select-method)
8880         (kill-buffer (current-buffer)))
8881       )))
8882
8883 (defun gnus-inews-insert-headers ()
8884   "Prepare article headers.
8885 Fields already prepared in the buffer are not modified.
8886 Fields in `gnus-required-headers' will be generated."
8887   (save-excursion
8888     (let ((date (gnus-inews-date))
8889           (message-id (gnus-inews-message-id))
8890           (organization (gnus-inews-organization)))
8891       (goto-char (point-min))
8892       (and (memq 'Path gnus-required-headers)
8893            (or (mail-fetch-field "path")
8894                (gnus-insert-end "Path: " (gnus-inews-path) "\n")))
8895       (and (memq 'From gnus-required-headers)
8896            (or (mail-fetch-field "from")
8897                (gnus-insert-end "From: " (gnus-inews-user-name) "\n")))
8898       ;; If there is no subject, make Subject: field.
8899       (and (memq 'Subject gnus-required-headers)
8900            (or (mail-fetch-field "subject")
8901                (gnus-insert-end "Subject: \n")))
8902       ;; If there is no newsgroups, make Newsgroups: field.
8903       (and (memq 'Newsgroups gnus-required-headers)
8904            (or (mail-fetch-field "newsgroups")
8905                (gnus-insert-end "Newsgroups: \n")))
8906       (and message-id
8907            (memq 'Message-ID gnus-required-headers)
8908            (progn
8909              (if (mail-fetch-field "message-id")
8910                  (progn
8911                    (goto-char (point-min))
8912                    (re-search-forward "^Message-ID" nil t)
8913                    (delete-region (progn (beginning-of-line) (point))
8914                                   (progn (forward-line 1) (point)))))
8915              (gnus-insert-end "Message-ID: " message-id "\n")))
8916       (and date
8917            (memq 'Date gnus-required-headers)
8918            (or (mail-fetch-field "date")
8919                (gnus-insert-end "Date: " date "\n")))
8920       ;; Optional fields in RFC977 and RFC1036
8921       (and organization
8922            (memq 'Organization gnus-required-headers)
8923            (or (mail-fetch-field "organization")
8924                (let ((begin (point-max))
8925                      (fill-column 79)
8926                      (fill-prefix "\t"))
8927                  (gnus-insert-end "Organization: " organization "\n")
8928                  (fill-region-as-paragraph begin (point-max)))))
8929       (and (memq 'Distribution gnus-required-headers)
8930            (or (mail-fetch-field "distribution")
8931                (gnus-insert-end "Distribution: \n")))
8932       (and (memq 'Lines gnus-required-headers)
8933            (or (mail-fetch-field "lines")
8934                (gnus-insert-end "Lines: " (gnus-inews-lines) "\n")))
8935       (and (memq 'X-Newsreader gnus-required-headers)
8936            (or (mail-fetch-field "x-newsreader")
8937                (gnus-insert-end "X-Newsreader: " gnus-version "\n")))
8938       )))
8939
8940
8941 (defun gnus-insert-end (&rest args)
8942   (save-excursion
8943     (goto-char (point-max))
8944     (apply 'insert args)))
8945
8946 (defun gnus-inews-insert-signature ()
8947   "Insert signature file in current article buffer.
8948 If there is a file named .signature-DISTRIBUTION. Set the variable to
8949 nil to prevent appending the signature file automatically.
8950 Signature file is specified by the variable gnus-signature-file."
8951   (save-excursion
8952     (save-restriction
8953       (let ((signature
8954              (if gnus-signature-file
8955                  (expand-file-name gnus-signature-file nil)))
8956             distribution)
8957         (goto-char (point-min))
8958         (search-forward "\n\n")
8959         (narrow-to-region (point-min) (point))
8960         (setq distribution (mail-fetch-field "distribution"))
8961         (widen)
8962         (if signature
8963             (progn
8964               ;; Insert signature.
8965               (if (file-exists-p signature)
8966                   (progn
8967                     (goto-char (point-max))
8968                     (insert "--\n")
8969                     (insert-file-contents signature)))
8970               ))))))
8971
8972 (defun gnus-inews-do-fcc ()
8973   "Process FCC: fields in current article buffer.
8974 Unless the first character of the field is `|', the article is saved
8975 to the specified file using the function specified by the variable
8976 gnus-author-copy-saver.  The default function rmail-output saves in
8977 Unix mailbox format.
8978 If the first character is `|', the contents of the article is send to
8979 a program specified by the rest of the value."
8980   (let ((fcc-list nil)
8981         (fcc-file nil)
8982         (case-fold-search t))           ;Should ignore case.
8983     (save-excursion
8984       (save-restriction
8985         (goto-char (point-min))
8986         (search-forward "\n\n")
8987         (narrow-to-region (point-min) (point))
8988         (goto-char (point-min))
8989         (while (re-search-forward "^FCC:[ \t]*" nil t)
8990           (setq fcc-list
8991                 (cons (buffer-substring
8992                        (point)
8993                        (progn
8994                          (end-of-line)
8995                          (skip-chars-backward " \t")
8996                          (point)))
8997                       fcc-list))
8998           (delete-region (match-beginning 0)
8999                          (progn (forward-line 1) (point))))
9000         ;; Process FCC operations.
9001         (widen)
9002         (while fcc-list
9003           (setq fcc-file (car fcc-list))
9004           (setq fcc-list (cdr fcc-list))
9005           (cond ((string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" fcc-file)
9006                  (let ((program (substring fcc-file
9007                                            (match-beginning 1) (match-end 1))))
9008                    ;; Suggested by yuki@flab.fujitsu.junet.
9009                    ;; Send article to named program.
9010                    (call-process-region (point-min) (point-max) shell-file-name
9011                                         nil nil nil "-c" program)
9012                    ))
9013                 (t
9014                  ;; Suggested by hyoko@flab.fujitsu.junet.
9015                  ;; Save article in Unix mail format by default.
9016                  (if (and gnus-author-copy-saver
9017                           (not (eq gnus-author-copy-saver 'rmail-output)))
9018                      (funcall gnus-author-copy-saver fcc-file)
9019                    (if (and (file-readable-p fcc-file) (rmail-file-p fcc-file))
9020                        (gnus-output-to-rmail fcc-file)
9021                      (rmail-output fcc-file 1 t t)))
9022                  ))
9023           )
9024         ))
9025     ))
9026
9027 (defun gnus-inews-path ()
9028   "Return uucp path."
9029   (let ((login-name (gnus-inews-login-name)))
9030     (cond ((null gnus-use-generic-path)
9031            (concat (nth 1 gnus-select-method) "!" login-name))
9032           ((stringp gnus-use-generic-path)
9033            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
9034            (concat gnus-use-generic-path "!" login-name))
9035           (t login-name))
9036     ))
9037
9038 (defun gnus-inews-user-name ()
9039   "Return user's network address as `NAME@DOMAIN (FULLNAME)'."
9040   (let ((full-name (gnus-inews-full-name)))
9041     (concat (or user-mail-address
9042                 (if (or gnus-user-login-name gnus-use-generic-from
9043                         gnus-local-domain (getenv "DOMAINNAME"))
9044                     (concat (gnus-inews-login-name) "@"
9045                             (gnus-inews-domain-name gnus-use-generic-from))
9046                   user-mail-address))
9047             ;; User's full name.
9048             (cond ((string-equal full-name "") "")
9049                   ((string-equal full-name "&") ;Unix hack.
9050                    (concat " (" (user-login-name) ")"))
9051                   (t
9052                    (concat " (" full-name ")")))
9053             )))
9054
9055 (defun gnus-inews-login-name ()
9056   "Return user login name.
9057 Got from the variable `gnus-user-login-name' and the function
9058 `user-login-name'."
9059   (or gnus-user-login-name (user-login-name)))
9060
9061 (defun gnus-inews-full-name ()
9062   "Return user full name.
9063 Got from the variable `gnus-user-full-name', the environment variable
9064 NAME, and the function `user-full-name'."
9065   (or gnus-user-full-name
9066       (getenv "NAME") (user-full-name)))
9067
9068 (defun gnus-inews-domain-name (&optional genericfrom)
9069   "Return user's domain name.
9070 If optional argument GENERICFROM is a string, use it as the domain
9071 name; if it is non-nil, strip of local host name from the domain name.
9072 If the function `system-name' returns full internet name and the
9073 domain is undefined, the domain name is got from it."
9074   (if (or genericfrom gnus-local-domain (getenv "DOMAINNAME"))
9075       (let ((domain (or (if (stringp genericfrom) genericfrom)
9076                         (getenv "DOMAINNAME")
9077                         gnus-local-domain
9078                         ;; Function `system-name' may return full internet name.
9079                         ;; Suggested by Mike DeCorte <mrd@sun.soe.clarkson.edu>.
9080                         (if (string-match "\\." (system-name))
9081                             (substring (system-name) (match-end 0)))
9082                         (read-string "Domain name (no host): ")))
9083             (host (or (if (string-match "\\." (system-name))
9084                           (substring (system-name) 0 (match-beginning 0)))
9085                       (system-name))))
9086         (if (string-equal "." (substring domain 0 1))
9087             (setq domain (substring domain 1)))
9088         ;; Support GENERICFROM as same as standard Bnews system.
9089         ;; Suggested by ohm@kaba.junet and vixie@decwrl.dec.com.
9090         (cond ((null genericfrom)
9091                (concat host "." domain))
9092               ;;((stringp genericfrom) genericfrom)
9093               (t domain)))
9094     (substring user-mail-address (1+ (string-match "@" user-mail-address)))))
9095
9096 (defun gnus-inews-message-id ()
9097   "Generate unique Message-ID for user."
9098   ;; Message-ID should not contain a slash and should be terminated by
9099   ;; a number.  I don't know the reason why it is so.
9100   (concat "<" (gnus-inews-unique-id) "@" (gnus-inews-domain-name) ">"))
9101
9102 (defun gnus-inews-unique-id ()
9103   "Generate unique ID from user name and current time."
9104   (let ((date (current-time-string))
9105         (name (gnus-inews-login-name)))
9106     (if (string-match "^[^ ]+ \\([^ ]+\\)[ ]+\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) [0-9][0-9]\\([0-9][0-9]\\)"
9107                       date)
9108         (concat (upcase name) "."
9109                 (substring date (match-beginning 6) (match-end 6)) ;Year
9110                 (substring date (match-beginning 1) (match-end 1)) ;Month
9111                 (substring date (match-beginning 2) (match-end 2)) ;Day
9112                 (substring date (match-beginning 3) (match-end 3)) ;Hour
9113                 (substring date (match-beginning 4) (match-end 4)) ;Minute
9114                 (substring date (match-beginning 5) (match-end 5)) ;Second
9115                 )
9116       (error "Cannot understand current-time-string: %s." date))
9117     ))
9118
9119 (defun gnus-current-time-zone (time)
9120   "The local time zone in effect at TIME, or nil if not known."
9121   (let ((z (and (fboundp 'current-time-zone) (current-time-zone time))))
9122     (if (and z (car z)) z gnus-local-timezone)))
9123
9124 (defun gnus-inews-date ()
9125   "Date string of today.
9126 If `current-time-zone' works, or if `gnus-local-timezone' is set correctly,
9127 this yields a date that conforms to RFC 822.  Otherwise a buggy date will
9128 be generated; this might work with some older news servers."
9129   (let* ((now (and (fboundp 'current-time) (current-time)))
9130          (zone (gnus-current-time-zone now)))
9131     (if zone
9132         (gnus-inews-valid-date now zone)
9133       ;; No timezone info.
9134       (gnus-inews-buggy-date now))))
9135
9136 (defun gnus-inews-valid-date (&optional time zone)
9137   "A date string that represents TIME and conforms to the Usenet standard.
9138 TIME is optional and defaults to the current time.
9139 Some older versions of Emacs always act as if TIME is nil.
9140 The optional argument ZONE specifies the local time zone (default GMT)."
9141   (timezone-make-date-arpa-standard
9142    (if (fboundp 'current-time)
9143        (current-time-string time)
9144      (current-time-string))
9145    zone "GMT"))
9146
9147 (defun gnus-inews-buggy-date (&optional time)
9148   "A buggy date string that represents TIME.
9149 TIME is optional and defaults to the current time.
9150 Some older versions of Emacs always act as if TIME is nil."
9151   (let ((date (if (fboundp 'current-time)
9152                   (current-time-string time)
9153                 (current-time-string))))
9154     (if (string-match "^[^ ]+ \\([^ ]+\\)[ ]+\\([0-9]+\\) \\([0-9:]+\\) [0-9][0-9]\\([0-9][0-9]\\)"
9155                       date)
9156         (concat (substring date (match-beginning 2) (match-end 2)) ;Day
9157                 " "
9158                 (substring date (match-beginning 1) (match-end 1)) ;Month
9159                 " "
9160                 (substring date (match-beginning 4) (match-end 4)) ;Year
9161                 " "
9162                 (substring date (match-beginning 3) (match-end 3))) ;Time
9163       (error "Cannot understand current-time-string: %s." date))
9164     ))
9165
9166 (defun gnus-inews-organization ()
9167   "Return user's organization.
9168 The ORGANIZATION environment variable is used if defined.
9169 If not, the variable gnus-local-organization is used instead.
9170 If the value begins with a slash, it is taken as the name of a file
9171 containing the organization."
9172   ;; The organization must be got in this order since the ORGANIZATION
9173   ;; environment variable is intended for user specific while
9174   ;; gnus-local-organization is for machine or organization specific.
9175
9176   (let* ((private-file (expand-file-name "~/.organization" nil))
9177          (organization (or (getenv "ORGANIZATION")
9178                            gnus-local-organization
9179                            private-file)))
9180     (and (stringp organization)
9181          (> (length organization) 0)
9182          (string-equal (substring organization 0 1) "/")
9183          ;; Get it from the user and system file.
9184          ;; Suggested by roland@wheaties.ai.mit.edu (Roland McGrath).
9185          (let ((dist (mail-fetch-field "distribution")))
9186            (setq organization
9187                  (cond ((file-exists-p (concat organization "-" dist))
9188                         (concat organization "-" dist))
9189                        ((file-exists-p organization) organization)
9190                        ((file-exists-p gnus-organization-file)
9191                         gnus-organization-file)
9192                        (t organization)))
9193            ))
9194     (cond ((not (stringp organization)) nil)
9195           ((and (string-equal (substring organization 0 1) "/")
9196                 (file-exists-p organization))
9197            ;; If the first character is `/', assume it is the name of
9198            ;; a file containing the organization.
9199            (save-excursion
9200              (let ((tmpbuf (get-buffer-create " *Gnus organization*")))
9201                (set-buffer tmpbuf)
9202                (erase-buffer)
9203                (insert-file-contents organization)
9204                (prog1 (buffer-string)
9205                  (kill-buffer tmpbuf))
9206                )))
9207           ((string-equal organization private-file) nil) ;No such file
9208           (t organization))
9209     ))
9210
9211 (defun gnus-inews-lines ()
9212   "Count the number of lines and return numeric string."
9213   (save-excursion
9214     (save-restriction
9215       (widen)
9216       (goto-char (point-min))
9217       (search-forward "\n\n" nil 'move)
9218       (int-to-string (count-lines (point) (point-max))))))
9219
9220 \f
9221 ;;;
9222 ;;; Gnus Mail Functions 
9223 ;;;
9224
9225 (autoload 'news-mail-reply "rnewspost")
9226 (autoload 'news-mail-other-window "rnewspost")
9227
9228 ;;; Mail reply commands of Gnus Summary Mode
9229
9230 (defun gnus-summary-reply (yank)
9231   "Reply mail to news author.
9232 If prefix argument YANK is non-nil, original article is yanked automatically.
9233 Customize the variable gnus-mail-reply-method to use another mailer."
9234   (interactive "P")
9235   ;; Bug fix by jbw@bigbird.bu.edu (Joe Wells)
9236   ;; Stripping headers should be specified with mail-yank-ignored-headers.
9237   (gnus-summary-select-article t)
9238   (setq gnus-winconf-post-news (current-window-configuration))
9239   (let ((gnus-newsgroup-name gnus-newsgroup-name))
9240     (bury-buffer gnus-article-buffer)
9241     (funcall gnus-mail-reply-method yank)))
9242
9243 (defun gnus-summary-reply-with-original ()
9244   "Reply mail to news author with original article.
9245 Customize the variable gnus-mail-reply-method to use another mailer."
9246   (interactive)
9247   (gnus-summary-reply t))
9248
9249 (defun gnus-summary-mail-forward ()
9250   "Forward the current message to another user.
9251 Customize the variable gnus-mail-forward-method to use another mailer."
9252   (interactive)
9253   (gnus-summary-select-article t)
9254   (set-buffer gnus-article-buffer)
9255   (let ((gnus-newsgroup-name gnus-newsgroup-name))
9256     (funcall gnus-mail-forward-method)))
9257
9258 (defun gnus-summary-mail-other-window ()
9259   "Compose mail in other window.
9260 Customize the variable `gnus-mail-other-window-method' to use another
9261 mailer."
9262   (interactive)
9263   (let ((gnus-newsgroup-name gnus-newsgroup-name))
9264     (funcall gnus-mail-other-window-method)))
9265
9266 (defun gnus-mail-reply-using-mail (&optional yank to-address)
9267   (save-excursion
9268     (set-buffer gnus-summary-buffer)
9269     (let ((info (nth 2 (gnus-gethash gnus-newsgroup-name gnus-newsrc-hashtb)))
9270           (group (gnus-group-real-name gnus-newsgroup-name))
9271           (cur (cons (current-buffer) gnus-current-article))
9272           from subject date to reply-to message-of
9273           references message-id sender follow-to)
9274       (set-buffer (get-buffer-create "*mail*"))
9275       (mail-mode)
9276       (make-local-variable 'gnus-article-reply)
9277       (setq gnus-article-reply cur)
9278       (local-set-key "\C-c\C-c" 'gnus-mail-send-and-exit)
9279       (local-set-key "\C-c\C-y" 'gnus-mail-yank-original)
9280       (if (and (buffer-modified-p)
9281                (> (buffer-size) 0)
9282                (not (y-or-n-p "Unsent article being composed; erase it? ")))
9283           ()
9284         (erase-buffer)
9285         (save-excursion
9286           (set-buffer gnus-article-buffer)
9287           (goto-char (point-min))
9288           (narrow-to-region (point-min)
9289                             (progn (search-forward "\n\n") (point)))
9290           (set-text-properties (point-min) (point-max) nil)
9291           (if (and (boundp 'gnus-reply-to-function)
9292                    gnus-reply-to-function)
9293               (save-excursion
9294                 (save-restriction
9295                   (gnus-narrow-to-headers)
9296                   (setq follow-to (funcall gnus-reply-to-function group)))))
9297           (setq from (mail-fetch-field "from"))
9298           (setq date (mail-fetch-field "date"))
9299           (and from
9300                (let ((stop-pos 
9301                       (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
9302                  (setq message-of
9303                        (concat (if stop-pos (substring from 0 stop-pos) from)
9304                                "'s message of " date))))
9305           (setq sender (mail-fetch-field "sender"))
9306           (setq subject (or (mail-fetch-field "subject")
9307                             "Re: none"))
9308           (or (string-match "^[Rr][Ee]:" subject)
9309               (setq subject (concat "Re: " subject)))
9310           (setq reply-to (mail-fetch-field "reply-to"))
9311           (setq references (mail-fetch-field "references"))
9312           (setq message-id (mail-fetch-field "message-id"))
9313           (widen))
9314         (setq news-reply-yank-from from)
9315         (setq news-reply-yank-message-id message-id)
9316         (mail-setup (or to-address follow-to reply-to from sender)
9317                     subject message-of nil gnus-article-buffer nil)
9318         ;; Fold long references line to follow RFC1036.
9319         (mail-position-on-field "References")
9320         (let ((begin (- (point) (length "References: ")))
9321               (fill-column 78)
9322               (fill-prefix "\t"))
9323           (if references (insert references))
9324           (if (and references message-id) (insert " "))
9325           (if message-id (insert message-id))
9326           ;; The region must end with a newline to fill the region
9327           ;; without inserting extra newline.
9328           (fill-region-as-paragraph begin (1+ (point))))
9329         (goto-char (point-min))
9330         (search-forward (concat "\n" mail-header-separator "\n"))
9331         (if yank
9332             (let ((last (point)))
9333               (run-hooks 'news-reply-header-hook)
9334               (mail-yank-original nil)
9335               (goto-char last))))
9336       (if (not yank)
9337           (let ((mail (current-buffer)))
9338             (switch-to-buffer gnus-article-buffer)
9339             (delete-other-windows)
9340             (switch-to-buffer-other-window mail))
9341         (delete-other-windows)
9342         (switch-to-buffer (current-buffer))))))
9343
9344 (defun gnus-mail-yank-original ()
9345   (interactive)
9346   (run-hooks 'news-reply-header-hook)
9347   (mail-yank-original nil))
9348
9349 (defun gnus-mail-send-and-exit ()
9350   (interactive)
9351   (let ((reply gnus-article-reply))
9352     (mail-send-and-exit nil)
9353     (if (and reply
9354              (get-buffer (car reply))
9355              (buffer-name (car reply)))
9356         (progn
9357           (set-buffer (car reply))
9358           (gnus-summary-mark-article-as-replied 
9359            (cdr reply)))))
9360   (if gnus-winconf-post-news
9361       (set-window-configuration gnus-winconf-post-news)))
9362
9363 (defun gnus-mail-forward-using-mail ()
9364   "Forward the current message to another user using mail."
9365   ;; This is almost a carbon copy of rmail-forward in rmail.el.
9366   (let ((forward-buffer (current-buffer))
9367         (subject
9368          (concat "[" gnus-newsgroup-name "] "
9369                  (or (gnus-fetch-field "Subject") "")))
9370         beg)
9371     ;; If only one window, use it for the mail buffer.
9372     ;; Otherwise, use another window for the mail buffer
9373     ;; so that the Rmail buffer remains visible
9374     ;; and sending the mail will get back to it.
9375     (if (if (one-window-p t)
9376             (mail nil nil subject)
9377           (mail-other-window nil nil subject))
9378         (save-excursion
9379           (setq beg (goto-char (point-max)))
9380           (insert "------- Start of forwarded message -------\n")
9381           (insert-buffer forward-buffer)
9382           (goto-char (point-max))
9383           (insert "------- End of forwarded message -------\n")
9384           ;; Suggested by Sudish Joseph <joseph@cis.ohio-state.edu>. 
9385           (goto-char beg)
9386           (while (setq beg (next-single-property-change (point) 'invisible))
9387             (goto-char beg)
9388             (delete-region beg (or (next-single-property-change 
9389                                     (point) 'invisible)
9390                                    (point-max))))
9391           ;; You have a chance to arrange the message.
9392           (run-hooks 'gnus-mail-forward-hook)))))
9393
9394 (defun gnus-mail-other-window-using-mail ()
9395   "Compose mail other window using mail."
9396   (news-mail-other-window)
9397   (gnus-overload-functions))
9398
9399 \f
9400 ;;;
9401 ;;; Dribble file
9402 ;;;
9403
9404 (defvar gnus-dribble-ignore nil)
9405
9406 (defun gnus-dribble-file-name ()
9407   (concat gnus-startup-file "-dribble"))
9408
9409 (defun gnus-dribble-open ()
9410   (save-excursion 
9411     (set-buffer 
9412      (setq gnus-dribble-buffer (find-file-noselect (gnus-dribble-file-name))))
9413     (buffer-disable-undo (current-buffer))
9414     (bury-buffer gnus-dribble-buffer)
9415     (auto-save-mode t)
9416     (goto-char (point-max))))
9417
9418 (defun gnus-dribble-enter (string)
9419   (if (not gnus-dribble-ignore)
9420       (let ((obuf (current-buffer)))
9421         (set-buffer gnus-dribble-buffer)
9422         (insert string "\n")
9423         (set-window-point (get-buffer-window (current-buffer)) (point-max))
9424         (set-buffer obuf))))
9425
9426 (defun gnus-dribble-read-file ()
9427   (let ((dribble-file (gnus-dribble-file-name)))
9428     (save-excursion 
9429       (set-buffer (setq gnus-dribble-buffer 
9430                         (get-buffer-create 
9431                          (file-name-nondirectory dribble-file))))
9432       (gnus-add-current-to-buffer-list)
9433       (erase-buffer)
9434       (set-visited-file-name dribble-file)
9435       (buffer-disable-undo (current-buffer))
9436       (bury-buffer (current-buffer))
9437       (set-buffer-modified-p nil)
9438       (let ((auto (make-auto-save-file-name))
9439             (gnus-dribble-ignore t))
9440         (if (or (file-exists-p auto) (file-exists-p dribble-file))
9441             (progn
9442               (if (file-newer-than-file-p auto dribble-file)
9443                   (setq dribble-file auto))
9444               (insert-file-contents dribble-file)
9445               (if (not (zerop (buffer-size)))
9446                   (set-buffer-modified-p t))
9447               (if (y-or-n-p "Auto-save file exists. Do you want to read it? ")
9448                   (progn
9449                     (message "Reading %s..." dribble-file) 
9450                     (eval-current-buffer)
9451                     (message "Reading %s...done" dribble-file)))))))))
9452
9453 (defun gnus-dribble-delete-file ()
9454   (save-excursion
9455     (set-buffer gnus-dribble-buffer)
9456     (let ((auto (make-auto-save-file-name)))
9457       (if (file-exists-p auto)
9458           (delete-file auto))
9459       (if (file-exists-p (gnus-dribble-file-name))
9460           (delete-file (gnus-dribble-file-name)))
9461       (erase-buffer)
9462       (set-buffer-modified-p nil))))
9463
9464 (defun gnus-dribble-save ()
9465   (if (and gnus-dribble-buffer
9466            (buffer-name gnus-dribble-buffer))
9467       (save-excursion
9468         (set-buffer gnus-dribble-buffer)
9469         (save-buffer))))
9470
9471 (defun gnus-dribble-clear ()
9472   (save-excursion
9473     (if (and gnus-dribble-buffer
9474              (buffer-name (get-buffer gnus-dribble-buffer)))
9475         (progn
9476           (set-buffer gnus-dribble-buffer)
9477           (erase-buffer)
9478           (set-buffer-modified-p nil)
9479           (setq buffer-saved-size (buffer-size))))))
9480
9481 ;;;
9482 ;;; Server Communication
9483 ;;;
9484
9485 (defun gnus-start-news-server (&optional confirm)
9486   "Open a method for getting news.
9487 If CONFIRM is non-nil, the user will be asked for an NNTP server."
9488   (let (how where)
9489     (if gnus-current-select-method
9490         ;; Stream is already opened.
9491         nil
9492       ;; Open NNTP server.
9493       (if (null gnus-nntp-service) (setq gnus-nntp-server nil))
9494       (if confirm
9495           (progn
9496             ;; Read server name with completion.
9497             (setq gnus-nntp-server
9498                   (completing-read "NNTP server: "
9499                                    (cons (list gnus-nntp-server)
9500                                          gnus-secondary-servers)
9501                                    nil nil gnus-nntp-server))
9502             (setq gnus-select-method
9503                   (list 'nntp gnus-nntp-server)))
9504
9505         (if (and gnus-nntp-server 
9506                  (stringp gnus-nntp-server)
9507                  (not (string= gnus-nntp-server "")))
9508             (setq gnus-select-method
9509                   (cond ((or (string= gnus-nntp-server "")
9510                              (string= gnus-nntp-server "::"))
9511                          (list 'nnspool (system-name)))
9512                         ((string-match ":" gnus-nntp-server)
9513                          (list 'nnmh gnus-nntp-server))
9514                         (t
9515                          (list 'nntp gnus-nntp-server))))))
9516
9517       (setq how (car gnus-select-method))
9518       (setq where (car (cdr gnus-select-method)))
9519       (cond ((eq how 'nnspool)
9520              (require 'nnspool)
9521              (message "Looking up local news spool..."))
9522             ((eq how 'nnmh)
9523              (require 'nnmh)
9524              (message "Looking up mh spool..."))
9525             (t
9526              (require 'nntp)))
9527       (setq gnus-current-select-method gnus-select-method)
9528       (run-hooks 'gnus-open-server-hook)
9529       (or 
9530        ;; gnus-open-server-hook might have opened it
9531        (gnus-server-opened gnus-select-method)  
9532        (gnus-open-server gnus-select-method)
9533        (error "%s" (gnus-nntp-message 
9534                     (format "Cannot open NNTP server on %s" 
9535                             where))))
9536       gnus-select-method)))
9537
9538 (defun gnus-check-news-server (method)
9539   "If the news server is down, start it up again."
9540   (let ((method (if method method gnus-select-method)))
9541     (if (gnus-server-opened method)
9542         ;; Stream is already opened.
9543         t
9544       ;; Open NNTP server.
9545       (message "Opening server %s on %s..." (car method) (nth 1 method))
9546       (run-hooks 'gnus-open-server-hook)
9547       (message "")
9548       (or (gnus-server-opened method)
9549           (gnus-open-server method)))))
9550
9551 (defun gnus-nntp-message (&optional message)
9552   "Check the status of the NNTP server.
9553 If the status of the server is clear and MESSAGE is non-nil, MESSAGE
9554 is returned insted of the status string."
9555   (let ((status (gnus-status-message gnus-current-select-method))
9556         (message (or message "")))
9557     (if (and (stringp status) (> (length status) 0))
9558         status message)))
9559
9560 (defun gnus-get-function (method function)
9561   (let ((func (intern (format "%s-%s" (car method) function))))
9562     (if (not (fboundp func)) 
9563         (progn
9564           (require (car method))
9565           (if (not (fboundp func)) 
9566               (error "No such function: %s" func))))
9567     func))
9568
9569 ;; Specifying port number suggested by Stephane Laveau <laveau@corse.inria.fr>.
9570 (defun gnus-open-server (method)
9571   (apply (gnus-get-function method 'open-server) (cdr method)))
9572
9573 (defun gnus-close-server (method)
9574   (funcall (gnus-get-function method 'close-server) (nth 1 method)))
9575
9576 (defun gnus-request-list (method)
9577   (funcall (gnus-get-function method 'request-list) (nth 1 method)))
9578
9579 (defun gnus-request-list-newsgroups (method)
9580   (funcall (gnus-get-function method 'request-list-newsgroups) (nth 1 method)))
9581
9582 (defun gnus-server-opened (method)
9583   (funcall (gnus-get-function method 'server-opened) (nth 1 method)))
9584
9585 (defun gnus-status-message (method)
9586   (funcall (gnus-get-function method 'status-message) (nth 1 method)))
9587
9588 (defun gnus-request-group (group &optional dont-check)
9589   (let ((method (gnus-find-method-for-group group)))
9590     (funcall (gnus-get-function method 'request-group) 
9591              (gnus-group-real-name group) (nth 1 method) dont-check)))
9592
9593 (defun gnus-retrieve-headers (articles group)
9594   (let ((method (gnus-find-method-for-group group)))
9595     (funcall (gnus-get-function method 'retrieve-headers) 
9596              articles (gnus-group-real-name group) (nth 1 method))))
9597
9598 (defun gnus-request-article (article group buffer)
9599   (let ((method (gnus-find-method-for-group group)))
9600     (funcall (gnus-get-function method 'request-article) 
9601              article (gnus-group-real-name group) (nth 1 method) buffer)))
9602
9603 (defun gnus-request-head (article group)
9604   (let ((method (gnus-find-method-for-group group)))
9605     (funcall (gnus-get-function method 'request-head) 
9606              article (gnus-group-real-name group) (nth 1 method))))
9607
9608 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
9609 (defun gnus-request-post-buffer (post header artbuf)
9610    (let* ((group gnus-newsgroup-name)
9611           (info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
9612           (method
9613            (if (and gnus-post-method
9614                     ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
9615                     (memq 'post (assoc
9616                                  (format "%s" (car gnus-current-select-method))
9617                                         gnus-valid-select-methods)))
9618                gnus-post-method
9619              gnus-current-select-method)))
9620     (funcall (gnus-get-function method 'request-post-buffer) 
9621              post header artbuf (gnus-group-real-name group) info)))
9622
9623 (defun gnus-request-post (method)
9624   (and gnus-post-method
9625        (memq 'post (assoc (format "%s" (car method))
9626                           gnus-valid-select-methods))
9627        (setq method gnus-post-method))
9628   (funcall (gnus-get-function method 'request-post) 
9629            (nth 1 method)))
9630
9631 (defun gnus-request-expire-articles (articles group &optional force)
9632   (let ((method (gnus-find-method-for-group group)))
9633     (funcall (gnus-get-function method 'request-expire-articles) 
9634              articles (gnus-group-real-name group) (nth 1 method)
9635              force)))
9636
9637 (defun gnus-request-move-article (article group server accept-function)
9638   (let ((method (gnus-find-method-for-group group)))
9639     (funcall (gnus-get-function method 'request-move-article) 
9640              article (gnus-group-real-name group) 
9641              (nth 1 method) accept-function)))
9642
9643 (defun gnus-request-accept-article (group)
9644   (let ((func (if (symbolp group) group
9645                 (car (gnus-find-method-for-group group)))))
9646     (funcall (intern (format "%s-request-accept-article" func))
9647              (gnus-group-real-name group))))
9648
9649 (defun gnus-find-method-for-group (group)
9650   (let ((info (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
9651     (if (or (not info)
9652             (not (nth 4 info)))
9653         gnus-select-method
9654       (nth 4 info))))
9655
9656 (defun gnus-check-backend-function (func group)
9657   (let ((method (if (stringp group) (car (gnus-find-method-for-group group))
9658                  group)))
9659     (fboundp (intern (format "%s-%s" method func)))))
9660
9661 (defun gnus-methods-using (method)
9662   (let ((valids gnus-valid-select-methods)
9663         outs)
9664     (while valids
9665       (if (memq method (car valids)) 
9666           (setq outs (cons (car valids) outs)))
9667       (setq valids (cdr valids)))
9668     outs))
9669
9670 ;;; 
9671 ;;; Active & Newsrc File Handling
9672 ;;;
9673
9674 ;; Newsrc related functions.
9675 ;; Gnus internal format of gnus-newsrc-assoc:
9676 ;; (("alt.general" 3 (1 . 1))
9677 ;;  ("alt.misc"    3 ((1 . 10) (12 . 15)))
9678 ;;  ("alt.test"    7 (1 . 99) (45 57 93)) ...)
9679 ;; The first item is the group name; the second is the subscription
9680 ;; level; the third is either a range of a list of ranges of read
9681 ;; articles, the optional fourth element is a list of marked articles,
9682 ;; the optional fifth element is the select method.
9683 ;;
9684 ;; Gnus internal format of gnus-newsrc-hashtb:
9685 ;; (95 ("alt.general" 3 (1 . 1)) ("alt.misc" 3 ((1 . 10) (12 . 15))) ...)
9686 ;; This is the entry for "alt.misc". The first element is the number
9687 ;; of unread articles in "alt.misc". The cdr of this entry is the
9688 ;; element *before* "alt.misc" in gnus-newsrc-assoc, which makes is
9689 ;; trivial to remove or add new elements into gnus-newsrc-assoc
9690 ;; without scanning the entire list. So, to get the actual information
9691 ;; of "alt.misc", you'd say something like 
9692 ;; (nth 2 (gnus-gethash "alt.misc" gnus-newsrc-hashtb))
9693 ;;
9694 ;; Gnus internal format of gnus-active-hashtb:
9695 ;; ((1 . 1))
9696 ;;  (5 . 10))
9697 ;;  (67 . 99)) ...)
9698 ;; The only element in each entry in this hash table is a range of
9699 ;; (possibly) available articles. (Articles in this range may have
9700 ;; been expired or cancelled.)
9701 ;;
9702 ;; Gnus internal format of gnus-killed-list and gnus-zombie-list:
9703 ;; ("alt.misc" "alt.test" "alt.general" ...)
9704
9705 (defun gnus-setup-news (&optional rawfile level)
9706   "Setup news information.
9707 If RAWFILE is non-nil, the .newsrc file will also be read.
9708 If LEVEL is non-nil, the news will be set up at level LEVEL."
9709   (let ((init (not (and gnus-newsrc-assoc
9710                         gnus-active-hashtb
9711                         (not rawfile)))))
9712     ;; Clear some variables to re-initialize news information.
9713     (if init
9714         (setq gnus-newsrc-assoc nil
9715               gnus-active-hashtb nil))
9716     ;; Read the acitve file and create `gnus-active-hashtb'.
9717     ;; If `gnus-read-active-file' is nil, then we just create an empty
9718     ;; hash table. The partial filling out of the hash table will be
9719     ;; done in `gnus-get-unread-articles'.
9720     (if (and gnus-read-active-file (not level))
9721         (gnus-read-active-file)
9722       (setq gnus-active-hashtb (make-vector 4095 0)))
9723
9724     ;; Read the newsrc file and create `gnus-newsrc-hashtb'.
9725     (if init (gnus-read-newsrc-file rawfile))
9726     ;; Find the number of unread articles in each non-dead group.
9727     (gnus-get-unread-articles (or level 7))
9728     ;; Find new newsgroups and treat them.
9729     (if (and init gnus-check-new-newsgroups gnus-read-active-file (not level))
9730         (gnus-find-new-newsgroups))
9731     (if (and init gnus-check-bogus-newsgroups 
9732              gnus-read-active-file (not level))
9733         (gnus-check-bogus-newsgroups))))
9734
9735 (defun gnus-find-new-newsgroups ()
9736   "Search for new newsgroups and add them.
9737 Each new newsgroup will be treated with `gnus-subscribe-newsgroup-method.'
9738 The `-n' option line from .newsrc is respected."
9739   (interactive)
9740   (if (not gnus-have-read-active-file) (gnus-read-active-file))
9741   (if (not (gnus-check-first-time-used))
9742       (let ((groups 0)
9743             group new-newsgroups)
9744         (if (not gnus-killed-hashtb) (gnus-make-hashtable-from-killed))
9745         ;; Go though every newsgroup in `gnus-active-hashtb' and compare
9746         ;; with `gnus-newsrc-hashtb' and `gnus-killed-hashtb'.
9747         (mapatoms
9748          (lambda (sym)
9749            (setq group (symbol-name sym))
9750            (if (or (gnus-gethash group gnus-killed-hashtb)
9751                    (gnus-gethash group gnus-newsrc-hashtb))
9752                ()
9753              (if (and gnus-newsrc-options-n-yes
9754                       (string-match gnus-newsrc-options-n-yes group))
9755                  (progn
9756                    (setq groups (1+ groups))
9757                    (gnus-sethash group group gnus-killed-hashtb)
9758                    (funcall gnus-subscribe-options-newsgroup-method group))
9759                (if (or (null gnus-newsrc-options-n-no)
9760                        (not (string-match gnus-newsrc-options-n-no group)))
9761                    ;; Add this group.
9762                    (progn
9763                      (setq groups (1+ groups))
9764                      (gnus-sethash group group gnus-killed-hashtb)
9765                      (if gnus-subscribe-hierarchical-interactive
9766                          (setq new-newsgroups (cons group new-newsgroups))
9767                        (funcall gnus-subscribe-newsgroup-method group)))))))
9768          gnus-active-hashtb)
9769         (if new-newsgroups 
9770             (gnus-subscribe-hierarchical-interactive new-newsgroups))
9771         ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
9772         (if (> groups 0)
9773             (message "%d new newsgroup%s arrived." 
9774                      groups (if (> groups 1) "s have" " has"))))))
9775
9776 (defun gnus-check-first-time-used ()
9777   (if (or (file-exists-p gnus-startup-file)
9778           (file-exists-p (concat gnus-startup-file ".el"))
9779           (file-exists-p (concat gnus-startup-file ".eld")))
9780       nil
9781     (message "First time user; subscribing you to default groups")
9782     (let ((groups gnus-default-subscribed-newsgroups)
9783           group)
9784       (if (eq groups t)
9785           nil
9786         (setq groups (or groups gnus-backup-default-subscribed-newsgroups))
9787         (mapatoms
9788          (lambda (sym)
9789            (setq group (symbol-name sym))
9790            (if (and gnus-newsrc-options-n-yes
9791                     (string-match gnus-newsrc-options-n-yes group))
9792                (funcall gnus-subscribe-options-newsgroup-method group)
9793              (and (or (null gnus-newsrc-options-n-no)
9794                       (not (string-match gnus-newsrc-options-n-no group)))
9795                   (setq gnus-killed-list (cons group gnus-killed-list)))))
9796          gnus-active-hashtb)
9797         (while groups
9798           (if (gnus-gethash (car groups) gnus-active-hashtb)
9799               (gnus-group-change-level (car groups) 3 9))
9800           (setq groups (cdr groups)))))))
9801
9802 ;; `gnus-group-change-level' is the fundamental function for changing
9803 ;; subscription levels of newsgroups. This might mean just changing
9804 ;; from level 1 to 2, which is pretty trivial, from 2 to 6 or back
9805 ;; again, which subscribes/unsubscribes a group, which is equally
9806 ;; trivial. Changing from 1-7 to 8-9 means that you kill a group, and
9807 ;; from 8-9 to 1-7 means that you remove the group from the list of
9808 ;; killed (or zombie) groups and add them to the (kinda) subscribed
9809 ;; groups. And last but not least, moving from 8 to 9 and 9 to 8,
9810 ;; which is trivial.
9811 ;; ENTRY can either be a string (newsgroup name) or a list (if
9812 ;; FROMKILLED is t, it's a list on the format (NUM INFO-LIST),
9813 ;; otherwise it's a list in the format of the `gnus-newsrc-hashtb'
9814 ;; entries. 
9815 ;; LEVEL is the new level of the group, OLDLEVEL is the old level and
9816 ;; PREVIOUS is the group (in hashtb entry format) to insert this group
9817 ;; after. 
9818 (defun gnus-group-change-level (entry level &optional oldlevel
9819                                       previous fromkilled)
9820   (let (group info active num)
9821     ;; Glean what info we can from the arguments
9822     (if (consp entry)
9823         (if fromkilled (setq group (nth 1 entry))
9824           (setq group (car (nth 2 entry))))
9825       (setq group entry))
9826     (if (and (stringp entry)
9827              oldlevel 
9828              (< oldlevel 8))
9829         (setq entry (gnus-gethash entry gnus-newsrc-hashtb)))
9830     (if (and (not oldlevel)
9831              (listp entry))
9832         (setq oldlevel (car (cdr (nth 2 entry)))))
9833     (if (stringp previous)
9834         (setq previous (gnus-gethash previous gnus-newsrc-hashtb)))
9835
9836     (gnus-dribble-enter
9837      (format "(gnus-group-change-level %S %S %S %S %S)" 
9838              group level oldlevel (car (nth 2 previous)) fromkilled))
9839     
9840     ;; Then we remove the newgroup from any old structures, if needed.
9841     ;; If the group was killed, we remove it from the killed or zombie
9842     ;; list. If not, and it is in fact going to be killed, we remove
9843     ;; it from the newsrc hash table and assoc.
9844     (cond ((>= oldlevel 8)
9845            (if (= oldlevel 8)
9846                (setq gnus-zombie-list (delete group gnus-zombie-list))
9847              (setq gnus-killed-list (delete group gnus-killed-list))))
9848           (t
9849            (if (>= level 8)
9850                (progn
9851                  (gnus-sethash (car (nth 2 entry))
9852                                nil gnus-newsrc-hashtb)
9853                  (if (nth 3 entry)
9854                      (setcdr (gnus-gethash (car (nth 3 entry))
9855                                            gnus-newsrc-hashtb)
9856                              (cdr entry)))
9857                  (setcdr (cdr entry) (cdr (cdr (cdr entry))))))))
9858
9859     ;; Finally we enter (if needed) the list where it is supposed to
9860     ;; go, and change the subscription level. If it is to be killed,
9861     ;; we enter it into the killed or zombie list.
9862     (cond ((>= level 8)
9863            (if (= level 8)
9864                (setq gnus-zombie-list (cons group gnus-zombie-list))
9865              (setq gnus-killed-list (cons group gnus-killed-list))))
9866           (t
9867            ;; If the list is to be entered into the newsrc assoc, and
9868            ;; it was killed, we have to create an entry in the newsrc
9869            ;; hashtb format and fix the pointers in the newsrc assoc.
9870            (if (>= oldlevel 8)
9871                (progn
9872                  (if (listp entry)
9873                      (progn
9874                        (setq info (cdr entry))
9875                        (setq num (car entry)))
9876                    (setq active (gnus-gethash group gnus-active-hashtb))
9877                    (setq num (- (1+ (cdr active)) (car active)))
9878                    (setq info (list group level (cons 1 (1- (car active))))))
9879                  (setq entry (cons info (if previous (cdr (cdr previous))
9880                                           (cdr gnus-newsrc-assoc))))
9881                  (setcdr (if previous (cdr previous) gnus-newsrc-assoc)
9882                          entry)
9883                  (gnus-sethash group (cons num (if previous (cdr previous)
9884                                                  gnus-newsrc-assoc))
9885                                gnus-newsrc-hashtb)
9886                  (if (cdr entry)
9887                      (setcdr (gnus-gethash (car (car (cdr entry)))
9888                                            gnus-newsrc-hashtb)
9889                              entry)))
9890              ;; It was alive, and it is going to stay alive, so we
9891              ;; just change the level and don't change any pointers or
9892              ;; hash table entries.
9893              (setcar (cdr (car (cdr (cdr entry)))) level))))))
9894
9895 (defun gnus-kill-newsgroup (newsgroup)
9896   "Obsolete function. Kills a newsgroup."
9897   (gnus-group-change-level (gnus-gethash newsgroup gnus-newsrc-hashtb) 9))
9898
9899 (defun gnus-check-bogus-newsgroups (&optional confirm)
9900   "Delete bogus newsgroups.
9901 If CONFIRM is non-nil, the user has to confirm the deletion of every
9902 newsgroup." 
9903   (let ((newsrc (cdr gnus-newsrc-assoc))
9904         (dead-lists '(gnus-killed-list gnus-zombie-list))
9905         bogus group killed)
9906     (message "Checking bogus newsgroups...")
9907     (if (not gnus-have-read-active-file) (gnus-read-active-file))
9908     ;; Find all bogus newsgroup that are subscribed.
9909     (while newsrc
9910       (setq group (car (car newsrc)))
9911       (if (or (gnus-gethash group gnus-active-hashtb)
9912               (nth 4 (car newsrc))
9913               (and confirm
9914                    (not (y-or-n-p
9915                          (format "Delete bogus newsgroup: %s " group)))))
9916           ;; Active newsgroup.
9917           ()
9918         ;; Found a bogus newsgroup.
9919         (setq bogus (cons group bogus)))
9920       (setq newsrc (cdr newsrc)))
9921     ;; Remove all bogus subscribed groups by first killing them, and
9922     ;; then removing them from the list of killed groups.
9923     (while bogus
9924       (gnus-group-change-level 
9925        (gnus-gethash (car bogus) gnus-newsrc-hashtb) 9)
9926       (setq gnus-killed-list (delq (car bogus) gnus-killed-list))
9927       (setq bogus (cdr bogus)))
9928     ;; Then we remove all bogus groups from the list of killed and
9929     ;; zombie groups. They are are deleted without confirmation.
9930     (while dead-lists
9931       (setq killed (symbol-value (car dead-lists)))
9932       (while killed
9933         (setq group (car killed))
9934         (or (gnus-gethash group gnus-active-hashtb)
9935             ;; The group is bogus.
9936             (setq bogus (cons group bogus)))
9937         (setq killed (cdr killed)))
9938       (while bogus
9939         (set (car dead-lists)
9940              (delq (car bogus) (symbol-value (car dead-lists))))
9941         (setq bogus (cdr bogus)))
9942       (setq dead-lists (cdr dead-lists)))
9943     (message "Checking bogus newsgroups... done")))
9944
9945 ;; Go though `gnus-newsrc-assoc' and compare with `gnus-active-hashtb'
9946 ;; and compute how many unread articles there are in each group.
9947 (defun gnus-get-unread-articles (&optional level)
9948   (let ((newsrc (cdr gnus-newsrc-assoc))
9949         (level (or level 7))
9950         info group active)
9951     (message "Checking new news...")
9952     (while newsrc
9953       (setq info (car newsrc))
9954       (setq group (car info))
9955
9956       ;; Check foreign newsgroups. If the user doesn't want to check
9957       ;; them, or they can't be checked, for instance, if the news
9958       ;; server can't be reached, we just set the number of unread
9959       ;; articles in this newsgroup to t. This means that Gnus
9960       ;; thinks that there are unread articles, but it has no idea how
9961       ;; many. 
9962       (if (nth 4 info)
9963           (and (or (if (numberp gnus-activate-foreign-newsgroups)
9964                        (> (nth 1 info) gnus-activate-foreign-newsgroups)
9965                      (not gnus-activate-foreign-newsgroups))
9966                    (not (gnus-activate-foreign-newsgroup info)))
9967                (progn
9968                  (gnus-sethash group nil gnus-active-hashtb)
9969                  (setcar (gnus-gethash group gnus-newsrc-hashtb) t))))
9970
9971       (if (or (and (> (nth 1 info) level)
9972                    (not (car (gnus-gethash group gnus-newsrc-hashtb)))
9973                    (setcar (gnus-gethash group gnus-newsrc-hashtb) t))
9974               (not (or (setq active (gnus-gethash group gnus-active-hashtb))
9975                        (and (not gnus-read-active-file)
9976                             (setq active (gnus-activate-newsgroup 
9977                                           (car info)))))))
9978           ;; If this is a bogus group, there's not much we can do.
9979           ()
9980         (gnus-get-unread-articles-in-group info active))
9981       (setq newsrc (cdr newsrc)))
9982     (message "Checking new news... done")))
9983
9984 ;; Create a hash table out of the newsrc alist. The `car's of the
9985 ;; alist elements are used as keys.
9986 (defun gnus-make-hashtable-from-newsrc-alist ()
9987   (let ((alist gnus-newsrc-assoc)
9988          prev)
9989     (setq gnus-newsrc-hashtb (gnus-make-hashtable (length alist)))
9990     (setq alist 
9991           (setq prev (setq gnus-newsrc-assoc 
9992                            (cons (list "dummy.group" 0 (cons 0 0)) alist))))
9993     (while alist
9994       (gnus-sethash (car (car alist)) (cons nil prev) gnus-newsrc-hashtb)
9995       (setq prev alist)
9996       (setq alist (cdr alist)))))
9997
9998 (defun gnus-make-hashtable-from-killed ()
9999   "Create a hash table from the killed and zombie lists."
10000   (let ((lists '(gnus-killed-list gnus-zombie-list))
10001         list)
10002     (setq gnus-killed-hashtb 
10003           (gnus-make-hashtable 
10004            (+ (length gnus-killed-list) (length gnus-zombie-list))))
10005     (while lists
10006       (setq list (symbol-value (car lists)))
10007       (setq lists (cdr lists))
10008       (while list
10009         (gnus-sethash (car list) (car list) gnus-killed-hashtb)
10010         (setq list (cdr list))))))
10011
10012 (defun gnus-get-unread-articles-in-group (info active)
10013   (let* (num srange lowest range group)
10014     ;; Modify the list of read articles according to what articles 
10015     ;; are available; then tally the unread articles and add the
10016     ;; number to the group hash table entry.
10017     (setq range (nth 2 info))
10018     (setq num 0)
10019     (cond ((not range)
10020            (setq num (- (1+ (cdr active)) (car active))))
10021           ((atom (car range))
10022            ;; Fix a single (num . num) range according to the
10023            ;; active hash table.
10024            (if (< (cdr range) (car active)) (setcdr range (car active)))
10025            ;; Compute number of unread articles.
10026            (setq num (max 0 (- (cdr active) 
10027                                (- (1+ (cdr range)) (car range))))))
10028           (t
10029            ;; The read list is a list of ranges. Fix them according to
10030            ;; the active hash table.
10031            (setq srange range)
10032            (setq lowest (1- (car active)))
10033            (while (and (< (cdr (car srange)) lowest))
10034              (if (and (cdr srange)
10035                       (<= (cdr (car srange)) (1+ lowest)))
10036                  (progn
10037                    (setcdr (car srange) (cdr (car (cdr srange))))
10038                    (setcdr srange (cdr (cdr srange))))
10039                (setcdr (car srange) lowest)))
10040            ;; Compute the number of unread articles.
10041            (while range
10042              (setq num (+ num (- (1+ (cdr (car range))) 
10043                                  (car (car range)))))
10044              (setq range (cdr range)))
10045            (setq num (max 0 (- (cdr active) num)))))
10046     (setcar (gnus-gethash (car info) gnus-newsrc-hashtb) num)
10047     num))
10048
10049 (defun gnus-activate-foreign-newsgroup (info)
10050   (and (gnus-check-news-server (nth 4 info))
10051        (gnus-activate-newsgroup (car info) (gnus-group-real-name (car info)))))
10052
10053 (defun gnus-activate-newsgroup (group &optional real-group-name)
10054   (let (active)
10055     (if (gnus-request-group group)
10056         (save-excursion
10057           (set-buffer nntp-server-buffer)
10058           (goto-char 1)
10059           (if (looking-at "[0-9]+ [0-9]+ \\([0-9]+\\) \\([0-9]+\\)")
10060               (gnus-sethash group 
10061                (setq active
10062                      (cons (string-to-int (buffer-substring (match-beginning 1)
10063                                                             (match-end 1)))
10064                            (string-to-int 
10065                             (buffer-substring (match-beginning 2) 
10066                                               (match-end 2)))))
10067                gnus-active-hashtb))))
10068     active))
10069
10070 (defun gnus-update-read-articles 
10071   (group unread unselected ticked &optional domarks replied expirable killed
10072          dormant bookmark)
10073   "Update the list of read and ticked articles in GROUP using the
10074 UNREAD and TICKED lists.
10075 Note: UNSELECTED has to be sorted over `<'."
10076   (let* ((active (gnus-gethash group gnus-active-hashtb))
10077          (entry (gnus-gethash group gnus-newsrc-hashtb))
10078          (number (car entry))
10079          (info (nth 2 entry))
10080          (marked (nth 3 info))
10081          (prev 1)
10082          (unread (sort (copy-sequence unread) (function <)))
10083          last read)
10084     (if (not info)
10085         ;; There is no info on this group if it was, in fact,
10086         ;; killed. Gnus stores no information on killed groups, so
10087         ;; there's nothing to be done. 
10088         ;; One could store the information somewhere temporarily,
10089         ;; perhaps... Hmmm... 
10090         ()
10091       ;; Remove any negative articles numbers.
10092       (while (and unread (< (car unread) 0))
10093         (setq unread (cdr unread)))
10094       (if (not (and (numberp number) (= 0 number)))
10095           (setq unread (nconc unselected unread)))
10096       ;; Set the number of unread articles in gnus-newsrc-hashtb.
10097       (or (eq 'nnvirtual (car gnus-current-select-method))
10098           (setcar entry (length unread)))
10099       ;; Compute the ranges of read articles by looking at the list of
10100       ;; unread articles.  
10101       (while unread
10102         (if (/= (car unread) prev)
10103             (setq read (cons (cons prev (1- (car unread))) read)))
10104         (setq prev (1+ (car unread)))
10105         (setq unread (cdr unread)))
10106       (if (<= prev (cdr active))
10107           (setq read (cons (cons prev (cdr active)) read)))
10108       ;; Enter this list into the group info.
10109       (setcar (cdr (cdr info)) 
10110               (if (> (length read) 1) (nreverse read) (car read)))
10111       ;; Enter the list of ticked articles.
10112       (gnus-set-marked-articles 
10113        info ticked
10114        (if domarks replied (cdr (assq 'reply marked)))
10115        (if domarks expirable (cdr (assq 'expire marked)))
10116        (if domarks killed (cdr (assq 'killed marked)))
10117        (if domarks dormant (cdr (assq 'dormant marked)))
10118        (if domarks bookmark (cdr (assq 'bookmark marked)))))))
10119
10120 (defun gnus-make-articles-unread (group articles)
10121   "Mark ARTICLES in GROUP as unread."
10122   (let ((info (nth 2 (or (gnus-gethash group gnus-newsrc-hashtb)
10123                          (gnus-gethash (concat gnus-foreign-group-prefix
10124                                                group)
10125                                        gnus-newsrc-hashtb)))))
10126     (setcar (nthcdr 2 info)
10127             (gnus-remove-from-range (nth 2 info) articles))
10128     (gnus-group-update-group group t)))
10129
10130 (defun gnus-read-active-file ()
10131   "Get active file from NNTP server."
10132   (gnus-group-set-mode-line)
10133   (setq gnus-have-read-active-file t)
10134   ;; Make sure a connection to NNTP server is alive.
10135   (gnus-check-news-server gnus-select-method)
10136   (let ((mesg (format "Reading active file from %s via %s..."
10137                       (nth 1 gnus-select-method) (car gnus-select-method))))
10138     (message mesg)
10139     (if (gnus-request-list gnus-select-method) ; Get active 
10140         (save-excursion
10141           (set-buffer nntp-server-buffer)
10142           (gnus-active-to-gnus-format)
10143           (setq gnus-have-read-active-file t)
10144           (message "%s...done" mesg))
10145       (error "Cannot read active file from NNTP server."))))
10146
10147 ;; rewritten by jwz based on ideas from Rick Sladkey <jrs@world.std.com>
10148 ;; Further rewrites by lmi.
10149 (defun gnus-active-to-gnus-format ()
10150   "Convert active file format to internal format.
10151 Lines matching gnus-ignored-newsgroups are ignored."
10152   (let ((cur (current-buffer)))
10153     ;; Delete unnecessary lines.
10154     (goto-char (point-min))
10155     (delete-matching-lines gnus-ignored-newsgroups)
10156     ;; Make large enough hash table.
10157     (setq gnus-active-hashtb
10158           (gnus-make-hashtable (count-lines (point-min) (point-max))))
10159     ;; Store active file in hashtable.
10160     (save-restriction
10161       (goto-char (point-min))
10162       (if (or (re-search-forward "\n.\r?$" nil t)
10163               (goto-char (point-max)))
10164           (progn
10165             (beginning-of-line)
10166             (narrow-to-region (point-min) (point))))
10167       (goto-char (point-min))
10168       (if (string-match "%[oO]" gnus-group-line-format)
10169           ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
10170           ;; If we want information on moderated groups, we use this
10171           ;; loop...   
10172           (let ((mod-hashtb (make-vector 7 0))
10173                 group max mod)
10174             (while (not (eobp))
10175               (setq group (let ((obarray gnus-active-hashtb))
10176                             (read cur)))
10177               (setq max (read cur))
10178               (set group (cons (read cur) max))
10179               ;; Enter moderated groups into a list.
10180               (if (string= 
10181                    (symbol-name  (let ((obarray mod-hashtb)) (read cur)))
10182                    "m")
10183                   (setq gnus-moderated-list 
10184                         (cons (symbol-name group) gnus-moderated-list)))
10185               (forward-line 1)))
10186         ;; And if we do not care about moderation, we use this loop,
10187         ;; which is faster.
10188         (let (group max)
10189           (while (not (eobp))
10190             ;; group gets set to a symbol interned in gnus-active-hashtb
10191             ;; (what a hack!!)
10192             (setq group (let ((obarray gnus-active-hashtb))
10193                           (read cur)))
10194             (setq max (read cur))
10195             (set group (cons (read cur) max))
10196             (forward-line 1)))))))
10197
10198 (defun gnus-read-newsrc-file (&optional force)
10199   "Read startup file.
10200 If FORCE is non-nil, the .newsrc file is read."
10201   (setq gnus-current-startup-file (gnus-make-newsrc-file gnus-startup-file))
10202   ;; Reset variables that might be defined in the .newsrc.eld file.
10203   (let ((variables gnus-variable-list))
10204     (while variables
10205       (set (car variables) nil)
10206       (setq variables (cdr variables))))
10207   (let* ((newsrc-file gnus-current-startup-file)
10208          (quick-file (concat newsrc-file ".el")))
10209     (save-excursion
10210       ;; We always load the .newsrc.eld file. If always contains
10211       ;; much information that can not be gotten from the .newsrc
10212       ;; file (ticked articles, killed groups, foreign methods, etc.)
10213       (gnus-read-newsrc-el-file quick-file)
10214  
10215       (if (or force
10216               (and (file-newer-than-file-p newsrc-file quick-file)
10217                    (file-newer-than-file-p newsrc-file 
10218                                            (concat quick-file "d")))
10219               (not gnus-newsrc-assoc))
10220           ;; We read the .newsrc file. Note that if there if a
10221           ;; .newsrc.eld file exists, it has already been read, and
10222           ;; the `gnus-newsrc-hashtb' has been created. While reading
10223           ;; the .newsrc file, Gnus will only use the information it
10224           ;; can find there for changing the data already read -
10225           ;; ie. reading the .newsrc file will not trash the data
10226           ;; already read (except for read articles).
10227           (save-excursion
10228             (message "Reading %s..." newsrc-file)
10229             (set-buffer (find-file-noselect newsrc-file))
10230             (buffer-disable-undo (current-buffer))
10231             (gnus-newsrc-to-gnus-format)
10232             (kill-buffer (current-buffer))
10233             (message "Reading %s... done" newsrc-file)))
10234       (gnus-dribble-read-file))))
10235
10236 (defun gnus-read-newsrc-el-file (file)
10237   (let ((ding-file (concat file "d")))
10238     ;; We always, always read the .eld file.
10239     (message "Reading %s..." ding-file)
10240     (condition-case nil
10241         (load ding-file t t t)
10242       (error nil))
10243     (gnus-make-hashtable-from-newsrc-alist)
10244     (if (not (file-newer-than-file-p file ding-file))
10245         ()
10246       ;; Old format quick file
10247       (message "Reading %s..." file)
10248       ;; The .el file is newer than the .eld file, so we read that one
10249       ;; as well. 
10250       (gnus-read-old-newsrc-el-file file))))
10251
10252 ;; Parse the old-style quick startup file
10253 (defun gnus-read-old-newsrc-el-file (file)
10254   (let (newsrc killed marked group g m len info)
10255     (prog1
10256         (let (gnus-killed-assoc gnus-marked-assoc gnus-newsrc-assoc)
10257           (prog1
10258               (condition-case nil
10259                   (load file t t t)
10260                 (error nil))
10261             (setq newsrc gnus-newsrc-assoc
10262                   killed gnus-killed-assoc
10263                   marked gnus-marked-assoc)))
10264       (setq gnus-newsrc-assoc nil)
10265       (while newsrc
10266         (setq group (car newsrc))
10267         (let ((info (nth 2 (gnus-gethash (car group) gnus-newsrc-hashtb))))
10268           (if info
10269               (progn
10270                 (setcar (nthcdr 2 info) (cdr (cdr group)))
10271                 (setcar (cdr info) (if (nth 1 group) 3 6))
10272                 (setq gnus-newsrc-assoc (cons info gnus-newsrc-assoc)))
10273             (setq gnus-newsrc-assoc
10274                   (cons 
10275                    (setq info
10276                          (list (car group)
10277                                (if (nth 1 group) 3 6) (cdr (cdr group))))
10278                    gnus-newsrc-assoc)))
10279           (if (setq m (assoc (car group) marked))
10280             (setcdr (cdr (cdr info)) (cons (list (cons 'tick (cdr m))) nil))))
10281         (setq newsrc (cdr newsrc)))
10282       (setq newsrc killed)
10283       (while newsrc
10284         (setcar newsrc (car (car newsrc)))
10285         (setq newsrc (cdr newsrc)))
10286       (setq gnus-killed-list killed))
10287     (setq gnus-newsrc-assoc (nreverse gnus-newsrc-assoc))
10288     (gnus-make-hashtable-from-newsrc-alist)))
10289       
10290 (defun gnus-make-newsrc-file (file)
10291   "Make server dependent file name by catenating FILE and server host name."
10292   (let* ((file (expand-file-name file nil))
10293          (real-file (concat file "-" (nth 1 gnus-select-method))))
10294     (if (file-exists-p real-file)
10295         real-file file)
10296     ))
10297
10298 ;; jwz: rewrote this function to be much more efficient, and not be subject
10299 ;; to regexp overflow errors when it encounters very long lines -- the old
10300 ;; behavior was to blow off the rest of the *file* when a line was encountered
10301 ;; that was too long to match!!  Now it uses only simple looking-at calls, and
10302 ;; doesn't create as many temporary strings.  It also now handles multiple
10303 ;; consecutive options lines (before it only handled the first.)
10304 ;; Tiny rewrite by lmi. 
10305 (defun gnus-newsrc-to-gnus-format ()
10306   "Parse current buffer as .newsrc file."
10307   ;; We have to re-initialize these variables (except for
10308   ;; gnus-killed-list) because quick startup file may contain bogus
10309   ;; values.
10310   (setq gnus-newsrc-options nil)
10311   (setq gnus-newsrc-options-n-yes nil)
10312   (setq gnus-newsrc-options-n-no nil)
10313   (setq gnus-newsrc-assoc nil)
10314   (gnus-parse-options-lines)
10315   (gnus-parse-newsrc-body))
10316
10317 (defun gnus-parse-options-lines ()
10318   ;; newsrc.5 seems to indicate that the options line can come anywhere
10319   ;; in the file, and that there can be any number of them:
10320   ;;
10321   ;;       An  options  line  starts  with  the  word  options (left-
10322   ;;       justified).  Then there are the list of  options  just  as
10323   ;;       they would be on the readnews command line.  For instance:
10324   ;;
10325   ;;       options -n all !net.sf-lovers !mod.human-nets -r
10326   ;;       options -c -r
10327   ;;
10328   ;;       A string of lines beginning with a space or tab after  the
10329   ;;       initial  options  line  will  be  considered  continuation
10330   ;;       lines.
10331   ;;
10332   ;; For now, we only accept it at the beginning of the file.
10333
10334   (goto-char (point-min))
10335   (skip-chars-forward " \t\n")
10336   (setq gnus-newsrc-options nil)
10337   (while (looking-at "^options[ \t]*\\(.*\\)\n")
10338     ;; handle consecutive options lines
10339     (setq gnus-newsrc-options (concat gnus-newsrc-options
10340                                       (if gnus-newsrc-options "\n\t")
10341                                       (buffer-substring (match-beginning 1)
10342                                                         (match-end 1))))
10343     (forward-line 1)
10344     (while (looking-at "[ \t]+\\(.*\\)\n")
10345       ;; handle subsequent continuation lines of this options line
10346       (setq gnus-newsrc-options (concat gnus-newsrc-options "\n\t"
10347                                         (buffer-substring (match-beginning 1)
10348                                                           (match-end 1))))
10349       (forward-line 1)))
10350   ;; Gather all "-n" options lines.
10351   (let ((start 0)
10352         (result nil))
10353     (if gnus-newsrc-options
10354         (while (and (string-match "^[ \t]*-n\\([^\n]*\\)$"
10355                                   gnus-newsrc-options
10356                                   start)
10357                     (setq start (match-end 0)))
10358           (setq result (concat result
10359                                (and result " ")
10360                                (substring gnus-newsrc-options
10361                                           (match-beginning 1)
10362                                           (match-end 1))))))
10363     (let ((yes-and-no (and result (gnus-parse-n-options result))))
10364       (setq gnus-newsrc-options-n-yes (car yes-and-no))
10365       (setq gnus-newsrc-options-n-no (cdr yes-and-no)))
10366     nil))
10367
10368 (defun gnus-parse-newsrc-body ()
10369   ;; Point has been positioned after the options lines.  We shouldn't
10370   ;; see any more in here.
10371
10372   (let ((subscribe nil)
10373         (read-list nil)
10374         (line (1+ (count-lines (point-min) (point))))
10375         newsgroup
10376         p p2)
10377     (save-restriction
10378       (skip-chars-forward " \t")
10379       (while (not (eobp))
10380         (cond
10381          ((= (following-char) ?\n)
10382           ;; skip blank lines
10383           nil)
10384          (t
10385           (setq p (point))
10386           (skip-chars-forward "^:!\n")
10387           (if (= (following-char) ?\n)
10388               (error "line %d is unparsable in %s" line (buffer-name)))
10389           (setq p2 (point))
10390           (skip-chars-backward " \t")
10391
10392           ;; #### note: we could avoid consing a string here by binding obarray
10393           ;; and reading the newsgroup directly into the gnus-newsrc-hashtb,
10394           ;; then setq'ing newsgroup to symbol-name of that, like we do in
10395           ;; gnus-active-to-gnus-format.
10396           (setq newsgroup (buffer-substring p (point)))
10397           (goto-char p2)
10398
10399           (setq subscribe (= (following-char) ?:))
10400           (setq read-list nil)
10401
10402           (forward-char 1)              ; after : or !
10403           (skip-chars-forward " \t")
10404           (while (not (= (following-char) ?\n))
10405             (skip-chars-forward " \t")
10406             (or
10407              (and (cond
10408                    ((looking-at "\\([0-9]+\\)-\\([0-9]+\\)") ; a range
10409                     (setq read-list
10410                           (cons
10411                            (cons
10412                             (progn
10413                               ;; faster that buffer-substring/string-to-int
10414                               (narrow-to-region (point-min) (match-end 1))
10415                               (read (current-buffer)))
10416                             (progn
10417                               (narrow-to-region (point-min) (match-end 2))
10418                               (forward-char) ; skip over "-"
10419                               (prog1
10420                                   (read (current-buffer))
10421                                 (widen))))
10422                            read-list))
10423                     t)
10424                    ((looking-at "[0-9]+")
10425                     ;; faster that buffer-substring/string-to-int
10426                     (narrow-to-region (point-min) (match-end 0))
10427                     (setq p (read (current-buffer)))
10428                     (widen)
10429                     (setq read-list (cons (cons p p) read-list))
10430                     t)
10431                    (t
10432                     ;; bogus chars in ranges
10433                     nil))
10434                   (progn
10435                     (goto-char (match-end 0))
10436                     (skip-chars-forward " \t")
10437                     (cond ((= (following-char) ?,)
10438                            (forward-char 1)
10439                            t)
10440                           ((= (following-char) ?\n)
10441                            t)
10442                           (t
10443                            ;; bogus char after range
10444                            nil))))
10445              ;; if we get here, the parse failed
10446              (progn
10447                (end-of-line)            ; give up on this line
10448                (ding)
10449                (message "Ignoring bogus line %d for %s in %s"
10450                         line newsgroup (buffer-name))
10451                (sleep-for 1))))
10452           (if read-list
10453               (let ((info (nth 2 (gnus-gethash newsgroup gnus-newsrc-hashtb))))
10454                 (if info
10455                     (progn
10456                       (setcar (nthcdr 2 info) (nreverse read-list))
10457                       (setcar (cdr info) (if subscribe 3 6))
10458                       (setq gnus-newsrc-assoc (cons info gnus-newsrc-assoc)))
10459                   (setq gnus-newsrc-assoc
10460                         (cons (list newsgroup (if subscribe 3 6) 
10461                                     (nreverse read-list))
10462                               gnus-newsrc-assoc))))
10463             (setq gnus-killed-list (cons newsgroup gnus-killed-list)))))
10464         (setq line (1+ line))
10465         (forward-line 1))))
10466   (setq gnus-newsrc-assoc (nreverse gnus-newsrc-assoc))
10467   (gnus-make-hashtable-from-newsrc-alist)
10468   nil)
10469
10470 (defun gnus-parse-n-options (options)
10471   "Parse -n NEWSGROUPS options and return a cons of YES and NO regexps."
10472   (let ((yes nil)
10473         (no nil)
10474         (yes-or-no nil)                 ;`!' or not.
10475         (newsgroup nil))
10476     ;; Parse each newsgroup description such as "comp.all".  Commas
10477     ;; and white spaces can be a newsgroup separator.
10478     (while
10479         (string-match "^[ \t\n,]*\\(!?\\)\\([^- \t\n,][^ \t\n,]*\\)" options)
10480       (setq yes-or-no
10481             (substring options (match-beginning 1) (match-end 1)))
10482       (setq newsgroup
10483             (regexp-quote
10484              (substring options
10485                         (match-beginning 2) (match-end 2))))
10486       (setq options (substring options (match-end 2)))
10487       ;; Rewrite "all" to ".+" not ".*".  ".+" requires at least one
10488       ;; character.
10489       (while (string-match "\\(^\\|\\\\[.]\\)all\\(\\\\[.]\\|$\\)" newsgroup)
10490         (setq newsgroup
10491               (concat (substring newsgroup 0 (match-end 1))
10492                       ".+"
10493                       (substring newsgroup (match-beginning 2)))))
10494       ;; It is yes or no.
10495       (cond ((string-equal yes-or-no "!")
10496              (setq no (cons newsgroup no)))
10497             ((string-equal newsgroup ".+")) ;Ignore `all'.
10498             (t
10499              (setq yes (cons newsgroup yes))))
10500       )
10501     ;; Make a cons of regexps from parsing result.
10502     ;; We have to append \(\.\|$\) to prevent matching substring of
10503     ;; newsgroup.  For example, "jp.net" should not match with
10504     ;; "jp.network".
10505     ;; Fixes for large regexp problems are from yonezu@nak.math.keio.ac.jp.
10506     (cons (if yes
10507               (concat "^\\("
10508                       (apply (function concat)
10509                              (mapcar
10510                               (lambda (newsgroup)
10511                                 (concat newsgroup "\\|"))
10512                               (cdr yes)))
10513                       (car yes) "\\)\\(\\.\\|$\\)"))
10514           (if no
10515               (concat "^\\("
10516                       (apply (function concat)
10517                              (mapcar
10518                               (lambda (newsgroup)
10519                                 (concat newsgroup "\\|"))
10520                               (cdr no)))
10521                       (car no) "\\)\\(\\.\\|$\\)")))
10522     ))
10523
10524 (defun gnus-save-newsrc-file ()
10525   "Save to .newsrc FILE."
10526   ;; Note: We cannot save .newsrc file if all newsgroups are removed
10527   ;; from the variable gnus-newsrc-assoc.
10528   (and (or gnus-newsrc-assoc gnus-killed-list)
10529        gnus-current-startup-file
10530        (save-excursion
10531          (if (= 0 (save-excursion
10532                     (set-buffer gnus-dribble-buffer)
10533                     (buffer-size)))
10534              (message "(No changes need to be saved)")
10535            (if gnus-save-newsrc-file
10536                (let ((make-backup-files t)
10537                      (version-control nil)
10538                      (require-final-newline t)) ;Don't ask even if requested.
10539                  (message "Saving %s..." gnus-current-startup-file)
10540                  ;; Make backup file of master newsrc.
10541                  ;; You can stop or change version control of backup file.
10542                  ;; Suggested by jason@violet.berkeley.edu.
10543                  (run-hooks 'gnus-save-newsrc-hook)
10544                  (gnus-gnus-to-newsrc-format)
10545                  (message "Saving %s... done" gnus-current-startup-file)))
10546            ;; Quickly loadable .newsrc.
10547            (set-buffer (get-buffer-create " *Gnus-newsrc*"))
10548            (gnus-add-current-to-buffer-list)
10549            (buffer-disable-undo (current-buffer))
10550            (erase-buffer)
10551            (message "Saving %s.eld..." gnus-current-startup-file)
10552            (gnus-gnus-to-quick-newsrc-format)
10553            (let ((make-backup-files nil)
10554                  (version-control nil)
10555                  (require-final-newline t)) ;Don't ask even if requested.
10556              (write-region 1 (point-max) 
10557                            (concat gnus-current-startup-file ".eld") 
10558                            nil 'nomesg))
10559            (kill-buffer (current-buffer))
10560            (message "Saving %s.eld... done" gnus-current-startup-file)
10561            (gnus-dribble-delete-file)))))
10562
10563 (defun gnus-gnus-to-quick-newsrc-format ()
10564   "Insert Gnus variables such as gnus-newsrc-assoc in lisp format."
10565   (insert ";; (ding) Gnus startup file.\n")
10566   (insert ";; Never delete this file - touch .newsrc instead to force Gnus\n")
10567   (insert ";; to read .newsrc.\n")
10568   (let ((variables gnus-variable-list)
10569         (gnus-newsrc-assoc (cdr gnus-newsrc-assoc))
10570         variable)
10571     ;; insert lisp expressions.
10572     (while variables
10573       (setq variable (car variables))
10574       (and (boundp variable)
10575            (symbol-value variable)
10576            (or gnus-save-killed-list 
10577                (not (or (eq variable 'gnus-killed-list)
10578                         (eq variable 'gnus-zombie-list))))
10579            (insert "(setq " (symbol-name variable) " '"
10580                    (prin1-to-string (symbol-value variable))
10581                    ")\n"))
10582       (setq variables (cdr variables)))))
10583
10584 (defun gnus-gnus-to-newsrc-format ()
10585   (let ((newsrc (cdr gnus-newsrc-assoc))
10586         group ranges)
10587     (save-excursion
10588       (set-buffer (create-file-buffer gnus-startup-file))
10589       (buffer-disable-undo (current-buffer))
10590       (erase-buffer)
10591       (if gnus-newsrc-options (insert "options " gnus-newsrc-options "\n"))
10592       (while newsrc
10593         (setq group (car newsrc))
10594         (insert (car group) (if (>= (nth 1 group) 6) "!" ":"))
10595         (if (setq ranges (nth 2 group))
10596             (progn
10597               (insert " ")
10598               (gnus-ranges-to-newsrc-format
10599                (if (atom (car ranges)) (list ranges) ranges))))
10600         (insert "\n")
10601         (setq newsrc (cdr newsrc)))
10602       (write-region 1 (point-max) gnus-current-startup-file nil 'nomesg)
10603       (kill-buffer (current-buffer)))))
10604
10605 (defun gnus-ranges-to-newsrc-format (ranges)
10606   "Insert ranges of read articles."
10607   (let ((range nil))                    ;Range is a pair of BEGIN and END.
10608     (while ranges
10609       (setq range (car ranges))
10610       (setq ranges (cdr ranges))
10611       (cond ((= (car range) (cdr range))
10612              (if (= (car range) 0)
10613                  (setq ranges nil)      ;No unread articles.
10614                (insert (int-to-string (car range)))
10615                (if ranges (insert ","))
10616                ))
10617             (t
10618              (insert (int-to-string (car range))
10619                      "-"
10620                      (int-to-string (cdr range)))
10621              (if ranges (insert ","))
10622              ))
10623       )))
10624
10625 (defun gnus-read-descriptions-file ()
10626   (message "Reading descriptions file...")
10627   (if (not (gnus-request-list-newsgroups gnus-select-method))
10628       (progn
10629         (message "Couldn't read newsgroups descriptions")
10630         nil)
10631     (let (group)
10632       (setq gnus-description-hashtb 
10633             (gnus-make-hashtable (length gnus-active-hashtb)))
10634       (save-excursion
10635         (save-restriction
10636           (set-buffer nntp-server-buffer)
10637           (goto-char (point-min))
10638           (delete-non-matching-lines "^[a-zA-Z\\.0-9]+[ \t]")
10639           (goto-char (point-min))
10640           (if (or (search-forward "\n.\n" nil t)
10641                   (goto-char (point-max)))
10642               (progn
10643                 (beginning-of-line)
10644                 (narrow-to-region (point-min) (point))))
10645           (goto-char (point-min))
10646           (while (not (eobp))
10647             (setq group (let ((obarray gnus-description-hashtb))
10648                           (read (current-buffer))))
10649             (skip-chars-forward " \t")
10650             (set group (buffer-substring 
10651                         (point) (save-excursion (end-of-line) (point))))
10652             (forward-line 1))))
10653       (message "Reading descriptions file...done")
10654       t)))
10655
10656 (provide 'gnus)
10657
10658 ;;; gnus.el ends here