Initial revision
[gnus] / lisp / gnus.el
1 ;;; (ding) Gnus: a newsreader for GNU Emacs
2 ;; Copyright (C) 1987,88,89,90,93,94 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 'nnheader)
39 (require 'rmail)
40 (require 'nnmail)
41
42 (defvar gnus-select-method 
43   (list 'nntp (or (getenv "NNTPSERVER") 
44                   (if (and gnus-default-nntp-server
45                            (not (string= gnus-default-nntp-server "")))
46                       gnus-default-nntp-server)
47                   (system-name))
48         "nntp")
49   "*Default method for selecting a newsgroup.
50 This variable should be a list, where the first element is how the
51 news is to be fetched, the second is the address, and the optional
52 third element is the \"port number\", if nntp is used.
53
54 For instance, if you want to get your news via NNTP from
55 \"flab.flab.edu\" on port 23, you could say:
56
57 (setq gnus-select-method '(nntp \"flab.flab.edu\" 23))
58
59 If you want to use your local spool, say:
60
61 (setq gnus-select-method (list 'nnspool (system-name)))
62
63 If you use this variable, you must set `gnus-nntp-server' to nil.")
64
65 ;; By Sudish Joseph <joseph@cis.ohio-state.edu>.
66 (defvar gnus-post-method nil
67   "*Preferred method for posting USENET news.
68 If this variable is nil, GNUS will use the current method to decide
69 which method to use when posting.  If it is non-nil, it will override
70 the current method. This method will not be used in mail groups and
71 the like, only in \"real\" newsgroups.
72
73 The value must be a valid method as discussed in the documentation of
74 `gnus-select-method'.")
75
76 (defvar gnus-default-nntp-server nil
77   "*Specify a default NNTP server.
78 This variable should be defined in paths.el.")
79
80 (defvar gnus-secondary-servers nil
81   "*List of NNTP servers that the user can choose between interactively.
82 The list should contain lists, where each list contains the name of
83 the server. To make Gnus query you for a server, you have to give
84 `gnus' a non-numeric prefix - `C-u M-x gnus', in short.")
85
86 (defvar gnus-nntp-server nil
87   "*The name of the host running the NNTP server.
88 This variable is semi-obsolete. Use the `gnus-select-method'
89 variable instead.")
90
91 (defvar gnus-nntp-service "nntp"
92   "NNTP service name (\"nntp\" or 119).
93 This is an obsolete variable, which is scarcely used. If you use an
94 nntp server for your newsgroup and want to change the port number
95 used to 899, you would say something along these lines:
96
97 (setq gnus-select-method '(nntp "my.nntp.server" 899))")
98
99 (defvar gnus-startup-file "~/.newsrc"
100   "*Your `.newsrc' file.  Use `.newsrc-SERVER' instead if it exists.")
101
102 (defvar gnus-signature-file "~/.signature"
103   "*Your `.signature' file.")
104
105 (defvar gnus-init-file "~/.gnus"
106   "*Your Gnus elisp startup file.
107 If a file with the .el or .elc suffixes exist, they will be read
108 instead.") 
109
110 (defvar gnus-default-subscribed-newsgroups nil
111   "*This variable lists what newsgroups should be susbcribed the first time Gnus is used.
112 It should be a list of strings.
113 If it is `t', Gnus will not do anything special the first time it is
114 started; it'll just use the normal newsgroups subscription methods.")
115
116 (defconst gnus-backup-default-subscribed-newsgroups 
117   '("news.announce.newusers" "news.groups.questions")
118   "Default default new newsgroups the first time Gnus is run.")
119
120 (defvar gnus-post-prepare-function nil
121   "*Function that is run after a post buffer has been prepared.
122 It is called with the name of the newsgroup that is posted to. It
123 might be use, for instance, for inserting signatures based on the
124 newsgroup name. (In that case, `gnus-signature-file' and
125 `mail-signature' should both be set to nil).")
126
127 (defvar gnus-use-cross-reference t
128   "*Non-nil means that cross referenced articles will be marked as read.
129 If nil, ignore cross references.  If t, mark articles as read in
130 all newsgroups.")
131
132 (defvar gnus-use-followup-to t
133   "*Specifies what to do with Followup-To field.
134 If nil, ignore the field. If it is t, use its value, but ignore 
135 `poster'. If it is neither nil nor t, always use the value.")
136
137 (defvar gnus-followup-to-function nil
138   "*A variable that contains a function that returns a followup address.
139 The function will be called in the buffer of the article that is being
140 followed up. The buffer will be narrowed to the headers of the
141 article. To pick header fields, one might use `mail-fetch-field'.  The
142 function will be called with the name of the current newsgroup as the
143 argument.
144
145 Here's an example `gnus-followup-to-function':
146
147 (setq gnus-followup-to-function
148       (lambda (group)
149         (cond ((string= group \"mail.list\")
150                (or (mail-fetch-field \"sender\") 
151                    (mail-fetch-field \"from\")))
152               (t
153                (or (mail-fetch-field \"reply-to\") 
154                    (mail-fetch-field \"from\"))))))")
155
156 (defvar gnus-reply-to-function nil
157   "*A variable that contains a function that returns a reply address.
158 See the `gnus-followup-to-function' variable for an explanation of how
159 this variable is used.")
160
161 (defvar gnus-large-newsgroup 50
162   "*The number of articles which indicates a large newsgroup.
163 If the number of articles in a newsgroup is greater than the value,
164 confirmation is required for selecting the newsgroup.")
165
166 (defvar gnus-author-copy (getenv "AUTHORCOPY")
167   "*Name of the file the article will be saved before it is posted using the FCC: field.
168 Initialized from the AUTHORCOPY environment variable.
169
170 Articles are saved using a function specified by the the variable
171 `gnus-author-copy-saver' (`rmail-output' is default) if a file name is
172 given.  Instead, if the first character of the name is `|', the
173 contents of the article is piped out to the named program. It is
174 possible to save an article in an MH folder as follows:
175
176 \(setq gnus-author-copy \"|/usr/local/lib/mh/rcvstore +Article\")")
177
178 (defvar gnus-author-copy-saver (function rmail-output)
179   "*A function called with a file name to save an author copy to.
180 The default function is `rmail-output' which saves in Unix mailbox format.")
181
182 (defvar gnus-use-long-file-name (not (memq system-type '(usg-unix-v xenix)))
183   "*Non-nil means that the default name of a file to save articles in is the newsgroup name.
184 If it's nil, the directory form of the newsgroup name is used instead.")
185
186 (defvar gnus-article-save-directory (getenv "SAVEDIR")
187   "*Name of the directory articles will be saved in (default \"~/News\").
188 Initialized from the SAVEDIR environment variable.")
189
190 (defvar gnus-kill-files-directory (getenv "SAVEDIR")
191   "*Name of the directory where kill files will be stored (default \"~/News\").
192 Initialized from the SAVEDIR environment variable.")
193
194 (defvar gnus-default-article-saver (function gnus-summary-save-in-rmail)
195   "*A function to save articles in your favorite format.
196 The function must be interactively callable (in other words, it must
197 be an Emacs command).
198
199 Gnus provides the following functions:
200
201 * gnus-summary-save-in-rmail (Rmail format)
202 * gnus-summary-save-in-mail (Unix mail format)
203 * gnus-summary-save-in-folder (MH folder)
204 * gnus-summary-save-in-file (article format).")
205
206 (defvar gnus-rmail-save-name (function gnus-plain-save-name)
207   "*A function generating a file name to save articles in Rmail format.
208 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
209
210 (defvar gnus-mail-save-name (function gnus-plain-save-name)
211   "*A function generating a file name to save articles in Unix mail format.
212 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
213
214 (defvar gnus-folder-save-name (function gnus-folder-save-name)
215   "*A function generating a file name to save articles in MH folder.
216 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER.")
217
218 (defvar gnus-file-save-name (function gnus-numeric-save-name)
219   "*A function generating a file name to save articles in article format.
220 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
221
222 (defvar gnus-kill-file-name "KILL"
223   "*Suffix of the kill files.")
224
225 (defvar gnus-novice-user t
226   "*Non-nil means that you are a usenet novice.
227 If non-nil, verbose messages may be displayed and confirmations may be
228 required.")
229
230 (defvar gnus-expert-user nil
231   "*Non-nil means that you will never be asked for confirmation about anything.
232 And that means *anything*.")
233
234 (defvar gnus-keep-same-level nil
235   "*Non-nil means that the next newsgroup after the current will be on the same level.
236 When you type, for instance, `n' after reading the last article in the
237 current newsgroup, you will go to the next newsgroup. If this variable
238 is nil, the next newsgroup will be the next from the Newsgroup
239 buffer. If this variable is non-nil, Gnus will either put you in the
240 next newsgroup with the same level, or, if no such newsgroup is
241 available, the next newsgroup with the lowest possible level higher
242 than the current level.")
243
244 (defvar gnus-gather-loose-threads t
245   "*Non-nil means sub-threads from a common thread will be gathered.
246 If the root of a thread has expired or been read in a previous
247 session, the information necessary to build a complete thread has been
248 lost. Instead of having many small sub-threads from this original thread
249 scattered all over the Summary buffer, Gnus will gather them. If the
250 `gnus-summary-make-false-root' variable is non-nil, Gnus will also
251 present them as one thread with a new root.")
252
253 (defvar gnus-summary-make-false-root 'adopt
254   "*nil means that Gnus won't print dummy roots of threads in the summary buffer.
255 If `gnus-gather-loose-threads' is non-nil, Gnus will try to gather all
256 loose sub-threads from an original thread into one large thread. If
257 this variable is nil, these sub-threads will not get a common root,
258 but will just be presented after one another. If this variable is
259 `dummy', Gnus will create a dummy root that will have all the
260 sub-threads as children.
261 If this variable is `adopt', Gnus will make one of the \"children\"
262 the parent and mark all the step-children as such.")
263
264 (defvar gnus-check-new-newsgroups t
265   "*Non-nil means that Gnus will add new newsgroups at startup.
266 If this variable is nil, then you have to tell Gnus explicitly to
267 check for new newsgroups with \\<gnus-group-mode-map>\\[gnus-find-new-newsgroups].")
268
269 (defvar gnus-check-bogus-newsgroups nil
270   "*Non-nil means that Gnus will check and delete bogus newsgroup at startup.
271 If this variable is nil, then you have to tell Gnus explicitly to
272 check for bogus newsgroups with \\<gnus-group-mode-map>\\[gnus-group-check-bogus-groups].")
273
274 (defvar gnus-read-active-file t
275   "*Non-nil means that Gnus will read the entire active file at startup.
276 If this variable is nil, Gnus will only read parts of the active file.")
277
278 (defvar gnus-activate-foreign-newsgroups nil
279   "*If nil, Gnus will not check foreign newsgroups at startup.
280 If it is non-nil, it should be a number between one and nine. Foreign
281 newsgroups that have a level lower or equal to this number will be
282 activated on startup. For instance, if you want to active all
283 subscribed newsgroups, but not the rest, you'd set this variable to 5.
284
285 If you subscribe to lots of newsgroups from different servers, startup
286 might take a while. By setting this variable to nil, you'll save time,
287 but you won't be told how many unread articles there are in the
288 newsgroups.")
289
290 (defvar gnus-save-newsrc-file t
291   "*Non-nil means that Gnus will save a .newsrc file.
292 Gnus always saves its own startup file, which is called \".newsrc.el\".
293 The file called \".newsrc\" is in a format that can be readily
294 understood by other newsreaders. If you don't plan on using other
295 newsreaders, set this variable to nil to save some time on exit.")
296
297 (defvar gnus-save-killed-list t
298   "If non-nil, save the list of killed groups to the startup file.
299 This will save both time (when starting and quitting) and space (on
300 disk), but it will also mean that Gnus has no record of what
301 newsgroups are new or old, so the automatic new newsgroups
302 subscription methods become meaningless. You should always set
303 `gnus-check-new-newsgroups' to nil if you set this variable to nil.") 
304
305 (defvar gnus-interactive-catchup t
306   "*Require your confirmation when catching up a newsgroup if non-nil.")
307
308 (defvar gnus-interactive-post t
309   "*Newsgroup and subject will be asked for if non-nil.")
310
311 (defvar gnus-interactive-exit t
312   "*Require your confirmation when exiting Gnus if non-nil.")
313
314 (defvar gnus-kill-killed t
315   "*If non-nil, Gnus will apply kill files to already \"killed\" articles.
316 If it is nil, Gnus will never apply kill files to articles that have
317 already been through the kill process, which might very well save lots
318 of time.")
319
320 (defvar gnus-user-login-name nil
321   "*The login name of the user.
322 Got from the function `user-login-name' if undefined.")
323
324 (defvar gnus-user-full-name nil
325   "*The full name of the user.
326 Got from the NAME environment variable if undefined.")
327
328 (defvar gnus-show-mime nil
329   "*Show MIME message if non-nil.")
330
331 (defvar gnus-show-threads t
332   "*Show conversation threads in Summary Mode if non-nil.")
333
334 (defvar gnus-thread-hide-subtree nil
335   "*Non-nil means hide thread subtrees initially.
336 If non-nil, you have to run the command `gnus-summary-show-thread' by
337 hand or by using `gnus-select-article-hook' to show hidden threads.")
338
339 (defvar gnus-thread-hide-killed t
340   "*Non-nil means hide killed thread subtrees automatically.")
341
342 (defvar gnus-thread-ignore-subject nil
343   "*Don't take care of subject differences, but only references if non-nil.
344 If it is non-nil, some commands work with subjects do not work properly.")
345
346 (defvar gnus-thread-indent-level 4
347   "*Indentation of thread subtrees.")
348
349 ;; jwz: nuke newsgroups whose name is all digits - that means that
350 ;; some loser has let articles get into the root of the news spool,
351 ;; which is toxic. Lines beginning with whitespace also tend to be
352 ;; toxic.
353 (defvar gnus-ignored-newsgroups
354   (purecopy (mapconcat 'identity
355                        '("^to\\."               ; not "real" groups
356                          "^[0-9. \t]+ "         ; all digits in name
357                          "[][\"#'();\\]"        ; bogus characters
358                          )
359                        "\\|"))
360   "*A regexp to match uninteresting newsgroups in the active file.
361 Any lines in the active file matching this regular expression are
362 removed from the newsgroup list before anything else is done to it,
363 thus making them effectively non-existant.")
364
365 (defvar gnus-ignored-headers
366   "^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:"
367   "All header lines that match this regexp will be hidden.
368 Also see `gnus-visible-headers'.")
369
370 (defvar gnus-visible-headers "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|Followup-To:\\|Organization:"
371   "All header lines that do not match this regexp will be hidden.
372 Also see `gnus-ignored-headers'.")
373
374 (defvar gnus-sorted-header-list
375   '("^From:" "^Subject:" "^Newsgroups:" "^Date:" "^Organization:")
376   "This variable is a list of regular expressions.
377 If it is non-nil, header lines that match the regular expressions will
378 be placed first in the Article buffer in the sequence specified by
379 this list.")
380
381 (defvar gnus-required-headers
382   '(From Date Newsgroups Subject Message-ID Organization Lines X-Newsreader)
383   ;; changed by jwz because it's not so nice to do "Lines: 0" by default.
384   ;; and to remove Path, since it's incorrect for Gnus to try
385   ;; and generate that - it is the responsibility of inews or nntpd.
386   "*All required fields for articles you post.
387 RFC977 and RFC1036 require From, Date, Newsgroups, Subject, Message-ID
388 and Path fields.  Organization, Lines and X-Newsreader are optional.
389 If you want Gnus not to insert some field, remove it from this
390 variable.")
391
392 (defvar gnus-show-all-headers nil
393   "*Show all headers of an article if non-nil.")
394
395 (defvar gnus-save-all-headers t
396   "*Save all headers of an article if non-nil.")
397
398 (defvar gnus-inhibit-startup-message nil
399   "The startup message will not be displayed if this function is non-nil.")
400
401 (defvar gnus-auto-extend-newsgroup t
402   "*Extend visible articles to forward and backward if non-nil.")
403
404 (defvar gnus-auto-select-first t
405   "*Select the first unread article automagically if non-nil.
406 If you want to prevent automatic selection of the first unread article
407 in some newsgroups, set the variable to nil in `gnus-select-group-hook'
408 or `gnus-apply-kill-hook'.")
409
410 (defvar gnus-auto-select-next t
411   "*Select the next newsgroup automagically if non-nil.
412 If the value is t and the next newsgroup is empty, Gnus will exit
413 Summary mode and go back to Group mode.  If the value is neither nil
414 nor t, Gnus will select the following unread newsgroup.  Especially, if
415 the value is the symbol `quietly', the next unread newsgroup will be
416 selected without any confirmations.")
417
418 (defvar gnus-auto-select-same nil
419   "*Select the next article with the same subject automagically if non-nil.")
420
421 (defvar gnus-auto-center-summary t
422   "*Always center the current summary in Gnus Summary window if non-nil.")
423
424 (defvar gnus-auto-mail-to-author nil
425   "*Insert `To: author' of the article when following up if non-nil.
426 Mail is sent using the function specified by the variable
427 `gnus-mail-send-method'.")
428
429 (defvar gnus-break-pages t
430   "*Break an article into pages if non-nil.
431 Page delimiter is specified by the variable `gnus-page-delimiter'.")
432
433 (defvar gnus-page-delimiter "^\^L"
434   "*Regexp describing line-beginnings that separate pages of news article.")
435
436 (defvar gnus-digest-show-summary t
437   "*Show a summary of undigestified messages if non-nil.")
438
439 (defvar gnus-digest-separator "^Subject:[ \t]"
440   "*Regexp that separates messages in a digest article.")
441
442 (defvar gnus-use-full-window t
443   "*Non-nil means to take up the entire screen of Emacs.")
444
445 (defvar gnus-window-configuration
446   '((summary (0 1 0))
447     (newsgroups (1 0 0))
448     (article (0 3 10)))
449   "*Specify window configurations for each action.
450 The format of the variable is either a list of (ACTION (G S A)), where
451 G, S, and A are the relative height of Group, Summary, and Article
452 windows, respectively, or a list of (ACTION FUNCTION), where FUNCTION
453 is a function that will be called with ACTION as an argument. ACTION
454 can be `summary', `newsgroups', or `article'.")
455
456 (defvar gnus-show-mime-method (function metamail-buffer)
457   "*Function to process a MIME message.
458 The function is expected to process current buffer as a MIME message.")
459
460 (defvar gnus-mail-reply-method
461   (function gnus-mail-reply-using-mail)
462   "*Function to compose reply mail.
463 The function `gnus-mail-reply-using-mail' uses usual sendmail mail
464 program.  The function `gnus-mail-reply-using-mhe' uses the MH-E mail
465 program.  You can use yet another program by customizing this variable.")
466
467 (defvar gnus-mail-forward-method
468   (function gnus-mail-forward-using-mail)
469   "*Function to forward current message to another user.
470 The function `gnus-mail-reply-using-mail' uses usual sendmail mail
471 program.  You can use yet another program by customizing this variable.")
472
473 (defvar gnus-mail-other-window-method
474   (function gnus-mail-other-window-using-mail)
475   "*Function to compose mail in other window.
476 The function `gnus-mail-other-window-using-mail' uses the usual sendmail
477 mail program.  The function `gnus-mail-other-window-using-mhe' uses the MH-E
478 mail program.  You can use yet another program by customizing this variable.")
479
480 (defvar gnus-mail-send-method send-mail-function
481   "*Function to mail a message too which is being posted as an article.
482 The message must have To: or Cc: field.  The default is copied from
483 the variable `send-mail-function'.")
484
485 (defvar gnus-subscribe-newsgroup-method
486   (function gnus-subscribe-zombies)
487   "*Function called with a newsgroup name when new newsgroup is found.
488 The function `gnus-subscribe-randomly' inserts a new newsgroup a the
489 beginning of newsgroups.  The function `gnus-subscribe-alphabetically'
490 inserts it in strict alphabetic order.  The function
491 `gnus-subscribe-hierarchically' inserts it in hierarchical newsgroup
492 order.  The function `gnus-subscribe-interactively' asks for your decision.")
493
494 ;; Suggested by a bug report by Hallvard B Furuseth
495 ;; <h.b.furuseth@usit.uio.no>. 
496 (defvar gnus-subscribe-options-newsgroup-method
497   (function gnus-subscribe-alphabetically)
498   "*This function is called to subscribe newsgroups mentioned on \"options -n\" lines.
499 If, for instance, you want to subscribe to all newsgroups in the
500 \"no\" and \"alt\" hierarchies, you'd put the following in your
501 .newsrc file:
502
503 options -n no.all alt.all
504
505 Gnus will the subscribe all new newsgroups in these hierarchies with
506 the subscription method in this variable.")
507
508 (defvar gnus-group-mode-hook nil
509   "*A hook for Gnus Group Mode.")
510
511 (defvar gnus-summary-mode-hook nil
512   "*A hook for Gnus Summary Mode.")
513
514 (defvar gnus-article-mode-hook nil
515   "*A hook for Gnus Article Mode.")
516
517 (defvar gnus-kill-file-mode-hook nil
518   "*A hook for Gnus KILL File Mode.")
519
520 (defvar gnus-open-server-hook nil
521   "*A hook called just before opening connection to news server.")
522
523 (defvar gnus-startup-hook nil
524   "*A hook called at startup time.
525 This hook is called after Gnus is connected to the NNTP server. So, it
526 is possible to change the behavior of Gnus according to the selected
527 NNTP server.")
528
529 (defvar gnus-group-prepare-hook nil
530   "*A hook called after the newsgroup list is created in the Newsgroup buffer.
531 If you want to modify the Newsgroup buffer, you can use this hook.")
532
533 (defvar gnus-summary-prepare-hook nil
534   "*A hook called after summary list is created in the Summary buffer.
535 If you want to modify the Summary buffer, you can use this hook.")
536
537 (defvar gnus-article-prepare-hook nil
538   "*A hook called after an article is prepared in the Article buffer.
539 If you want to run a special decoding program like nkf, use this hook.")
540
541 (defvar gnus-article-display-hook '(gnus-article-hide-headers-if-wanted)
542   "A hook called after the article is displayed in the Article buffer.
543 The hook is designed to change the contents of the Article
544 buffer. Typical functions that this hook may contain are
545 `gnus-article-hide-headers' and `gnus-article-hide-signature'.")
546
547 (defvar gnus-select-group-hook nil
548   "*A hook called when a newsgroup is selected.
549 If you want to sort Summary buffer by date and then by subject, you
550 can use the following hook:
551
552  (setq gnus-select-group-hook
553       (list
554         (lambda ()
555           ;; First of all, sort by date.
556           (gnus-keysort-headers
557            (function string-lessp)
558             (lambda (a)
559               (gnus-sortable-date (gnus-header-date a))))
560           ;; Then sort by subject string ignoring `Re:'.
561           ;; If case-fold-search is non-nil, case of letters is ignored.
562           (gnus-keysort-headers
563            (function string-lessp)
564             (lambda (a)
565               (if case-fold-search
566                   (downcase (gnus-simplify-subject (gnus-header-subject a) t))
567                 (gnus-simplify-subject (gnus-header-subject a) t)))))))
568
569 If you'd like to simplify subjects like the
570 `gnus-summary-next-same-subject' command does, you can use the
571 following hook:
572
573  (setq gnus-select-group-hook
574       (list
575         (lambda ()
576           (mapcar (lambda (header)
577                      (header-set-subject
578                       header
579                       (gnus-simplify-subject
580                        (gnus-header-subject header) 're-only)))
581                   gnus-newsgroup-headers))))
582 ")
583
584 (defvar gnus-select-article-hook
585   '(gnus-summary-show-thread)
586   "*A hook called when an article is selected.
587 The default hook shows conversation thread subtrees of the selected
588 article automatically using `gnus-summary-show-thread'.
589
590 If you'd like to run RMAIL on a digest article automagically, you can
591 use the following hook:
592
593 \(setq gnus-select-article-hook
594       (list
595         (lambda ()
596           (gnus-summary-show-thread)
597           (cond ((string-equal \"comp.sys.sun\" gnus-newsgroup-name)
598                  (gnus-summary-rmail-digest))
599                 ((and (string-equal \"comp.text\" gnus-newsgroup-name)
600                       (string-match \"^TeXhax Digest\"
601                                     (gnus-header-subject gnus-current-headers)))
602                  (gnus-summary-rmail-digest)
603                  )))))")
604
605 (defvar gnus-select-digest-hook
606   (list
607     (lambda ()
608       ;; Reply-To: is required by `undigestify-rmail-message'.
609       (or (mail-position-on-field "Reply-to" t)
610           (progn
611             (mail-position-on-field "Reply-to")
612             (insert (gnus-fetch-field "From"))))))
613   "*A hook called when reading digest messages using Rmail.
614 This hook can be used to modify incomplete digest articles as follows
615 \(this is the default):
616
617 \(setq gnus-select-digest-hook
618       (list
619         (lambda ()
620           ;; Reply-To: is required by `undigestify-rmail-message'.
621           (or (mail-position-on-field \"Reply-to\" t)
622               (progn
623                 (mail-position-on-field \"Reply-to\")
624                 (insert (gnus-fetch-field \"From\")))))))")
625
626 (defvar gnus-rmail-digest-hook nil
627   "*A hook called when reading digest messages using Rmail.
628 This hook is intended to customize Rmail mode for reading digest articles.")
629
630 (defvar gnus-apply-kill-hook '(gnus-apply-kill-file)
631   "*A hook called when a newsgroup is selected and summary list is prepared.
632 This hook is intended to apply a KILL file to the selected newsgroup.
633 The function `gnus-apply-kill-file' is called by default.
634
635 Since a general KILL file is too heavy to use only for a few
636 newsgroups, I recommend you to use a lighter hook function. For
637 example, if you'd like to apply a KILL file to articles which contains
638 a string `rmgroup' in subject in newsgroup `control', you can use the
639 following hook:
640
641 \(setq gnus-apply-kill-hook
642       (list
643         (lambda ()
644           (cond ((string-match \"control\" gnus-newsgroup-name)
645                  (gnus-kill \"Subject\" \"rmgroup\")
646                  (gnus-expunge \"X\"))))))")
647
648 (defvar gnus-prepare-article-hook (list (function gnus-inews-insert-signature))
649   "*A hook called after preparing body, but before preparing header fields.
650 The default hook (`gnus-inews-insert-signature') inserts a signature
651 file specified by the variable `gnus-signature-file'.")
652
653 (defvar gnus-inews-article-hook (list (function gnus-inews-do-fcc))
654   "*A hook called before finally posting an article.
655 The default hook (`gnus-inews-do-fcc') does FCC processing (save article
656 to a file).")
657
658 (defvar gnus-exit-group-hook nil
659   "*A hook called when exiting (not quitting) Summary mode.
660 If your machine is so slow that exiting from Summary mode takes very
661 long time, set the variable `gnus-use-cross-reference' to nil. This
662 inhibits marking articles as read using cross-reference information.")
663
664 (defvar gnus-suspend-gnus-hook nil
665   "*A hook called when suspending (not exiting) Gnus.")
666
667 (defvar gnus-exit-gnus-hook (list 'nntp-request-close)
668   "*A hook called when exiting Gnus.")
669
670 (defvar gnus-save-newsrc-hook nil
671   "*A hook called when saving the newsrc file.
672 This hook is called before saving the `.newsrc' file.")
673
674 (defvar gnus-auto-expirable-newsgroups nil
675   "*All newsgroups that match this regexp will have all read articles automatically marked as expirable.")
676
677 (defvar gnus-subscribe-hierarchical-interactive nil
678   "*If non-nil, Gnus will offer to subscribe hierarchically.
679 When a new hierarchy appears, Gnus will ask the user:
680
681 'alt.binaries': Do you want to subscribe to this hierarchy? ([d]ys):
682
683 If the user pressed `d', Gnus will descend the hierarchy, `y' will
684 subscribe to all newsgroups in the hierarchy and `s' will skip this
685 hierarchy in its entirety.")
686
687 (defvar gnus-group-line-format "%M%S%5N: %G %O %z\n"
688   "*Format of Newsgroups lines.
689 It works along the same lines as a normal formatting string,
690 with some simple extrensions.
691
692 %M    Only marked articles (character, \"*\" or \" \")
693 %S    Whether the group is subscribed (character, \"U\", \"K\", \"Z\" or \" \")
694 %L    Level of subscribedness (integer, 1-9)
695 %N    Number of unread articles (integer)
696 %G    Group name (string)
697 %D    Newsgroup description (string)
698 %s    Select method (string)
699 %o    Moderated group (char, \"m\")
700 %O    Moderated group (string, \"(m)\" or \"\")
701 %n    Select from where (string)
702 %z    A string that look like `<%s:%n>' if a foreign select method is used
703
704 Note that this format specification is not always respected. For
705 reasons of efficiency, when listing killed groups, this specification
706 is ignored altogether. If the spec is changed considerably, your
707 output may end up looking strange when listing both alive and killed
708 groups.
709
710 If you use %o or %O, reading the active file will be slower and quite
711 a bit of extra memory will be used. %D will also worsen performance.
712 Also note that if you change the format specification to include any
713 of these specs, you must probably re-start Gnus to see them go into
714 effect.") 
715
716 (defvar gnus-summary-line-format "%U%R%X %I%[%4L: %-20,20n%] %s\n"
717   "*The format specification of the lines in the Summary buffer.
718 The first specification must always be \"%U%R%X\", at least in this
719 version of Gnus.
720
721 It works along the same lines as a normal formatting string,
722 with some simple extensions.
723
724 %N   Article number, left padded with spaces (integer)
725 %S   Subject (string)
726 %s   Subject if it is at the root of a thread, and \"\" otherwise (string)
727 %n   Name of the poster (string)
728 %A   Address of the poster (string)
729 %L   Number of lines in the article (integer)
730 %D   Date of the article (string)
731 %I   Indentation based on thread level (a string of spaces)
732 %T   A string with two possible values: 80 spaces if the article
733      is on thread level two or larger and 0 spaces on level one
734 %C   This is the current article (character, \"+\" or \" \")
735 %U   Status of this article (character, \"D\", \"K\", \"-\" or \" \") 
736 %[   Opening bracket (character, \"[\" or \"=\")
737 %]   Closing bracket (character, \"]\" or \"=\")
738 ")
739
740 (defconst gnus-summary-dummy-line-format "*   :                          : %S\n"
741   "*The format specification for the dummy roots in the Summary buffer.
742 It works along the same lines as a normal formatting string,
743 with some simple extensions.
744
745 %S  The subject")
746
747 (defvar gnus-summary-mode-line-format "(ding) %G/%A %Z"
748   "*The format specification for the Summary mode line.")
749
750 (defvar gnus-article-mode-line-format "(ding) %G/%A %S"
751   "*The format specification for the Article mode line.")
752
753 (defconst gnus-group-mode-line-format "(ding) List of Newsgroups   {%M:%S}"
754   "*The format specification for the Newsgroup mode line.")
755
756
757 \f
758 ;; Site dependent variables. You have to define these variables in
759 ;;  site-init.el, default.el or your .emacs.
760
761 (defvar gnus-local-timezone nil
762   "*Local time zone.
763 This value is used only if `current-time-zone' does not work in your Emacs.
764 It specifies the GMT offset, i.e. a decimal integer
765 of the form +-HHMM giving the hours and minutes ahead of (i.e. east of) GMT.
766 For example, +0900 should be used in Japan, since it is 9 hours ahead of GMT.
767
768 For backwards compatibility, it may also be a string like \"JST\",
769 but strings are obsolescent: you should use numeric offsets instead.")
770
771 (defvar gnus-local-domain nil
772   "*Local domain name without a host name like: \"stars.flab.Fujitsu.CO.JP\"
773 The `DOMAINNAME' environment variable is used instead if defined.  If
774 the function (system-name) returns the full internet name, there is no
775 need to define the name.")
776
777 (defvar gnus-local-organization nil
778   "*Local organization like: \"Fujitsu Laboratories Ltd., Kawasaki, Japan.\"
779 The `ORGANIZATION' environment variable is used instead if defined.")
780
781 (defvar gnus-use-generic-from nil
782   "*If nil, prepend local host name to the defined domain in the From:
783 field; if stringp, use this; if non-nil, strip of the local host name.")
784
785 (defvar gnus-use-generic-path nil
786   "*If nil, use the NNTP server name in the Path: field; if stringp,
787 use this; if non-nil, use no host name (user name only)")
788
789 (defvar gnus-valid-select-methods
790   '(("nntp" post) ("nnspool" post) ("nnvirtual" none) 
791     ("nnmail" mail respool) ("nnml" mail respool))
792   "A list of valid select methods.
793 Each element in this list should be a list. The first element of these
794 lists should be a string with the name of the select method. The
795 other elements may be be the category of this method (ie. `post',
796 `mail', `none' or whatever) or other properties that this method has
797 (like being respoolable). 
798 If you implement a new select method, all you should have to change is
799 this variable. I think.")
800
801 (defvar gnus-updated-mode-lines '(group article summary)
802   "This variable is a list of buffers that should keep their mode lines updated.
803 The list may contain the symbols `group', `article' and `summary'. If
804 the corresponding symbol is present, Gnus will keep that mode line
805 updated with information that may be pertinent. 
806 If this variable is nil, screen refresh may be quicker.")
807
808 \f
809 ;; Internal variables.
810
811 (defvar caesar-translate-table nil)
812
813 (defvar gnus-dribble-buffer nil)
814
815 (defvar gnus-article-reply nil)
816
817 (defvar gnus-newsgroup-dependencies nil)
818
819 (defconst gnus-group-edit-buffer "*Gnus edit newsgroup*")
820
821 (defvar gnus-default-subscribe-level 2
822   "Default subscription level.")
823
824 (defvar gnus-default-unsubscribe-level 6
825   "Default unsubscription level.")
826
827 (defvar gnus-default-kill-level 9
828   "Default kill level.")
829
830 (defconst gnus-group-line-format-alist
831   (list (list ?M 'marked ?c)
832         (list ?S 'subscribed ?c)
833         (list ?L 'level ?d)
834         (list ?N 'number ?s)
835         (list ?G 'group ?s)
836         (list ?D 'newsgroup-description ?s)
837         (list ?o 'moderated ?c)
838         (list ?O 'moderated-string ?s)
839         (list ?s 'news-server ?s)
840         (list ?n 'news-method ?s)
841         (list ?z 'news-method-string ?s)))
842
843 (defconst gnus-summary-line-format-alist 
844   (list (list ?N 'number ?d)
845         (list ?S 'subject ?s)
846         (list ?s 'subject-or-nil ?s)
847         (list ?n 'name ?s)
848         (list ?A 'address ?s)
849         (list ?F 'from ?s)
850         (list ?x (macroexpand '(header-xref header)) ?s)
851         (list ?D (macroexpand '(header-date header)) ?s)
852         (list ?M (macroexpand '(header-id header)) ?s)
853         (list ?r (macroexpand '(header-references header)) ?s)
854         (list ?L 'lines ?d)
855         (list ?I 'indentation ?s)
856         (list ?T 'thread-space ?s)
857         (list ?C 'current ?c)
858         (list ?R 'replied ?c)
859         (list ?X 'expirable ?c)
860         (list ?\[ 'opening-bracket ?c)
861         (list ?\] 'closing-bracket ?c)
862         (list ?U 'unread ?c))
863   "An alist of format specifications that can appear in summary lines,
864 and what variables they correspond with, along with the type of the
865 variable (string, integer, character, etc).")
866
867 (defconst gnus-summary-dummy-line-format-alist
868   (list (list ?S 'subject ?s)
869         (list ?N 'number ?d)))
870
871 (defconst gnus-summary-mode-line-format-alist 
872   (list (list ?G 'group-name ?s)
873         (list ?A 'article-number ?d)
874         (list ?Z 'unread-and-unselected ?s)
875         (list ?V 'gnus-version ?s)
876         (list ?U 'unread ?d)
877         (list ?S 'subject ?s)
878         (list ?u 'unselected ?d)))
879
880 (defconst gnus-group-mode-line-format-alist 
881   (list (list ?S 'news-server ?s)
882         (list ?M 'news-method ?s)))
883
884 (defvar gnus-have-read-active-file nil)
885
886 (defconst gnus-foreign-group-prefix "foreign.")
887
888 (defconst gnus-version "(ding) Gnus v0.5"
889   "Version numbers of this version of Gnus.")
890
891 (defvar gnus-info-nodes
892   '((gnus-group-mode            "(gnus)Newsgroup Commands")
893     (gnus-summary-mode          "(gnus)Summary Commands")
894     (gnus-article-mode          "(gnus)Article Commands")
895     (gnus-kill-file-mode        "(gnus)Kill File"))
896   "Assoc list of major modes and related Info nodes.")
897
898 (defvar gnus-group-buffer "*Newsgroup*")
899 (defvar gnus-summary-buffer "*Summary*")
900 (defvar gnus-article-buffer "*Article*")
901 (defvar gnus-digest-buffer "Gnus Digest")
902 (defvar gnus-digest-summary-buffer "Gnus Digest-summary")
903
904 (defvar gnus-buffer-list nil
905   "Gnus buffers that should be killed when exiting.")
906
907 (defvar gnus-variable-list
908   '(gnus-newsrc-options
909     gnus-newsrc-options-n-yes gnus-newsrc-options-n-no
910     gnus-newsrc-assoc gnus-killed-list gnus-zombie-list)
911   "Gnus variables saved in the quick startup file.")
912
913 (defvar gnus-overload-functions
914   '((news-inews gnus-inews-news "rnewspost")
915     (caesar-region gnus-caesar-region "rnews"))
916   "Functions overloaded by gnus.
917 It is a list of `(original overload &optional file)'.")
918
919 (defvar gnus-newsrc-options nil
920   "Options line in the .newsrc file.")
921
922 (defvar gnus-newsrc-options-n-yes nil
923   "Regexp representing subscribed newsgroups.")
924
925 (defvar gnus-newsrc-options-n-no nil
926   "Regexp representing unsubscribed newsgroups.")
927
928 (defvar gnus-newsrc-assoc nil
929   "Assoc list of read articles.
930 gnus-newsrc-hashtb should be kept so that both hold the same information.")
931
932 (defvar gnus-newsrc-hashtb nil
933   "Hashtable of gnus-newsrc-assoc.")
934
935 (defvar gnus-killed-list nil
936   "List of killed newsgroups.")
937
938 (defvar gnus-killed-hashtb nil
939   "Hash table equivalent of gnus-killed-list.")
940
941 (defvar gnus-zombie-list nil
942   "List of almost dead newsgroups.")
943
944 (defvar gnus-description-hashtb nil
945   "Descriptions of newsgroups (from the file 'newsgroups').")
946
947 (defvar gnus-list-of-killed-groups nil
948   "List of newsgroups that have recently been killed by the user.")
949
950 (defvar gnus-xref-hashtb nil
951   "Hash table of cross-posted articles.")
952
953 (defvar gnus-active-hashtb nil
954   "Hashtable of active articles.")
955
956 (defvar gnus-moderated-list nil
957   "List of moderated newsgroups.")
958
959 (defvar gnus-current-startup-file nil
960   "Startup file for the current host.")
961
962 (defvar gnus-last-search-regexp nil
963   "Default regexp for article search command.")
964
965 (defvar gnus-last-shell-command nil
966   "Default shell command on article.")
967
968 (defvar gnus-current-select-method nil
969   "The current method for selecting a newsgroup.")
970
971 (defvar gnus-have-all-newsgroups nil)
972
973 (defvar gnus-article-internal-prepare-hook nil)
974
975 (defvar gnus-newsgroup-name nil)
976 (defvar gnus-newsgroup-begin nil)
977 (defvar gnus-newsgroup-end nil)
978 (defvar gnus-newsgroup-last-rmail nil)
979 (defvar gnus-newsgroup-last-mail nil)
980 (defvar gnus-newsgroup-last-folder nil)
981 (defvar gnus-newsgroup-last-file nil)
982 (defvar gnus-newsgroup-auto-expire nil
983   "If non-nil, all read articles will be marked as expirable.")
984
985 (defvar gnus-newsgroup-unreads nil
986   "List of unread articles in the current newsgroup.")
987
988 (defvar gnus-newsgroup-unselected nil
989   "List of unselected unread articles in the current newsgroup.")
990
991 (defvar gnus-newsgroup-marked nil
992   "List of ticked articles in the current newsgroup (a subset of unread art).")
993
994 (defvar gnus-newsgroup-killed nil
995   "List of ranges of articles that have been through the kill process.")
996
997 (defvar gnus-newsgroup-replied nil
998   "List of articles that have been replied to in the current newsgroup.")
999
1000 (defvar gnus-newsgroup-expirable nil
1001   "List of articles in the current newsgroup that can be expired.")
1002
1003 (defvar gnus-newsgroup-processable nil
1004   "List of articles in the current newsgroup that can be processed.")
1005
1006 (defvar gnus-newsgroup-bookmarks nil
1007   "List of articles in the current newsgroup that have bookmarks.")
1008
1009 (defvar gnus-newsgroup-interesting nil
1010   "List of interesting articles in the current newsgroup.")
1011
1012 (defvar gnus-newsgroup-headers nil
1013   "List of article headers in the current newsgroup.")
1014 (defvar gnus-newsgroup-headers-hashtb-by-number nil)
1015
1016 (defvar gnus-current-article nil)
1017 (defvar gnus-article-current nil)
1018 (defvar gnus-current-headers nil)
1019 (defvar gnus-have-all-headers nil "Must be either T or NIL.")
1020 (defvar gnus-last-article nil)
1021 (defvar gnus-current-kill-article nil)
1022 (defvar gnus-newsgroup-interesting-subjects nil)
1023
1024 ;; Save window configuration.
1025 (defvar gnus-winconf-kill-file nil)
1026
1027 (defconst gnus-group-mode-map nil)
1028 (defvar gnus-summary-mode-map nil)
1029 (defvar gnus-article-mode-map nil)
1030 (defvar gnus-kill-file-mode-map nil)
1031
1032 ;; Format specs
1033 (defvar gnus-summary-line-format-spec nil)
1034 (defvar gnus-summary-dummy-line-format-spec nil)
1035 (defvar gnus-group-line-format-spec nil)
1036 (defvar gnus-summary-mode-line-format-spec nil)
1037 (defvar gnus-article-mode-line-format-spec nil)
1038 (defvar gnus-group-mode-line-format-spec nil)
1039
1040 (defvar gnus-reffed-article-number nil)
1041
1042 (defvar rmail-default-file (expand-file-name "~/XMBOX"))
1043 (defvar rmail-default-rmail-file (expand-file-name "~/XNEWS"))
1044
1045 (defvar gnus-summary-local-variables 
1046   '(gnus-newsgroup-name
1047     gnus-newsgroup-begin gnus-newsgroup-end gnus-newsgroup-last-rmail
1048     gnus-newsgroup-last-mail gnus-newsgroup-last-folder
1049     gnus-newsgroup-last-file gnus-newsgroup-auto-expire
1050     gnus-newsgroup-unreads gnus-newsgroup-unselected gnus-newsgroup-marked
1051     gnus-newsgroup-replied gnus-newsgroup-expirable
1052     gnus-newsgroup-processable gnus-newsgroup-killed
1053     gnus-newsgroup-bookmarks gnus-newsgroup-interesting
1054     gnus-newsgroup-interesting-subjects
1055     gnus-newsgroup-headers gnus-newsgroup-headers-hashtb-by-number
1056     gnus-current-article gnus-current-headers gnus-have-all-headers
1057     gnus-last-article gnus-article-internal-prepare-hook))
1058
1059 (defvar gnus-mark-article-hook
1060   (list
1061    (lambda ()
1062      (or (memq gnus-current-article gnus-newsgroup-marked)
1063          (memq gnus-current-article gnus-newsgroup-interesting)
1064          (gnus-summary-mark-as-read gnus-current-article))))
1065   "*A hook called when an article is selected at the first time.
1066 The hook is intended to mark an article as read (or unread)
1067 automatically when it is selected.
1068
1069 If you'd like to tick articles instead, use the following hook:
1070
1071 \(setq gnus-mark-article-hook
1072       (list
1073         (lambda ()
1074           (gnus-summary-tick-article gnus-current-article)
1075           (gnus-summary-set-current-mark \"+\"))))")
1076
1077 ;; Define some autoload functions Gnus may use.
1078 (eval-and-compile
1079   (autoload 'metamail-buffer "metamail")
1080   (autoload 'Info-goto-node "info")
1081   
1082   (autoload 'timezone-make-date-arpa-standard "timezone")
1083   (autoload 'timezone-fix-time "timezone")
1084   (autoload 'timezone-make-sortable-date "timezone")
1085   (autoload 'timezone-make-time-string "timezone")
1086   
1087   (autoload 'rmail-output "rmailout"
1088     "Append this message to Unix mail file named FILE-NAME." t)
1089   (autoload 'mail-position-on-field "sendmail")
1090
1091   (autoload 'gnus-mail-reply-using-mhe "gnus-mh")
1092   (autoload 'gnus-mail-forward-using-mhe "gnus-mh")
1093   (autoload 'gnus-mail-other-window-using-mhe "gnus-mh")
1094   (autoload 'gnus-summary-save-in-folder "gnus-mh")
1095   (autoload 'gnus-Folder-save-name "gnus-mh")
1096   (autoload 'gnus-folder-save-name "gnus-mh"))
1097
1098 (put 'gnus-group-mode 'mode-class 'special)
1099 (put 'gnus-summary-mode 'mode-class 'special)
1100 (put 'gnus-article-mode 'mode-class 'special)
1101
1102 (autoload 'gnus-uu-ctl-map "gnus-uu" nil nil 'keymap)
1103 \f
1104
1105 ;; Fix by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
1106 (defun gnus-summary-position-cursor () nil)
1107 (defun gnus-group-position-cursor () nil)
1108 (fset 'gnus-summary-position-cursor 'gnus-goto-colon)
1109 (fset 'gnus-group-position-cursor 'gnus-goto-colon)
1110
1111 (defmacro gnus-eval-in-buffer-window (buffer &rest forms)
1112   "Pop to BUFFER, evaluate FORMS, and then returns to original window."
1113   (` (let ((GnusStartBufferWindow (selected-window)))
1114        (unwind-protect
1115            (progn
1116              (pop-to-buffer (, buffer))
1117              (,@ forms))
1118          (select-window GnusStartBufferWindow)))))
1119
1120 (defun gnus-make-hashtable (&optional hashsize)
1121   "Make a hash table (default and minimum size is 255).
1122 Optional argument HASHSIZE specifies the table size."
1123   (make-vector (if hashsize 
1124                    (max (gnus-create-hash-size hashsize) 255)
1125                  255) 0))
1126
1127 (defmacro gnus-gethash (string hashtable)
1128   "Get hash value of STRING in HASHTABLE."
1129   ;;(` (symbol-value (abbrev-symbol (, string) (, hashtable))))
1130   ;;(` (abbrev-expansion (, string) (, hashtable)))
1131   (` (symbol-value (intern-soft (, string) (, hashtable)))))
1132
1133 (defmacro gnus-sethash (string value hashtable)
1134   "Set hash value. Arguments are STRING, VALUE, and HASHTABLE."
1135   ;; We cannot use define-abbrev since it only accepts string as value.
1136 ;  (set (intern string hashtable) value))
1137   (` (set (intern (, string) (, hashtable)) (, value))))
1138
1139 (defsubst gnus-buffer-substring (beg end)
1140   (buffer-substring (match-beginning beg) (match-end end)))
1141
1142 (defsubst gnus-simplify-subject-re (subject)
1143   "Remove \"Re:\" from subject lines."
1144   (if (string-match "^[Rr][Ee]:[ \t]*" subject)
1145       (substring subject (match-end 0))
1146     subject))
1147
1148 \f
1149 ;;;
1150 ;;; Gnus Utility Functions
1151 ;;;
1152
1153 (defsubst gnus-extract-address-components (from)
1154   (let (name address)
1155     (if (string-match "([^)]+)" from)
1156         (setq name (substring from (1+ (match-beginning 0)) 
1157                               (1- (match-end 0)))))
1158     (if (string-match "\\b[^@ \t<>]+@[^@ \t<>]+\\b" from)
1159         (setq address (substring from (match-beginning 0) (match-end 0))))
1160     (if (and (not name) address)
1161         (if (string-match (concat "<" address ">") from)
1162             (setq name (substring from 0 (1- (match-beginning 0))))))
1163     (cons (or name from) (or address from))))
1164
1165 (defun gnus-fetch-field (field)
1166   "Return the value of the header FIELD of current article."
1167   (save-excursion
1168     (save-restriction
1169       (gnus-narrow-to-headers)
1170       (mail-fetch-field field))))
1171
1172 (defun gnus-goto-colon ()
1173   (beginning-of-line)
1174   (search-forward ":" (save-excursion (end-of-line) (point)) t))
1175
1176 (defun gnus-prefs-p (&rest values)
1177   (< 0 
1178      (apply '+ (mapcar 
1179                 (lambda (v)
1180                   (if (consp v)
1181                       (* (cdr v)
1182                          (or (car v)
1183                              (cdr (assq (car v) gnus-user-preferences))
1184                              ;; Check if the user said (novice)
1185                              ;; instead of (novice . 100)
1186                              (if (memq (car v) gnus-user-preferences) 100)
1187                              (cdr (assq (car v) gnus-default-preferences))
1188                              0))
1189                     v))
1190                 values))))
1191
1192 (defun gnus-narrow-to-headers ()
1193   (widen)
1194   (save-excursion
1195     (goto-char 1)
1196     (if (search-forward "\n\n")
1197         (narrow-to-region 1 (1- (point))))))
1198
1199 ;; Get a number that is suitable for hashing; bigger than MIN
1200 (defun gnus-create-hash-size (min)
1201   (let ((i 1))
1202     (while (< i min)
1203       (setq i (* 2 i)))
1204     (1- i)))
1205
1206 (defun gnus-update-format-specifications ()
1207   (setq gnus-summary-line-format-spec 
1208         (gnus-parse-format gnus-summary-line-format 
1209                            gnus-summary-line-format-alist))
1210   (setq gnus-summary-dummy-line-format-spec 
1211         (gnus-parse-format gnus-summary-dummy-line-format 
1212                            gnus-summary-dummy-line-format-alist))
1213   (if (and (memq 'newsgroup-description
1214                  (cdr (cdr (setq gnus-group-line-format-spec 
1215                                  (gnus-parse-format 
1216                                   gnus-group-line-format 
1217                                   gnus-group-line-format-alist)))))
1218            (not gnus-description-hashtb))
1219       (gnus-read-descriptions-file))
1220   (setq gnus-summary-mode-line-format-spec 
1221         (gnus-parse-format gnus-summary-mode-line-format 
1222                            gnus-summary-mode-line-format-alist))
1223   (setq gnus-article-mode-line-format-spec 
1224         (gnus-parse-format gnus-article-mode-line-format 
1225                            gnus-summary-mode-line-format-alist))
1226   (setq gnus-group-mode-line-format-spec 
1227         (gnus-parse-format gnus-group-mode-line-format 
1228                            gnus-group-mode-line-format-alist)))
1229
1230 (defun gnus-format-max-width (var length)
1231   (let (result)
1232     (if (> (length (setq result (eval var))) length)
1233         (format "%s" (substring result 0 length))
1234       (format "%s" result))))
1235
1236 (defun gnus-parse-format (format spec-alist)
1237 ;; This function parses the FORMAT string with the help of the
1238 ;; SPEC-ALIST and returns a list that can be eval'ed to return the
1239 ;; string. The list will consist of the symbol `format', a format
1240 ;; specification string, and a list of forms depending on the
1241 ;; SPEC-ALIST.
1242   (let ((max-width 0)
1243         spec flist fstring b newspec max-width elem beg)
1244     (save-excursion
1245       (set-buffer (get-buffer-create "*gnus work*"))
1246       (buffer-disable-undo (current-buffer))
1247       (gnus-add-current-to-buffer-list)
1248       (erase-buffer)
1249       (insert format)
1250       (goto-char 1)
1251       (while (re-search-forward "%[-0-9]*\\(,[0-9]*\\)*\\(.\\)" nil t)
1252         (setq spec (string-to-char (buffer-substring (match-beginning 2)
1253                                                      (match-end 2))))
1254         ;; First check if there are any specs that look anything like
1255         ;; "%12,12A", ie. with a "max width specification". These have
1256         ;; to be treated specially.
1257         (if (setq beg (match-beginning 1))
1258             (setq max-width 
1259                   (string-to-int 
1260                    (buffer-substring (1+ (match-beginning 1)) (match-end 1))))
1261           (setq max-width 0)
1262           (setq beg (match-beginning 2)))
1263         ;; Find the specification from `spec-alist'.
1264         (if (not (setq elem (cdr (assq spec spec-alist))))
1265             (setq elem '("*" ?s)))
1266         (if (not (= max-width 0))
1267             (progn
1268               (setq flist (cons (list 'gnus-format-max-width 
1269                                       (car elem) max-width) flist))
1270               (setq newspec ?s))
1271           (setq flist (cons (car elem) flist))
1272           (setq newspec (car (cdr elem))))
1273         ;; Remove the old specification (and possibly a ",12" string).
1274         (delete-region beg (match-end 2))
1275         ;; Insert the new specification.
1276         (goto-char beg)
1277         (insert newspec))
1278       (setq fstring (buffer-substring 1 (point-max)))
1279       (kill-buffer (current-buffer)))
1280     (cons 'format (cons fstring (nreverse flist)))))
1281
1282 ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
1283 (defun gnus-read-init-file ()
1284   (if (and gnus-init-file
1285            (file-exists-p gnus-init-file))
1286       (load gnus-init-file nil t)))
1287
1288 ;; Article file names when saving.
1289
1290 (defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
1291   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1292 If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group/num.
1293 Otherwise, it is like ~/News/news/group/num."
1294   (let ((default
1295           (expand-file-name
1296            (concat (if gnus-use-long-file-name
1297                        (gnus-capitalize-newsgroup newsgroup)
1298                      (gnus-newsgroup-directory-form newsgroup))
1299                    "/" (int-to-string (header-number headers)))
1300            (or gnus-article-save-directory "~/News"))))
1301     (if (and last-file
1302              (string-equal (file-name-directory default)
1303                            (file-name-directory last-file))
1304              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1305         default
1306       (or last-file default))))
1307
1308 (defun gnus-numeric-save-name (newsgroup headers &optional last-file)
1309   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1310 If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group/num.
1311 Otherwise, it is like ~/News/news/group/num."
1312   (let ((default
1313           (expand-file-name
1314            (concat (if gnus-use-long-file-name
1315                        newsgroup
1316                      (gnus-newsgroup-directory-form newsgroup))
1317                    "/" (int-to-string (header-number headers)))
1318            (or gnus-article-save-directory "~/News"))))
1319     (if (and last-file
1320              (string-equal (file-name-directory default)
1321                            (file-name-directory last-file))
1322              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1323         default
1324       (or last-file default))))
1325
1326 (defun gnus-Plain-save-name (newsgroup headers &optional last-file)
1327   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1328 If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group.
1329 Otherwise, it is like ~/News/news/group/news."
1330   (or last-file
1331       (expand-file-name
1332        (if gnus-use-long-file-name
1333            (gnus-capitalize-newsgroup newsgroup)
1334          (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
1335        (or gnus-article-save-directory "~/News"))))
1336
1337 (defun gnus-plain-save-name (newsgroup headers &optional last-file)
1338   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1339 If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group.
1340 Otherwise, it is like ~/News/news/group/news."
1341   (or last-file
1342       (expand-file-name
1343        (if gnus-use-long-file-name
1344            newsgroup
1345          (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
1346        (or gnus-article-save-directory "~/News"))))
1347
1348 ;; For subscribing new newsgroup
1349
1350 (defun gnus-subscribe-hierarchical-interactive (groups)
1351   (let ((groups (sort groups 'string<))
1352         prefixes prefix start rest ans group starts)
1353     (while groups
1354       (setq prefixes (list "^"))
1355       (while (and groups prefixes)
1356         (while (not (string-match (car prefixes) (car groups)))
1357           (setq prefixes (cdr prefixes)))
1358         (setq prefix (car prefixes))
1359         (setq start (1- (length prefix)))
1360         (if (and (string-match "[^\\.]\\." (car groups) start)
1361                  (cdr groups)
1362                  (setq prefix 
1363                        (concat "^" (substring (car groups) 0 (match-end 0))))
1364                  (string-match prefix (car (cdr groups))))
1365             (progn
1366               (setq prefixes (cons prefix prefixes))
1367               (message "Descend hierarchy %s'? ([y]nsq): " 
1368                        (substring prefix 1 (1- (length prefix))))
1369               (setq ans (read-char))
1370               (cond ((= ans ?n)
1371                      (while (and groups 
1372                                  (string-match prefix 
1373                                                (setq group (car groups))))
1374                        (setq gnus-killed-list 
1375                              (cons group gnus-killed-list))
1376                        (gnus-sethash group group gnus-killed-hashtb)
1377                        (setq groups (cdr groups)))
1378                      (setq starts (cdr starts)))
1379                     ((= ans ?s)
1380                      (while (and groups 
1381                                  (string-match prefix 
1382                                                (setq group (car groups))))
1383                        (gnus-sethash group group gnus-killed-hashtb)
1384                        (funcall gnus-subscribe-newsgroup-method 
1385                                 (car groups))
1386                        (setq groups (cdr groups)))
1387                      (setq starts (cdr starts)))
1388                     ((= ans ?q)
1389                      (while groups
1390                        (setq group (car groups))
1391                        (setq gnus-killed-list (cons group gnus-killed-list))
1392                        (gnus-sethash group group gnus-killed-hashtb)
1393                        (setq groups (cdr groups))))
1394                     (t nil)))
1395           (message "Subscribe '%s'? ([n]yq)" (car groups))
1396           (setq ans (read-char))
1397           (cond ((= ans ?y)
1398                  (funcall gnus-subscribe-newsgroup-method (car groups))
1399                  (gnus-sethash group group gnus-killed-hashtb))
1400                 ((= ans ?q)
1401                  (while groups
1402                    (setq group (car groups))
1403                    (setq gnus-killed-list (cons group gnus-killed-list))
1404                    (gnus-sethash group group gnus-killed-hashtb)
1405                    (setq groups (cdr groups))))
1406                 (t 
1407                  (setq gnus-killed-list (cons group gnus-killed-list))
1408                  (gnus-sethash group group gnus-killed-hashtb)))
1409           (setq groups (cdr groups)))))))
1410
1411 (defun gnus-subscribe-randomly (newsgroup)
1412   "Subscribe new NEWSGROUP by making it the first newsgroup."
1413   (gnus-subscribe-newsgroup newsgroup))
1414
1415 (defun gnus-subscribe-alphabetically (newgroup)
1416   "Subscribe new NEWSGROUP and insert it in alphabetical order."
1417   ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
1418   (let ((groups (cdr gnus-newsrc-assoc))
1419         before)
1420     (while (and (not before) groups)
1421       (if (string< newgroup (car (car groups)))
1422           (setq before (car (car groups)))
1423         (setq groups (cdr groups))))
1424     (gnus-subscribe-newsgroup newgroup before)))
1425
1426 (defun gnus-subscribe-hierarchically (newgroup)
1427   "Subscribe new NEWSGROUP and insert it in hierarchical newsgroup order."
1428   ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
1429   (save-excursion
1430     (set-buffer (find-file-noselect gnus-current-startup-file))
1431     (let ((groupkey newgroup)
1432           before)
1433       (while (and (not before) groupkey)
1434         (goto-char (point-min))
1435         (let ((groupkey-re
1436                (concat "^\\(" (regexp-quote groupkey) ".*\\)[!:]")))
1437           (while (and (re-search-forward groupkey-re nil t)
1438                       (progn
1439                         (setq before (buffer-substring
1440                                       (match-beginning 1) (match-end 1)))
1441                         (string< before newgroup)))))
1442         ;; Remove tail of newsgroup name (eg. a.b.c -> a.b)
1443         (setq groupkey
1444               (if (string-match "^\\(.*\\)\\.[^.]+$" groupkey)
1445                   (substring groupkey (match-beginning 1) (match-end 1)))))
1446       (gnus-subscribe-newsgroup newgroup before))))
1447
1448 (defun gnus-subscribe-interactively (newsgroup)
1449   "Subscribe new NEWSGROUP interactively.
1450 It is inserted in hierarchical newsgroup order if subscribed. If not,
1451 it is killed."
1452   (if (y-or-n-p (format "Subscribe new newsgroup: %s " newsgroup))
1453       (gnus-subscribe-hierarchically newsgroup)
1454     (setq gnus-killed-list (cons newsgroup gnus-killed-list))))
1455
1456 (defun gnus-subscribe-zombies (newsgroup)
1457   "Make new NEWSGROUP a zombie group."
1458   (setq gnus-zombie-list (cons newsgroup gnus-zombie-list)))
1459
1460 (defun gnus-subscribe-newsgroup (newsgroup &optional next)
1461   "Subscribe new NEWSGROUP.
1462 If NEXT is non-nil, it is inserted before NEXT. Otherwise it is made
1463 the first newsgroup."
1464   ;; We subscribe the group by changing its level to 3.
1465   (gnus-group-change-level 
1466    newsgroup 3 9 
1467    (if next (gnus-gethash next gnus-newsrc-hashtb)
1468      (gnus-gethash (car (nth 1 gnus-newsrc-assoc)) gnus-newsrc-hashtb)))
1469   (message "Subscribe newsgroup: %s" newsgroup))
1470
1471 ;; For directories
1472
1473 (defun gnus-newsgroup-directory-form (newsgroup)
1474   "Make hierarchical directory name from NEWSGROUP name."
1475   (let ((newsgroup (substring newsgroup 0)) ;Copy string.
1476         (len (length newsgroup))
1477         (idx 0))
1478     ;; Replace all occurrences of `.' with `/'.
1479     (while (< idx len)
1480       (if (= (aref newsgroup idx) ?.)
1481           (aset newsgroup idx ?/))
1482       (setq idx (1+ idx)))
1483     newsgroup
1484     ))
1485
1486 (defun gnus-make-directory (directory)
1487   "Make DIRECTORY recursively."
1488   (let ((directory (expand-file-name directory default-directory)))
1489     (or (file-exists-p directory)
1490         (gnus-make-directory-1 "" directory))
1491     ))
1492
1493 (defun gnus-make-directory-1 (head tail)
1494   (cond ((string-match "^/\\([^/]+\\)" tail)
1495          ;; ange-ftp interferes with calling match-* after
1496          ;; calling file-name-as-directory.
1497          (let ((beg (match-beginning 1))
1498                (end (match-end 1)))
1499            (setq head (concat (file-name-as-directory head)
1500                               (substring tail beg end)))
1501            (or (file-exists-p head)
1502                (call-process "mkdir" nil nil nil head))
1503            (gnus-make-directory-1 head (substring tail end))))
1504         ((string-equal tail "") t)
1505         ))
1506
1507 (defun gnus-capitalize-newsgroup (newsgroup)
1508   "Capitalize NEWSGROUP name with treating '.' and '-' as part of words."
1509   ;; Suggested by "Jonathan I. Kamens" <jik@pit-manager.MIT.EDU>.
1510   (let ((current-syntax-table (syntax-table)))
1511     (unwind-protect
1512         (progn
1513           (set-syntax-table (copy-syntax-table current-syntax-table))
1514           (modify-syntax-entry ?- "w")
1515           (modify-syntax-entry ?. "w")
1516           (capitalize newsgroup))
1517       (set-syntax-table current-syntax-table))))
1518
1519 ;; Var
1520
1521 (defun gnus-simplify-subject (subject &optional re-only)
1522   "Remove `Re:' and words in parentheses.
1523 If optional argument RE-ONLY is non-nil, strip `Re:' only."
1524   (let ((case-fold-search t))           ;Ignore case.
1525     ;; Remove `Re:' and `Re^N:'.
1526     (if (string-match "^re:[ \t]*" subject)
1527         (setq subject (substring subject (match-end 0))))
1528     ;; Remove words in parentheses from end.
1529     (or re-only
1530         (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1531           (setq subject (substring subject 0 (match-beginning 0)))))
1532     ;; Return subject string.
1533     subject
1534     ))
1535
1536 (defun gnus-add-current-to-buffer-list ()
1537   (setq gnus-buffer-list (cons (current-buffer) gnus-buffer-list)))
1538
1539 ;; Functions accessing headers.
1540 ;; Functions are more convenient than macros in some case.
1541
1542 (defun gnus-header-number (header)
1543   "Return article number in HEADER."
1544   (header-number header))
1545
1546 (defun gnus-header-subject (header)
1547   "Return subject string in HEADER."
1548   (header-subject header))
1549
1550 (defun gnus-header-from (header)
1551   "Return author string in HEADER."
1552   (header-from header))
1553
1554 (defun gnus-header-xref (header)
1555   "Return xref string in HEADER."
1556   (header-xref header))
1557
1558 (defun gnus-header-lines (header)
1559   "Return lines in HEADER."
1560   (header-lines header))
1561
1562 (defun gnus-header-date (header)
1563   "Return date in HEADER."
1564   (header-date header))
1565
1566 (defun gnus-header-id (header)
1567   "Return Id in HEADER."
1568   (header-id header))
1569
1570 (defun gnus-header-references (header)
1571   "Return references in HEADER."
1572   (header-references header))
1573
1574 (defun gnus-clear-system ()
1575   "Clear all variables and buffers."
1576   ;; Clear Gnus variables.
1577   (let ((variables gnus-variable-list))
1578     (while variables
1579       (set (car variables) nil)
1580       (setq variables (cdr variables))))
1581   ;; Clear other internal variables.
1582   (setq gnus-list-of-killed-groups nil
1583         gnus-have-read-active-file nil
1584         gnus-newsrc-assoc nil
1585         gnus-newsrc-hashtb nil
1586         gnus-killed-list nil
1587         gnus-zombie-list nil
1588         gnus-killed-hashtb nil
1589         gnus-active-hashtb nil
1590         gnus-moderated-list nil
1591         gnus-use-moderated nil
1592         gnus-description-hashtb nil
1593         gnus-newsgroup-headers nil
1594         gnus-newsgroup-headers-hashtb-by-number nil
1595         gnus-current-select-method nil)
1596   ;; Kill the startup file.
1597   (and gnus-current-startup-file
1598        (get-file-buffer gnus-current-startup-file)
1599        (kill-buffer (get-file-buffer gnus-current-startup-file)))
1600   (setq gnus-current-startup-file nil)
1601   (gnus-dribble-clear)
1602   ;; Kill Gnus buffers.
1603   (while gnus-buffer-list
1604     (if (and (get-buffer (car gnus-buffer-list))
1605              (buffer-name (get-buffer (car gnus-buffer-list))))
1606         (kill-buffer (car gnus-buffer-list)))
1607     (setq gnus-buffer-list (cdr gnus-buffer-list))))
1608
1609 (defun gnus-configure-windows (action &optional force)
1610   "Configure Gnus windows according to the next ACTION.
1611 The ACTION is either a symbol, such as `summary', or a
1612 configuration list such as `(1 1 2)'.  If ACTION is not a list,
1613 configuration list is got from the variable gnus-window-configuration.
1614 If FORCE is non-nil, the updating will be done whether it is necessary
1615 or not."
1616   (let* ((windows
1617           (if (listp action) action 
1618             (if (listp gnus-window-configuration)
1619                 (car (cdr (assq action gnus-window-configuration)))
1620               gnus-window-configuration)))
1621          (grpwin (get-buffer-window gnus-group-buffer))
1622          (subwin (get-buffer-window gnus-summary-buffer))
1623          (artwin (get-buffer-window gnus-article-buffer))
1624          (winsum nil)
1625          (height nil)
1626          (grpheight 0)
1627          (subheight 0)
1628          (artheight 0))
1629     (if (and (symbolp windows) (fboundp windows))
1630         (funcall windows action)
1631       (if (and (not force)
1632                (or (null windows)               ;No configuration is specified.
1633                    (and (eq (null grpwin)
1634                             (zerop (nth 0 windows)))
1635                         (eq (null subwin)
1636                             (zerop (nth 1 windows)))
1637                         (eq (null artwin)
1638                             (zerop (nth 2 windows))))))
1639           ;; No need to change window configuration.
1640           nil
1641         (select-window (or grpwin subwin artwin (selected-window)))
1642         ;; First of all, compute the height of each window.
1643         (cond (gnus-use-full-window
1644                ;; Take up the entire screen.
1645                (delete-other-windows)
1646                (setq height (window-height (selected-window))))
1647               (t
1648                (setq height (+ (if grpwin (window-height grpwin) 0)
1649                                (if subwin (window-height subwin) 0)
1650                                (if artwin (window-height artwin) 0)))))
1651         ;; The Newsgroup buffer exits always. So, use it to extend the
1652         ;; Group window so as to get enough window space.
1653         (switch-to-buffer gnus-group-buffer 'norecord)
1654         (and (get-buffer gnus-summary-buffer)
1655              (delete-windows-on gnus-summary-buffer))
1656         (and (get-buffer gnus-article-buffer)
1657              (delete-windows-on gnus-article-buffer))
1658         ;; Compute expected window height.
1659         (setq winsum (apply (function +) windows))
1660         (if (not (zerop (nth 0 windows)))
1661             (setq grpheight (max window-min-height
1662                                  (/ (* height (nth 0 windows)) winsum))))
1663         (if (not (zerop (nth 1 windows)))
1664             (setq subheight (max window-min-height
1665                                  (/ (* height (nth 1 windows)) winsum))))
1666         (if (not (zerop (nth 2 windows)))
1667             (setq artheight (max window-min-height
1668                                  (/ (* height (nth 2 windows)) winsum))))
1669         (setq height (+ grpheight subheight artheight))
1670         (enlarge-window (max 0 (- height (window-height (selected-window)))))
1671         ;; Then split the window.
1672         (and (not (zerop artheight))
1673              (or (not (zerop grpheight))
1674                  (not (zerop subheight)))
1675              (split-window-vertically (+ grpheight subheight)))
1676         (and (not (zerop grpheight))
1677              (not (zerop subheight))
1678              (split-window-vertically grpheight))
1679         ;; Then select buffers in each window.
1680         (and (not (zerop grpheight))
1681              (progn
1682                (switch-to-buffer gnus-group-buffer 'norecord)
1683                (other-window 1)))
1684         (and (not (zerop subheight))
1685              (progn
1686                (switch-to-buffer gnus-summary-buffer 'norecord)
1687                (other-window 1)))
1688         (and (not (zerop artheight))
1689              (progn
1690                ;; If Article buffer does not exist, it will be created
1691                ;; and initialized.
1692                (gnus-article-setup-buffer)
1693                (switch-to-buffer gnus-article-buffer 'norecord)))))
1694     ))
1695
1696 (defun gnus-window-configuration-split (action)
1697   (switch-to-buffer gnus-group-buffer t)
1698   (delete-other-windows)
1699   (split-window-horizontally)
1700   (cond ((or (eq action 'newsgroup) (eq action 'summary))
1701          (if (and (get-buffer gnus-summary-buffer)
1702                   (buffer-name gnus-summary-buffer))
1703              (switch-to-buffer-other-window gnus-summary-buffer)))
1704         ((eq action 'article)
1705          (switch-to-buffer gnus-summary-buffer t)
1706          (other-window 1)
1707          (gnus-article-setup-buffer)
1708          (switch-to-buffer gnus-article-buffer t))))
1709
1710 (defun gnus-version ()
1711   "Version numbers of this version of Gnus."
1712   (interactive)
1713   (let ((methods gnus-valid-select-methods)
1714         (mess gnus-version)
1715         meth)
1716     ;; Go through all the legal select methods and add their version
1717     ;; numbers to the total version string. Only the backends that are
1718     ;; currently in use will have their message numbers taken into
1719     ;; consideration. 
1720     (while methods
1721       (setq meth (intern (concat (car (car methods)) "-version")))
1722       (and (boundp meth)
1723            (stringp (symbol-value meth))
1724            (setq mess (concat mess "; " (symbol-value meth))))
1725       (setq methods (cdr methods)))
1726     (message mess)))
1727
1728 (defun gnus-info-find-node ()
1729   "Find Info documentation of Gnus."
1730   (interactive)
1731   ;; Enlarge info window if needed.
1732   (cond ((eq major-mode 'gnus-group-mode)
1733          (gnus-configure-windows '(1 0 0)) ;Take all windows.
1734          (pop-to-buffer gnus-group-buffer))
1735         ((eq major-mode 'gnus-summary-mode)
1736          (gnus-configure-windows '(0 1 0)) ;Take all windows.
1737          (pop-to-buffer gnus-summary-buffer)))
1738   (Info-goto-node (car (cdr (assq major-mode gnus-info-nodes)))))
1739
1740 (defun gnus-overload-functions (&optional overloads)
1741   "Overload functions specified by optional argument OVERLOADS.
1742 If nothing is specified, use the variable gnus-overload-functions."
1743   (let ((defs nil)
1744         (overloads (or overloads gnus-overload-functions)))
1745     (while overloads
1746       (setq defs (car overloads))
1747       (setq overloads (cdr overloads))
1748       ;; Load file before overloading function if necessary.  Make
1749       ;; sure we cannot use `require' always.
1750       (and (not (fboundp (car defs)))
1751            (car (cdr (cdr defs)))
1752            (load (car (cdr (cdr defs))) nil 'nomessage))
1753       (fset (car defs) (car (cdr defs)))
1754       )))
1755
1756 ;; List and range functions
1757
1758 (defun gnus-last-element (list)
1759   "Return last element of LIST."
1760   (while (cdr list)
1761     (setq list (cdr list)))
1762   (car list))
1763
1764 (defun gnus-set-difference (list1 list2)
1765   "Return a list of elements of LIST1 that do not appear in LIST2."
1766   (let ((list1 (copy-sequence list1)))
1767     (while list2
1768       (setq list1 (delq (car list2) list1))
1769       (setq list2 (cdr list2)))
1770     list1
1771     ))
1772
1773 (defun gnus-intersection (list1 list2)      
1774   (let ((result nil))
1775     (while list2
1776       (if (memq (car list2) list1)
1777           (setq result (cons (car list2) result)))
1778       (setq list2 (cdr list2)))
1779     result
1780     ))
1781
1782 (defun gnus-compress-sequence (numbers &optional always-list)
1783   "Convert list of numbers to a list of ranges or a single range.
1784 If ALWAYS-LIST is non-nil, this function will always release a list of
1785 ranges."
1786   (let* ((numbers (sort numbers (function <)))
1787          (first (car numbers))
1788          (last (car numbers))
1789          result)
1790     (while numbers
1791       (cond ((= last (car numbers)) nil) ;Omit duplicated number
1792             ((= (1+ last) (car numbers)) ;Still in sequence
1793              (setq last (car numbers)))
1794             (t                          ;End of one sequence
1795              (setq result (cons (cons first last) result))
1796              (setq first (car numbers))
1797              (setq last  (car numbers))))
1798       (setq numbers (cdr numbers)))
1799     (if (and (not always-list) (null result))
1800         (cons first last)
1801       (nreverse (cons (cons first last) result)))))
1802
1803 (defun gnus-uncompress-sequence (ranges)
1804   "Expand a list of ranges into a list of numbers.
1805 RANGES is either a single range on the form `(num . num)' or a list of
1806 these ranges."
1807   (let (first last result)
1808     (if (atom (car ranges))
1809         (progn
1810           (setq first (car ranges))
1811           (setq last (cdr ranges))
1812           (while (<= first last)
1813             (setq result (cons first result))
1814             (setq first (1+ first))))
1815       (while ranges
1816         (setq first (car (car ranges)))
1817         (setq last  (cdr (car ranges)))
1818         (while (<= first last)
1819           (setq result (cons first result))
1820           (setq first (1+ first)))
1821         (setq ranges (cdr ranges))))
1822     (nreverse result)))
1823
1824 (defun gnus-add-to-range (ranges list)
1825   "Return a list of ranges that has all articles from both RANGES and LIST.
1826 Note: LIST has to be sorted over `<'."
1827   (let* ((ranges (if (and ranges (atom (car ranges))) (list ranges) ranges))
1828          (inrange ranges)
1829          range nranges first last)
1830     (if (not ranges)
1831         (gnus-compress-sequence list t)
1832       (while (and ranges list)
1833         (setq range (car ranges))
1834         (while (and list (<= (car list) (cdr range)))
1835           (setq list (cdr list)))
1836         (while (and list (= (1- (car list)) (cdr range)))
1837           (setcdr range (car list))
1838           (setq list (cdr list)))
1839         (if (and list (and (> (car list) (cdr range)) (cdr ranges)
1840                            (< (car list) (car (car (cdr ranges))))))
1841             (setcdr ranges (cons (cons (car list) (car list)) (cdr ranges))))
1842         (setq ranges (cdr ranges)))
1843       (if (and list (not ranges))
1844           (setq inrange (nconc inrange (gnus-compress-sequence list t))))
1845       (setq ranges inrange)
1846       (while ranges
1847         (if (and (cdr ranges) (>= (1+ (cdr (car ranges)))
1848                                   (car (car (cdr ranges)))))
1849             (progn
1850               (setcdr (car ranges) (cdr (car (cdr ranges))))
1851               (setcdr ranges (cdr (cdr ranges))))
1852           (setq ranges (cdr ranges))))
1853       (if (not (cdr inrange))
1854           (car inrange)
1855         inrange))))
1856
1857 (defun gnus-member-of-range (number ranges)
1858   (let ((not-stop t))
1859     (while (and ranges not-stop)
1860       (if (and (>= number (car (car ranges)))
1861                (<= number (cdr (car ranges))))
1862           (setq not-stop nil))
1863       (setq ranges (cdr ranges)))
1864     (not not-stop)))
1865
1866 \f
1867 ;;;
1868 ;;; Gnus Group Mode
1869 ;;;
1870
1871 (if gnus-group-mode-map
1872     nil
1873   (setq gnus-group-mode-map (make-keymap))
1874   (suppress-keymap gnus-group-mode-map)
1875   (define-key gnus-group-mode-map " " 'gnus-group-read-group)
1876   (define-key gnus-group-mode-map "=" 'gnus-group-select-group)
1877   (define-key gnus-group-mode-map "j" 'gnus-group-jump-to-group)
1878   (define-key gnus-group-mode-map "n" 'gnus-group-next-unread-group)
1879   (define-key gnus-group-mode-map "p" 'gnus-group-prev-unread-group)
1880   (define-key gnus-group-mode-map [del] 'gnus-group-prev-unread-group)
1881   (define-key gnus-group-mode-map "N" 'gnus-group-next-group)
1882   (define-key gnus-group-mode-map "P" 'gnus-group-prev-group)
1883   (define-key gnus-group-mode-map "\M-n" 'gnus-group-next-unread-group-same-level)
1884   (define-key gnus-group-mode-map "\M-p" 'gnus-group-prev-unread-group-same-level)
1885   (define-key gnus-group-mode-map "\r" 'gnus-group-select-group)
1886   (define-key gnus-group-mode-map "u" 'gnus-group-unsubscribe-current-group)
1887   (define-key gnus-group-mode-map "U" 'gnus-group-unsubscribe-group)
1888   (define-key gnus-group-mode-map "c" 'gnus-group-catchup-current)
1889   (define-key gnus-group-mode-map "C" 'gnus-group-catchup-current-all)
1890   (define-key gnus-group-mode-map "l" 'gnus-group-list-groups)
1891   (define-key gnus-group-mode-map "L" 'gnus-group-list-all-groups)
1892   (define-key gnus-group-mode-map "m" 'gnus-group-mail)
1893   (define-key gnus-group-mode-map "g" 'gnus-group-get-new-news)
1894   (define-key gnus-group-mode-map "\M-g" 'gnus-group-get-new-news-this-group)
1895   (define-key gnus-group-mode-map "R" 'gnus-group-restart)
1896   (define-key gnus-group-mode-map "r" 'gnus-group-read-init-file)
1897   (define-key gnus-group-mode-map "B" 'gnus-group-browse-foreign-server)
1898   (define-key gnus-group-mode-map "b" 'gnus-group-check-bogus-groups)
1899   (define-key gnus-group-mode-map "F" 'gnus-find-new-newsgroups)
1900   (define-key gnus-group-mode-map "D" 'gnus-group-describe-group)
1901   (define-key gnus-group-mode-map "\M-d" 'gnus-group-describe-all-groups)
1902   (define-key gnus-group-mode-map "a" 'gnus-group-post-news)
1903   (define-key gnus-group-mode-map "\M-a" 'gnus-group-add-newsgroup)
1904   (define-key gnus-group-mode-map "\M-e" 'gnus-group-edit-newsgroup)
1905   (define-key gnus-group-mode-map "\ek" 'gnus-group-edit-local-kill)
1906   (define-key gnus-group-mode-map "\eK" 'gnus-group-edit-global-kill)
1907   (define-key gnus-group-mode-map "k" 'gnus-group-kill-group)
1908   (define-key gnus-group-mode-map "\C-k" 'gnus-group-kill-group)
1909   (define-key gnus-group-mode-map "y" 'gnus-group-yank-group)
1910   (define-key gnus-group-mode-map "\C-y" 'gnus-group-yank-group)
1911   (define-key gnus-group-mode-map "\C-w" 'gnus-group-kill-region)
1912   (define-key gnus-group-mode-map "\M-z" 'gnus-group-kill-all-zombies)
1913   (define-key gnus-group-mode-map "\C-x\C-t" 'gnus-group-transpose-groups)
1914   (define-key gnus-group-mode-map "\C-c\C-l" 'gnus-group-list-killed)
1915   (define-key gnus-group-mode-map "\C-c\C-k" 'gnus-group-list-killed)
1916   (define-key gnus-group-mode-map "\C-c\C-z" 'gnus-group-list-zombies)
1917   (define-key gnus-group-mode-map "\C-c\C-x" 'gnus-group-expire-articles)
1918   (define-key gnus-group-mode-map "\C-c\M-\C-x" 'gnus-group-expire-all-groups)
1919   (define-key gnus-group-mode-map "V" 'gnus-version)
1920   (define-key gnus-group-mode-map "S" 'gnus-group-set-current-level)
1921   (define-key gnus-group-mode-map "s" 'gnus-group-save-newsrc)
1922   (define-key gnus-group-mode-map "z" 'gnus-group-suspend)
1923   (define-key gnus-group-mode-map "Z" 'gnus-group-clear-dribble)
1924   (define-key gnus-group-mode-map "q" 'gnus-group-exit)
1925   (define-key gnus-group-mode-map "Q" 'gnus-group-quit)
1926   (define-key gnus-group-mode-map "?" 'gnus-group-describe-briefly)
1927   (define-key gnus-group-mode-map "\C-c\C-i" 'gnus-info-find-node)
1928   (define-key gnus-group-mode-map [mouse-2] 'gnus-mouse-pick-group)
1929
1930   ;; Make a menu bar item.
1931   (define-key gnus-group-mode-map [menu-bar Gnus]
1932         (cons "Gnus" (make-sparse-keymap "Gnus")))
1933
1934   (define-key gnus-group-mode-map [menu-bar Gnus force-update]
1935         '("Force Update" . gnus-group-force-update))
1936   (define-key gnus-group-mode-map [menu-bar Gnus quit]
1937         '("Quit" . gnus-group-quit))
1938   (define-key gnus-group-mode-map [menu-bar Gnus exit]
1939         '("Exit" . gnus-group-exit))
1940   (define-key gnus-group-mode-map [menu-bar Gnus restart]
1941         '("Restart" . gnus-group-restart))
1942   (define-key gnus-group-mode-map [menu-bar Gnus suspend]
1943         '("Suspend" . gnus-group-suspend))
1944   (define-key gnus-group-mode-map [menu-bar Gnus get-new-news]
1945         '("Get New News" . gnus-group-get-new-news))
1946
1947   ;; Make a menu bar item.
1948   (define-key gnus-group-mode-map [menu-bar groups]
1949         (cons "Groups" (make-sparse-keymap "Groups")))
1950
1951   (define-key gnus-group-mode-map [menu-bar groups catchup]
1952         '("Catchup" . gnus-group-catchup))
1953   (define-key gnus-group-mode-map [menu-bar groups edit-global-kill]
1954         '("Edit Kill File" . gnus-group-edit-global-kill))
1955
1956   (define-key gnus-group-mode-map [menu-bar groups separator-2]
1957         '("--"))
1958
1959   (define-key gnus-group-mode-map [menu-bar groups yank-group]
1960         '("Yank Group" . gnus-group-yank-group))
1961   (define-key gnus-group-mode-map [menu-bar groups kill-group]
1962         '("Kill Group" . gnus-group-kill-group))
1963
1964   (define-key gnus-group-mode-map [menu-bar groups separator-1]
1965         '("--"))
1966
1967   (define-key gnus-group-mode-map [menu-bar groups jump-to-group]
1968         '("Jump to Group..." . gnus-group-jump-to-group))
1969   (define-key gnus-group-mode-map [menu-bar groups list-all-groups]
1970         '("List All Groups" . gnus-group-list-all-groups))
1971   (define-key gnus-group-mode-map [menu-bar groups list-groups]
1972         '("List Groups" . gnus-group-list-groups))
1973   (define-key gnus-group-mode-map [menu-bar groups unsub-current-group]
1974         '("Unsubscribe Group" . gnus-group-unsubscribe-current-group))
1975   )
1976
1977 (defun gnus-group-mode ()
1978   "Major mode for reading news.
1979 All normal editing commands are switched off.
1980 The following commands are available:
1981
1982 \\<gnus-group-mode-map>
1983 \\[gnus-group-read-group]\t Choose the current group
1984 \\[gnus-group-select-group]\t Select the current group without selecting the first article
1985 \\[gnus-group-jump-to-group]\t Go to some group
1986 \\[gnus-group-next-unread-group]\t Go to the next unread group
1987 \\[gnus-group-prev-unread-group]\t Go to the previous unread group
1988 \\[gnus-group-next-group]\t Go to the next group
1989 \\[gnus-group-prev-group]\t Go to the previous group
1990 \\[gnus-group-next-unread-group-same-level]\t Go to the next unread group on the same level
1991 \\[gnus-group-prev-unread-group-same-level]\t Go to the previous unread group un the same level
1992 \\[gnus-group-unsubscribe-current-group]\t (Un)subscribe to the current group
1993 \\[gnus-group-unsubscribe-group]\t (Un)subscribe to some group
1994 \\[gnus-group-catchup-current]\t Mark all unread articles in the current group as read
1995 \\[gnus-group-catchup-current-all]\t Mark all alrticles in the current group as read
1996 \\[gnus-group-list-groups]\t List groups that have unread articles
1997 \\[gnus-group-list-all-groups]\t List all groups
1998 \\[gnus-group-mail]\t Compose a mail
1999 \\[gnus-group-get-new-news]\t Look for new news
2000 \\[gnus-group-get-new-news-this-group]\t Look for new news for the current group
2001 \\[gnus-group-restart]\t Restart Gnus
2002 \\[gnus-group-save-newsrc]\t Save the startup file(s)
2003 \\[gnus-group-browse-foreign-server]\t Browse a foreign (NNTP) server
2004 \\[gnus-group-check-bogus-groups]\t Check for and delete bogus newsgroups
2005 \\[gnus-find-new-newsgroups]\t Find new newsgroups
2006 \\[gnus-group-describe-group]\t Describe the current newsgroup
2007 \\[gnus-group-describe-all-groups]\t Describe all newsgroups
2008 \\[gnus-group-post-news]\t Post an article to some newsgroup
2009 \\[gnus-group-add-newsgroup]\t Add a newsgroup entry
2010 \\[gnus-group-edit-newsgroup]\t Edit a newsgroup entry
2011 \\[gnus-group-edit-local-kill]\t Edit a local kill file
2012 \\[gnus-group-edit-global-kill]\t Edit the global kill file
2013 \\[gnus-group-kill-group]\t Kill the current newsgroup
2014 \\[gnus-group-yank-group]\t Yank a previously killed newsgroup
2015 \\[gnus-group-kill-region]\t Kill all newsgroups between point and mark
2016 \\[gnus-group-kill-all-zombies]\t Kill all zombie newsgroups
2017 \\[gnus-group-transpose-groups]\t Transpose two newsgroups
2018 \\[gnus-group-list-killed]\t List all killed newsgroups
2019 \\[gnus-group-list-zombies]\t List all zombie newsgroups
2020 \\[gnus-group-expire-articles]\t Expire the expirable articles in the current newsgroup
2021 \\[gnus-group-expire-all-groups]\t Expire expirable articles in all newsgroups
2022 \\[gnus-version]\t Display the current Gnus version
2023 \\[gnus-group-set-current-level]\t Set the level of the current newsgroup
2024 \\[gnus-group-suspend]\t Suspend Gnus
2025 \\[gnus-group-clear-dribble]\t Clear the dribble buffer
2026 \\[gnus-group-exit]\t Stop reading news
2027 \\[gnus-group-quit]\t Stop reading news without saving the startup files
2028 \\[gnus-group-describe-briefly]\t Give a brief description of the current mode
2029 \\[gnus-info-find-node]\t Find the info pages for Gnus
2030 "
2031   (interactive)
2032   (kill-all-local-variables)
2033   (setq mode-line-modified "--- ")
2034   (setq major-mode 'gnus-group-mode)
2035   (setq mode-name "Newsgroup")
2036   (gnus-group-set-mode-line)
2037   (setq mode-line-process nil)
2038   (use-local-map gnus-group-mode-map)
2039   (buffer-disable-undo (current-buffer))
2040   (setq truncate-lines t)
2041   (setq buffer-read-only t)
2042   (run-hooks 'gnus-group-mode-hook))
2043
2044 (defun gnus-mouse-pick-group (e)
2045   (interactive "e")
2046   (mouse-set-point e)
2047   (gnus-group-read-group nil))
2048
2049 (defalias '\(ding\) 'gnus)
2050
2051 ;;;###autoload
2052 (defun gnus (&optional arg)
2053   "Read network news.
2054 If ARG is non-nil and a positive number, Gnus will use that as the
2055 startup level. If ARG is non-nil and not a positive number, Gnus will
2056 prompt the user for the name of an NNTP server to use."
2057   (interactive "P")
2058   (gnus-clear-system)
2059   (gnus-read-init-file)
2060   (if (and gnus-signature-file mail-signature)
2061       (setq gnus-signature-file nil))
2062   (let ((level (and arg (numberp arg) (> arg 0) arg)))
2063     (unwind-protect
2064         (progn
2065           (switch-to-buffer (get-buffer-create gnus-group-buffer))
2066           (gnus-add-current-to-buffer-list)
2067           (gnus-group-mode)
2068           (gnus-start-news-server (and arg (not level))))
2069       (if (not (gnus-server-opened gnus-select-method))
2070           (gnus-group-quit)
2071         ;; NNTP server is successfully open. 
2072         (gnus-update-format-specifications)
2073         (let ((buffer-read-only nil))
2074           (erase-buffer)
2075           (if (not gnus-inhibit-startup-message)
2076               (progn
2077                 (gnus-group-startup-message)
2078                 (sit-for 0))))
2079         (run-hooks 'gnus-startup-hook)
2080         (gnus-setup-news nil (or level 7))
2081         (gnus-dribble-open)
2082         (or (not gnus-novice-user)
2083             gnus-expert-user
2084             (gnus-group-describe-briefly)) ;Show brief help message.
2085         (gnus-group-list-groups (or level 5))))))
2086
2087 (defun gnus-group-startup-message (&optional x y)
2088   "Insert startup message in current buffer."
2089   ;; Insert the message.
2090   (erase-buffer)
2091   (insert
2092    (format "
2093 %s
2094        A newsreader 
2095   for GNU Emacs
2096
2097     Based on GNUS 
2098          written by 
2099  Masanobu UMEDA
2100
2101 Lars Ingebrigtsen 
2102   larsi@ifi.uio.no
2103
2104            gnus-version))
2105   ;; And then hack it.
2106   ;; 18 is the longest line.
2107   (indent-rigidly (point-min) (point-max) 
2108                   (/ (max (- (window-width) (or x 28)) 0) 2))
2109   (goto-char (point-min))
2110   ;; +4 is fuzzy factor.
2111   (insert-char ?\n (/ (max (- (window-height) (or y 12)) 0) 2)))
2112
2113 (defun gnus-group-list-groups (level &optional unread)
2114   "List newsgroups with level LEVEL or lower that have unread alticles.
2115 Default is 5, which lists all subscribed groups.
2116 If argument UNREAD is non-nil, groups with no unread articles are also listed."
2117   (interactive "P")
2118   (setq level (or level 5))
2119   (let ((case-fold-search nil)
2120         (group (gnus-group-group-name)))
2121     (set-buffer gnus-group-buffer)      ;May call from out of Group buffer
2122     (gnus-group-prepare level unread)
2123     (if (zerop (buffer-size))
2124         ;; Suggested by Andrew Eskilsson <pi92ae@lelle.pt.hk-r.se>.
2125         (message "No news is horrible news")
2126       (goto-char (point-min))
2127       (if (not group)
2128           ()
2129         ;; Find the right group to put point on. If the current group
2130         ;; has disapeared in the new listing, try to find the next
2131         ;; one. If no next one can be found, just leave point at the
2132         ;; first newsgroup in the buffer.
2133         (if (not (re-search-forward (gnus-group-make-regexp group) nil t))
2134             (let ((newsrc (nthcdr 3 (gnus-gethash group gnus-newsrc-hashtb))))
2135               (while (and newsrc
2136                           (not (re-search-forward 
2137                                 (gnus-group-make-regexp (car (car newsrc))) 
2138                                 nil t)))
2139                 (setq newsrc (cdr newsrc))))))
2140       ;; Adjust cursor point.
2141       (gnus-group-position-cursor))))
2142
2143 (defun gnus-group-prepare (level &optional all lowest) 
2144   "List all newsgroups with unread articles of level LEVEL or lower.
2145 If ALL is non-nil, list groups that have no unread articles.
2146 If LOWEST is non-nil, list all newsgroups of level LOWEST or higher."
2147   (set-buffer (get-buffer-create gnus-group-buffer))
2148   (gnus-add-current-to-buffer-list)
2149   (let ((buffer-read-only nil)
2150         (newsrc (cdr gnus-newsrc-assoc))
2151         (zombie gnus-zombie-list)
2152         (killed gnus-killed-list)
2153         info clevel unread active group)
2154     (if (not lowest)
2155         (setq lowest 1))
2156     (erase-buffer)
2157     (if (< lowest 8)
2158         ;; List alive newsgroups.
2159         (while newsrc
2160           (setq info (car newsrc)
2161                 group (car info)
2162                 newsrc (cdr newsrc)
2163                 unread (car (gnus-gethash group gnus-newsrc-hashtb)))
2164           (if (and unread ; This group might be bogus
2165                    (or all (eq unread t) (> unread 0))
2166                    (and (<= (setq clevel (car (cdr info))) level))
2167                    (>= clevel lowest))
2168               (gnus-group-insert-group-line 
2169                nil group (car (cdr info)) (nth 3 info) unread
2170                (nth 4 info)))))
2171
2172     ;; List zombies and killed lists somehwat faster, which was
2173     ;; suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>. It does
2174     ;; this by ignoring the group format specification altogether.
2175     (let ((lists (list 'gnus-zombie-list 'gnus-killed-list))
2176           mark beg)
2177       (while lists
2178         (if (or (and (eq (car lists) 'gnus-zombie-list)
2179                      (progn (setq mark ?Z)
2180                             (and (>= level 8) (<= lowest 8))))
2181                 (and (eq (car lists) 'gnus-killed-list)
2182                      (progn (setq mark ?K)
2183                             (and (>= level 9) (<= lowest 9)))))
2184             (progn
2185               (setq newsrc (set (car lists)
2186                                 (sort (symbol-value (car lists)) 
2187                                       (function string<))))
2188               (while newsrc
2189                 (setq group (car newsrc)
2190                       newsrc (cdr newsrc))
2191                 (insert (format " %c    *: %s" mark group))
2192                 (setq beg (point))
2193                 (insert (format " %s  %d\n" group 
2194                                 (if (= mark ?Z) 8 9)))
2195                 (set-text-properties beg (1- (point))
2196                                      '(invisible t)))))
2197         (setq lists (cdr lists))))
2198
2199     (gnus-group-set-mode-line)
2200     (setq gnus-have-all-newsgroups all)
2201     (run-hooks 'gnus-group-prepare-hook)))
2202
2203 (defun gnus-group-real-name (group)
2204   "Find the real name of a foreign newsgroup."
2205   (if (string-match (concat "^" gnus-foreign-group-prefix) group)
2206       (substring group (match-end 0))
2207     group))
2208
2209 (defun gnus-group-set-info (info)
2210   (let ((entry (gnus-gethash (car info) gnus-newsrc-hashtb)))
2211     (if entry
2212         (progn
2213           (setcar (nthcdr 2 entry) info)
2214           (if (and (not (eq (car entry) t)) 
2215                    (gnus-gethash (car info) gnus-active-hashtb))
2216               (setcar entry (length (gnus-list-of-unread-articles 
2217                                      (car info))))))
2218       (error "No such group: %s" (car info)))))
2219
2220 (defun gnus-group-update-group-line ()
2221   "This function updates the current line in the newsgroup buffer and
2222 moves the point to the colon."
2223   (let ((group (gnus-group-group-name))
2224         (buffer-read-only nil))
2225     (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
2226       (if entry
2227           (gnus-dribble-enter 
2228            (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2229                    ")"))))
2230     (beginning-of-line)
2231     (delete-region (point) (save-excursion (forward-line 1) (point)))
2232     (gnus-group-insert-group-line-info group)
2233     (forward-line -1)
2234     (gnus-group-position-cursor)))
2235
2236 (defun gnus-group-insert-group-line-info (group)
2237   (let ((entry (gnus-gethash group gnus-newsrc-hashtb)) 
2238         active info)
2239     (if entry
2240         (progn
2241           (setq info (nth 2 entry))
2242           (gnus-group-insert-group-line 
2243            nil group (nth 1 info) (nth 3 info) (car entry) (nth 4 info)))
2244       (setq active (gnus-gethash group gnus-active-hashtb))
2245       (gnus-group-insert-group-line 
2246        nil group (if (member group gnus-zombie-list) 8 9)
2247        nil (- (1+ (cdr active)) (car active)) nil))))
2248
2249 (defun gnus-group-insert-group-line (gformat group level marked number method)
2250   (let* ((gformat (or gformat gnus-group-line-format-spec))
2251          (marked (if (and (assq 'tick marked) (numberp number) 
2252                           (>= (1- (length (assq 'tick marked))) number))
2253                      ?* ? ))
2254          (subscribed (cond ((< level 6) ? )
2255                            ((< level 8) ?U)
2256                            ((= level 8) ?Z)
2257                            (t ?K)))
2258          (buffer-read-only nil)
2259          (newsgroup-description 
2260           (if gnus-description-hashtb
2261               (or (gnus-gethash group gnus-description-hashtb) "")
2262             ""))
2263          (moderated (if (member group gnus-moderated-list) ?m ? ))
2264          (moderated-string (if (eq moderated ?m) "(m)" ""))
2265          (news-server (or (car (cdr method)) ""))
2266          (news-method (or (car method) ""))
2267          (news-method-string 
2268           (if method (format "(%s:%s)" (car method) (car (cdr method))) ""))
2269          (number (if (eq number t) "*" number))
2270          flist b)
2271     (beginning-of-line)
2272     (let ((group (if method (gnus-group-real-name group) group)))
2273       ;; Insert the visible text.
2274       (insert (eval gformat)))
2275     (forward-char -1)
2276     ;; Insert the invisible info on the end of the line.
2277     (setq b (point))
2278     ;; The info is GROUP UNREAD MARKED LEVEL.
2279     (insert 
2280      (format " %s%c%c%d"
2281              group (if (or (stringp number) (> number 0)) ?+ ? )
2282              marked level))
2283     (set-text-properties b (point) '(invisible t))
2284     (forward-char 1)))
2285
2286 (defun gnus-group-update-group (group &optional visible-only)
2287   "Update newsgroup info of GROUP.
2288 If VISIBLE-ONLY is non-nil, the group won't be displayed if it isn't already."
2289   (let ((buffer-read-only nil)
2290         (case-fold-search nil)
2291         (regexp (gnus-group-make-regexp group))
2292         (visible nil))
2293     (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
2294       (if entry
2295           (gnus-dribble-enter 
2296            (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2297                    ")"))))
2298     ;; Buffer may be narrowed.
2299     (save-restriction
2300       (widen)
2301       ;; Search a line to modify.  If the buffer is large, the search
2302       ;; takes long time.  In most cases, current point is on the line
2303       ;; we are looking for.  So, first of all, check current line. 
2304       ;; And then if current point is in the first half, search from
2305       ;; the beginning.  Otherwise, search from the end.
2306       (if (cond ((progn
2307                    (beginning-of-line)
2308                    (looking-at regexp)))
2309                 ((and (> (/ (buffer-size) 2) (point)) ;In the first half.
2310                       (progn
2311                         (goto-char (point-min))
2312                         (re-search-forward regexp nil t))))
2313                 ((progn
2314                    (goto-char (point-max))
2315                    (re-search-backward regexp nil t))))
2316           ;; GROUP is listed in current buffer. So, delete old line.
2317           (progn
2318             (setq visible t)
2319             (beginning-of-line)
2320             (delete-region (point) (progn (forward-line 1) (point))))
2321         ;; No such line in the buffer, find out where it's supposed to
2322         ;; go, and insert it there (or at the end of the buffer).
2323         (let ((entry (cdr (gnus-gethash group gnus-newsrc-hashtb))))
2324           (goto-char (point-min))
2325           (while (and entry
2326                       (not (re-search-forward (gnus-group-make-regexp
2327                                                (car (car entry))) nil t)))
2328             (setq entry (cdr entry)))
2329           (if (not entry)
2330               (goto-char (point-max)))))
2331       (if (or visible (not visible-only))
2332           (progn
2333             (gnus-group-insert-group-line-info group)
2334             (forward-line -1)           ; Move point back to the inserted line.
2335             ))))
2336   (gnus-group-set-mode-line))
2337
2338 (defun gnus-group-set-mode-line ()
2339   (if (memq 'group gnus-updated-mode-lines)
2340       (let* ((gformat (or gnus-group-mode-line-format-spec
2341                           (setq gnus-group-mode-line-format-spec
2342                                 (gnus-parse-format 
2343                                  gnus-group-mode-line-format 
2344                                  gnus-group-mode-line-format-alist))))
2345              (news-server (car (cdr gnus-select-method)))
2346              (news-method (car gnus-select-method))
2347              (mode-string (eval gformat))
2348              (max-len 60))
2349         (if (> (length mode-string) max-len) 
2350             (setq mode-string (substring mode-string 0 (- max-len 4))))
2351         (setq mode-line-buffer-identification mode-string)
2352         (set-buffer-modified-p t))))
2353
2354 (defun gnus-group-group-name ()
2355   "Get the name of the newsgroup on the current line."
2356   (save-excursion
2357     (let ((buffer-read-only nil))
2358       (beginning-of-line)
2359       (if (re-search-forward " \\([^ ]*\\)...$" nil t)
2360           (prog2
2361               (set-text-properties (match-beginning 1) (match-end 1) nil)
2362               (buffer-substring (match-beginning 1) (match-end 1))
2363             (set-text-properties (match-beginning 1) (match-end 1) 
2364                                  '(invisible t)))))))
2365
2366 (defun gnus-group-group-level ()
2367   "Get the level of the newsgroup on the current line."
2368   (save-excursion
2369     (end-of-line)
2370     (forward-char -1)
2371     (let ((c (following-char)))
2372       (if (and (>= c ?1) (<= c ?9))
2373           (1+ (- c ?1))))))
2374
2375 (defun gnus-group-make-regexp (newsgroup)
2376   "Return regexp that will match the line that NEWSGROUP is on."
2377   (concat " " (regexp-quote newsgroup) "...$"))
2378
2379 (defun gnus-group-search-forward (&optional backward all level)
2380   "Find the next newsgroup with unread articles.
2381 If BACKWARD is non-nil, find the previous newsgroup instead.
2382 If ALL is non-nil, just find any newsgroup.
2383 If LEVEL is non-nil, find group with level LEVEL, or higher if no such
2384 group exists."
2385   (if (not level)
2386       (let ((regexp (if all "...$" "\\+.[1-5]$")))
2387         (prog1
2388             (if backward
2389                 (progn
2390                   (beginning-of-line)
2391                   (re-search-backward regexp nil t))
2392               (end-of-line)
2393               (re-search-forward regexp nil t))
2394           (gnus-group-position-cursor)))
2395     (let ((beg (point)))
2396       (while (and (< level 10)
2397                   (goto-char beg)
2398                   (let ((regexp (format "%s.%d$" (if all "." "\\+") level)))
2399                     (not            
2400                      (if backward
2401                          (progn
2402                            (beginning-of-line)
2403                            (re-search-backward regexp nil t))
2404                        (end-of-line)
2405                        (re-search-forward regexp nil t)))))
2406         (setq level (1+ level)))
2407       (< level 10))))
2408
2409 ;; Gnus Group mode command
2410
2411 (defun gnus-group-read-group (all &optional no-article)
2412   "Read news in this newsgroup.
2413 If argument ALL is non-nil, already read articles become readable.
2414 If optional argument NO-ARTICLE is non-nil, no article body is displayed."
2415   (interactive "P")
2416   (let ((group (gnus-group-group-name))
2417         number active)
2418     (if (not group)
2419         (error "No group on current line"))
2420     ;; This group might be a dead group. In that case we have to get
2421     ;; the number of unread articles from `gnus-active-hashtb'.
2422     (if (>= (gnus-group-group-level) 8)
2423         (setq number (- (1+ (cdr (setq active (gnus-gethash 
2424                                                group gnus-active-hashtb))))
2425                         (car active)))
2426       (setq number (car (gnus-gethash group gnus-newsrc-hashtb))))
2427     (gnus-summary-read-group 
2428      group (or all (and (numberp number) (zerop number))) no-article)))
2429
2430 (defun gnus-group-select-group (all)
2431   "Select this newsgroup.
2432 No article is selected automatically.
2433 If argument ALL is non-nil, already read articles become readable."
2434   (interactive "P")
2435   (gnus-group-read-group all t))
2436
2437 (defun gnus-group-jump-to-group (group)
2438   "Jump to newsgroup GROUP."
2439   (interactive
2440    (list 
2441     (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
2442   (let ((case-fold-search nil))
2443     (goto-char (point-min))
2444     ;; Either go to the line in the group buffer...
2445     (or (re-search-forward (gnus-group-make-regexp group) nil t)
2446         ;; ... or insert the line.
2447         (gnus-group-update-group group))
2448     ;; Adjust cursor point.
2449     (gnus-group-position-cursor)))
2450
2451 (defun gnus-group-next-group (n)
2452   "Go to next N'th newsgroup.
2453 If N is negative, search backward instead.
2454 Returns the difference between N and the number of skips actually
2455 done."
2456   (interactive "p")
2457   (gnus-group-next-unread-group n t))
2458
2459 (defun gnus-group-next-unread-group (n &optional all level)
2460   "Go to next N'th unread newsgroup.
2461 If N is negative, search backward instead.
2462 If ALL is non-nil, choose any newsgroup, unread or not.
2463 If LEVEL is non-nil, choose the next group with level LEVEL, or, if no
2464 such group can be found, the next group with a level higher than
2465 LEVEL.
2466 Returns the difference between N and the number of skips actually
2467 done."
2468   (interactive "p")
2469   (let ((backward (< n 0))
2470         (n (abs n)))
2471   (while (and (> n 0)
2472               (gnus-group-search-forward backward all level))
2473     (setq n (1- n)))
2474   (if (/= 0 n) (message "No more%s newsgroups%s" (if all "" " unread")
2475                         (if level " on this level or higher" "")))
2476   n))
2477
2478 (defun gnus-group-prev-group (n)
2479   "Go to previous N'th newsgroup.
2480 Returns the difference between N and the number of skips actually
2481 done."
2482   (interactive "p")
2483   (gnus-group-next-unread-group (- n) t))
2484
2485 (defun gnus-group-prev-unread-group (n)
2486   "Go to previous N'th unread newsgroup.
2487 Returns the difference between N and the number of skips actually
2488 done."  
2489   (interactive "p")
2490   (gnus-group-next-unread-group (- n)))
2491
2492 (defun gnus-group-next-unread-group-same-level (n)
2493   "Go to next N'th unread newsgroup on the same level.
2494 If N is negative, search backward instead.
2495 Returns the difference between N and the number of skips actually
2496 done."
2497   (interactive "p")
2498   (gnus-group-next-unread-group n t (gnus-group-group-level))
2499   (gnus-group-position-cursor))
2500
2501 (defun gnus-group-prev-unread-group-same-level (n)
2502   "Go to next N'th unread newsgroup on the same level.
2503 Returns the difference between N and the number of skips actually
2504 done."
2505   (interactive "p")
2506   (gnus-group-next-unread-group (- n) t (gnus-group-group-level))
2507   (gnus-group-position-cursor))
2508
2509 (defun gnus-group-add-newsgroup (&optional name how where)
2510   "Add a new newsgroup."
2511   (interactive)
2512   (let ((methods gnus-valid-select-methods)
2513         nname)
2514     (if (not name)
2515         (setq name (read-string "Newsgroup name: ")))
2516     (setq nname (concat gnus-foreign-group-prefix name))
2517     (while (gnus-gethash nname gnus-newsrc-hashtb)
2518       (setq name (read-string "Name already in use. Newsgroup name: "))
2519       (setq nname (concat gnus-foreign-group-prefix name)))
2520     (if (not how)
2521         (setq how (completing-read (format "%s method: " name) methods nil t)))
2522     (if (not where)
2523         (setq where (read-string 
2524                      (format "Get %s by method %s from: " name how))))
2525     (gnus-group-change-level 
2526      (list t nname 3 nil nil (list (intern how) where))
2527      3 9 (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)
2528      t)
2529     (gnus-group-insert-group-line-info nname)))
2530
2531 (defun gnus-group-edit-newsgroup ()
2532   (interactive)
2533   (let ((group (gnus-group-group-name))
2534         info)
2535     (if group (setq info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
2536       (error "No group on current line"))
2537     (switch-to-buffer (get-buffer-create gnus-group-edit-buffer))
2538     (gnus-add-current-to-buffer-list)
2539     (emacs-lisp-mode)
2540     (erase-buffer)
2541     (insert ";; Type `C-c C-c' after you have edited the newsgroup entry.\n\n")
2542     (insert (format "(gnus-group-set-info\n  '%S)\n" info))
2543     (local-set-key "\C-c\C-c" 'gnus-group-edit-newsgroup-done)))
2544
2545 (defun gnus-group-edit-newsgroup-done ()
2546   (interactive)
2547   (set-buffer (get-buffer-create gnus-group-edit-buffer))
2548   (eval-current-buffer)
2549   (kill-buffer (current-buffer))
2550   (set-buffer gnus-group-buffer)
2551   (gnus-group-update-group (gnus-group-group-name))
2552   (gnus-group-position-cursor))
2553
2554 (defun gnus-group-make-mail-groups (method)
2555   ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
2556   (interactive
2557    (list
2558     (intern
2559      (completing-read
2560       "Mail method: " 
2561       (gnus-methods-using 'mail) nil t "nnmail"))))
2562   (let ((groups nnmail-split-methods)
2563         group)
2564     (while groups
2565       (setq group (concat gnus-foreign-group-prefix (car (car groups))))
2566       (if (not (gnus-gethash group gnus-newsrc-hashtb))
2567           (progn
2568             (gnus-group-change-level 
2569              (list t group 1 nil nil (list method ""))
2570              1 9 (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)
2571              t)
2572             (gnus-group-insert-group-line-info group)))
2573       (setq groups (cdr groups)))))
2574
2575 (defun gnus-group-catchup-current (n &optional all)
2576   "Mark all articles not marked as unread in current newsgroup as read.
2577 If prefix argument N is numeric, the ARG next newsgroups will be
2578 caught up. If ALL is non-nil, marked articles will also be marked as
2579 read. Cross references (Xref: field) of articles are ignored.
2580 The difference between N and actual number of newsgroups that were
2581 caught up is returned."
2582   (interactive "p")
2583   (if (or (not gnus-interactive-catchup) ;Without confirmation?
2584           gnus-expert-user
2585           (y-or-n-p
2586            (if all
2587                "Do you really want to mark all articles as read? "
2588              "Mark all unread articles as read? ")))
2589       (progn
2590         (while 
2591             (and (> n 0)
2592                  (progn
2593                    (setq n (1- n))
2594                    (gnus-group-catchup (gnus-group-group-name) all)
2595                    (gnus-group-update-group-line)
2596                    t)
2597                  (= 0 (gnus-group-next-unread-group 1))))))
2598     n)
2599
2600 (defun gnus-group-catchup-current-all (n)
2601   "Mark all articles in current newsgroup as read.
2602 Cross references (Xref: field) of articles are ignored."
2603   (interactive "p")
2604   (gnus-group-catchup-current n 'all))
2605
2606 (defun gnus-group-catchup (group &optional all)
2607   "Mark all articles in GROUP as read.
2608 If ALL is non-nil, all articles are marked as read.
2609 The return value is the number of articles that were marked as read,
2610 or nil if no action could be taken."
2611   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
2612          (num (car entry))
2613          ticked)
2614     ;; Do the updating only if the newsgroup isn't killed
2615     (if entry
2616         (progn
2617           (setq ticked (if all nil (cdr (assq 'tick (nth 3 (nth 2 entry))))))
2618           (gnus-update-read-articles group ticked nil ticked)))
2619     num))
2620
2621 (defun gnus-group-expire-articles (newsgroup)
2622   "Expire all expirable articles in the current newsgroup."
2623   (interactive (list (gnus-group-group-name)))
2624   (if (not newsgroup) (error "No current newsgroup"))
2625   (let ((expirable 
2626          (assq 'expire (nth 3 (nth 2 (gnus-gethash newsgroup 
2627                                                    gnus-newsrc-hashtb))))))
2628     (if (and expirable 
2629              (gnus-check-backend-function 
2630               'gnus-request-expire-articles newsgroup))
2631         (setcdr expirable
2632                 (gnus-request-expire-articles (cdr expirable) newsgroup)))))
2633
2634 (defun gnus-group-expire-all-groups ()
2635   "Expire all expirable articles in all newsgroups."
2636   (interactive)
2637   (let ((newsrc (cdr gnus-newsrc-assoc)))
2638     (while newsrc
2639       (gnus-group-expire-articles (car (car newsrc)))
2640       (setq newsrc (cdr newsrc)))))
2641
2642 (defun gnus-group-set-current-level (n)
2643   "Set the level of the current group to the numeric prefix."
2644   (interactive "P")
2645   (let ((group (gnus-group-group-name)))
2646     (if (not group) (error "No newsgroup on current line.")
2647     (if (and (numberp n) (>= n 1) (<= n 9))
2648         (progn
2649           (gnus-group-change-level group n (gnus-group-group-level))
2650           (gnus-group-update-group-line))
2651       (error "Illegal level: %s" n)))))
2652
2653 (defun gnus-group-unsubscribe-current-group (arg)
2654   "Toggle subscribe from/to unsubscribe current group."
2655   (interactive "P")
2656   (let ((group (gnus-group-group-name)))
2657     (if group
2658         (progn
2659           (if (not arg) 
2660               (setq arg (if (<= (gnus-group-group-level) 5) 7 3)))
2661           (gnus-group-unsubscribe-group group arg)
2662           (gnus-group-next-group 1))
2663       (message "No newsgroup on current line"))))
2664
2665 (defun gnus-group-unsubscribe-group (group &optional level)
2666   "Toggle subscribe from/to unsubscribe GROUP.
2667 New newsgroup is added to .newsrc automatically."
2668   (interactive
2669    (list (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
2670   (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
2671     (cond (newsrc
2672            ;; Toggle subscription flag.
2673            (gnus-group-change-level 
2674             newsrc (if level level (if (< (nth 1 (nth 2 newsrc)) 6) 7 4)))
2675            (gnus-group-update-group group))
2676           ((and (stringp group)
2677                 (gnus-gethash group gnus-active-hashtb))
2678            ;; Add new newsgroup.
2679            (gnus-group-change-level 
2680             group 
2681             (if level level 3) 
2682             (if (member group gnus-zombie-list) 8 9)
2683             (or (and (gnus-group-group-name)
2684                      (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb))
2685                 (gnus-gethash (car (car gnus-newsrc-assoc)) 
2686                               gnus-newsrc-hashtb)))
2687            (gnus-group-update-group group))
2688           (t (error "No such newsgroup: %s" group)))
2689     (gnus-group-position-cursor)))
2690
2691 (defun gnus-group-transpose-groups (arg)
2692   "Exchange current newsgroup and previous newsgroup.
2693 With argument ARG, takes previous newsgroup and moves it past ARG newsgroup."
2694   (interactive "p")
2695   ;; BUG: last newsgroup and the last but one cannot be transposed
2696   ;; since gnus-group-search-forward does not move forward beyond the
2697   ;; last.  If we instead use forward-line, no problem, but I don't
2698   ;; want to use it for later extension.
2699   (while (> arg 0)
2700     (gnus-group-search-forward t t)
2701     (gnus-group-kill-group 1)
2702     (gnus-group-search-forward nil t)
2703     (gnus-group-yank-group)
2704     (gnus-group-search-forward nil t)
2705     (setq arg (1- arg))
2706     ))
2707
2708 (defun gnus-group-kill-all-zombies ()
2709   "Kill all zombie newsgroups."
2710   (interactive)
2711   (setq gnus-killed-list (nconc gnus-zombie-list gnus-killed-list))
2712   (setq gnus-zombie-list nil)
2713   (gnus-group-prepare 5)
2714   (goto-char (point-min))
2715   (gnus-group-position-cursor))
2716
2717 (defun gnus-group-kill-region (begin end)
2718   "Kill newsgroups in current region (excluding current point).
2719 The killed newsgroups can be yanked by using \\[gnus-group-yank-group]."
2720   (interactive "r")
2721   (let ((lines
2722          ;; Exclude a line where current point is on.
2723          (1-
2724           ;; Count lines.
2725           (save-excursion
2726             (count-lines
2727              (progn
2728                (goto-char begin)
2729                (beginning-of-line)
2730                (point))
2731              (progn
2732                (goto-char end)
2733                (end-of-line)
2734                (point)))))))
2735     (goto-char begin)
2736     (beginning-of-line)                 ;Important when LINES < 1
2737     (gnus-group-kill-group lines)))
2738
2739 (defun gnus-group-kill-group (n)
2740   "Kill newsgroup on current line, repeated prefix argument N times.
2741 The killed newsgroups can be yanked by using \\[gnus-group-yank-group].
2742 However, only groups that were alive can be yanked; already killed 
2743 groups or zombie groups can't be yanked.
2744 The return value is the name of the (last) newsgroup that was killed."
2745   (interactive "p")
2746   (let ((buffer-read-only nil)
2747         group entry level)
2748     (while (>= (setq n  (1- n)) 0)
2749       (setq group (gnus-group-group-name))
2750       (or group
2751           (signal 'end-of-buffer nil))
2752       (setq level (gnus-group-group-level))
2753       (beginning-of-line)
2754       (delete-region (point)
2755                      (progn (forward-line 1) (point)))
2756       (if (setq entry (gnus-gethash group gnus-newsrc-hashtb))
2757           (setq gnus-list-of-killed-groups 
2758                 (cons (cons (car entry) (nth 2 entry)) 
2759                       gnus-list-of-killed-groups)))
2760       (gnus-group-change-level 
2761        (if entry entry group) 9
2762        (if entry nil level)))
2763     (if (eobp)
2764         (forward-line -1))
2765     (gnus-group-position-cursor)
2766     group))
2767
2768 (defun gnus-group-yank-group (&optional arg)
2769   "Yank the last newsgroups killed with \\[gnus-group-kill-group],
2770 inserting it before the current newsgroup.  The numeric ARG specifies
2771 how many newsgroups are to be yanked.  The name of the (last)
2772 newsgroup yanked is returned."
2773   (interactive "p")
2774   (if (not arg) (setq arg 1))
2775   (let (info group prev)
2776     (while (>= (setq arg (1- arg)) 0)
2777       (if (not (setq info (car gnus-list-of-killed-groups)))
2778           (error "No more newsgroups to yank"))
2779       (setq group (nth 2 info))
2780       ;; Find which newsgroup to insert this one before - search
2781       ;; backward until something suitable is found. If there are no
2782       ;; other newsgroups in this buffer, just make this newsgroup the
2783       ;; first newsgroup.
2784       (while (and (not (setq prev (gnus-group-group-name)))
2785                   (= 0 (forward-line -1))))
2786       (if (not prev)
2787           (setq prev (car (car gnus-newsrc-assoc))))
2788       (gnus-group-change-level 
2789        info (nth 2 info) 9 
2790        (gnus-gethash prev gnus-newsrc-hashtb)
2791        t)
2792       (gnus-group-insert-group-line-info (nth 1 info))
2793       (setq gnus-list-of-killed-groups 
2794             (cdr gnus-list-of-killed-groups)))
2795     (forward-line -1)
2796     (gnus-group-position-cursor)
2797     group))
2798       
2799 (defun gnus-group-list-all-groups (arg)
2800   "List all newsgroups with level ARG or lower.
2801 Default is 7, which lists all subscribed and unsubscribed groups."
2802   (interactive "P")
2803   (setq arg (or arg 7))
2804   (gnus-group-list-groups arg t))
2805
2806 (defun gnus-group-list-killed ()
2807   "List all killed newsgroups in the Newsgroup buffer."
2808   (interactive)
2809   (gnus-group-prepare 9 t 9)
2810   (goto-char (point-min))
2811   (gnus-group-position-cursor))
2812
2813 (defun gnus-group-list-zombies ()
2814   "List all zombie newsgroups in the Newsgroup buffer."
2815   (interactive)
2816   (gnus-group-prepare 8 t 8)
2817   (goto-char (point-min))
2818   (gnus-group-position-cursor))
2819
2820 (defun gnus-group-get-new-news (&optional arg)
2821   "Get newly arrived articles.
2822 If ARG is non-nil, it should be a number between one and nine to
2823 specify which levels you are interested in re-scanning."
2824   (interactive "P")
2825   (if (and gnus-read-active-file (not arg))
2826       (gnus-read-active-file))
2827   (if arg
2828       (let ((gnus-read-active-file nil))
2829         (gnus-get-unread-articles arg))
2830     (gnus-get-unread-articles 7))
2831   (gnus-group-list-groups 5 gnus-have-all-newsgroups))
2832
2833 (defun gnus-group-get-new-news-this-group (n)
2834   "Check for newly arrived news in the current group (and the N-1 next groups).
2835 The difference between N and the number of newsgroup checked is returned.
2836 If N is negative, this group and the N-1 previous groups will be checked."
2837   (interactive "p")
2838   (let ((backward (< n 0))
2839         (n (abs n))
2840         group)
2841   (while (and (> n 0)
2842               (progn
2843                 (and (setq group (gnus-group-group-name))
2844                      (gnus-activate-newsgroup 
2845                       group (gnus-group-real-name group))
2846                      (progn
2847                        (gnus-get-unread-articles-in-group 
2848                         (nth 2 (gnus-gethash group gnus-newsrc-hashtb))
2849                         (gnus-gethash group gnus-active-hashtb))
2850                        (gnus-group-update-group-line)))
2851                 t)
2852               (= 0 (gnus-group-next-group 1)))
2853     (setq n (1- n)))
2854   (if (/= 0 n) (message "No more newsgroups"))
2855   n))
2856   
2857 (defun gnus-group-describe-group (&optional group)
2858   "Display a description of the current newsgroup."
2859   (interactive)
2860   (let ((group (or group (gnus-group-group-name))))
2861     (if (not group)
2862         (message "No group on current line")
2863       (and (or gnus-description-hashtb
2864                (gnus-read-descriptions-file))
2865            (message
2866             (or (gnus-gethash group gnus-description-hashtb)
2867                 "No description available"))))))
2868
2869 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
2870 (defun gnus-group-describe-all-groups ()
2871   "Pop up a buffer with descriptons of all newsgroups."
2872   (interactive)
2873   (if (not (or gnus-description-hashtb
2874                (gnus-read-descriptions-file)))
2875       (error "Couldn't request descriptions file"))
2876   (let ((buffer-read-only nil)
2877         beg)
2878     (erase-buffer)
2879     (mapatoms
2880      (lambda (group)
2881        (insert (format "      *: %-20s %s" (symbol-name group)
2882                        (symbol-value group)))
2883        (setq beg (point))
2884        (insert (format " %s  6\n" group))
2885        (set-text-properties beg (1- (point)) '(invisible t)))
2886      gnus-description-hashtb)
2887     (goto-char (point-min))
2888     (gnus-group-position-cursor)))
2889
2890 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
2891 (defun gnus-group-save-newsrc ()
2892   "Save the Gnus startup files."
2893   (interactive)
2894   (gnus-save-newsrc-file))
2895
2896 (defun gnus-group-restart (&optional arg)
2897   "Force Gnus to read the .newsrc file."
2898   (interactive "P")
2899   (gnus-save-newsrc-file)
2900   (gnus-setup-news 'force)
2901   (gnus-group-list-groups (or arg 5) gnus-have-all-newsgroups))
2902
2903 (defun gnus-group-read-init-file ()
2904   "Read the Gnus elisp init file."
2905   (interactive)
2906   (gnus-read-init-file))
2907
2908 (defun gnus-group-check-bogus-groups ()
2909   "Check bogus newsgroups."
2910   (interactive)
2911   (gnus-check-bogus-newsgroups (not gnus-expert-user))  ;Require confirmation.
2912   (gnus-group-list-groups 5 gnus-have-all-newsgroups))
2913
2914 (defun gnus-group-mail ()
2915   "Start composing a mail."
2916   (interactive)
2917   (mail))
2918
2919 (defun gnus-group-edit-global-kill ()
2920   "Edit a global KILL file."
2921   (interactive)
2922   (setq gnus-current-kill-article nil)  ;No articles selected.
2923   (gnus-kill-file-edit-file nil)        ;Nil stands for global KILL file.
2924   (message
2925    (substitute-command-keys
2926     "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
2927
2928 (defun gnus-group-edit-local-kill ()
2929   "Edit a local KILL file."
2930   (interactive)
2931   (setq gnus-current-kill-article nil)  ;No articles selected.
2932   (gnus-kill-file-edit-file (gnus-group-group-name))
2933   (message
2934    (substitute-command-keys
2935     "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
2936
2937 (defun gnus-group-force-update ()
2938   "Update `.newsrc' file."
2939   (interactive)
2940   (gnus-save-newsrc-file))
2941
2942 (defun gnus-group-suspend ()
2943   "Suspend the current Gnus session.
2944 In fact, cleanup buffers except for Group Mode buffer.
2945 The hook gnus-suspend-gnus-hook is called before actually suspending."
2946   (interactive)
2947   (run-hooks 'gnus-suspend-gnus-hook)
2948   ;; Kill Gnus buffers except for Group Mode buffer.
2949   (let ((group-buf (get-buffer gnus-group-buffer)))
2950     (while gnus-buffer-list
2951       (and (not (eq (car gnus-buffer-list) group-buf))
2952            (get-buffer (car gnus-buffer-list))
2953            (buffer-name (get-buffer (car gnus-buffer-list)))
2954            (kill-buffer (car gnus-buffer-list)))
2955       (setq gnus-buffer-list (cdr gnus-buffer-list)))
2956     (setq gnus-buffer-list (list group-buf))
2957     (bury-buffer group-buf)
2958     (delete-windows-on group-buf t)))
2959
2960 (defun gnus-group-clear-dribble ()
2961   "Clear all information from the dribble buffer."
2962   (interactive)
2963   (gnus-dribble-clear))
2964
2965 (defun gnus-group-exit ()
2966   "Quit reading news after updating .newsrc.eld and .newsrc.
2967 The hook `gnus-exit-gnus-hook' is called before actually exiting."
2968   (interactive)
2969   (if (or noninteractive                ;For gnus-batch-kill
2970           (zerop (buffer-size))         ;No news is good news.
2971           (not (gnus-server-opened gnus-select-method)) ;NNTP connection closed
2972           (not gnus-interactive-exit)   ;Without confirmation
2973           gnus-expert-user
2974           (y-or-n-p "Are you sure you want to quit reading news? "))
2975       (progn
2976         (message "")                    ;Erase "Yes or No" question.
2977         (run-hooks 'gnus-exit-gnus-hook)
2978         (gnus-save-newsrc-file)
2979         (gnus-clear-system))))
2980
2981 (defun gnus-group-quit ()
2982   "Quit reading news without updating .newsrc.eld or .newsrc.
2983 The hook `gnus-exit-gnus-hook' is called before actually exiting."
2984   (interactive)
2985   (if (or noninteractive                ;For gnus-batch-kill
2986           (zerop (buffer-size))
2987           (not (gnus-server-opened gnus-select-method))
2988           gnus-expert-user
2989           (yes-or-no-p
2990            (format "Quit reading news without saving %s? "
2991                    (file-name-nondirectory gnus-current-startup-file))))
2992       (progn
2993         (message "")                    ;Erase "Yes or No" question.
2994         (run-hooks 'gnus-exit-gnus-hook)
2995         (gnus-dribble-save)
2996         (gnus-clear-system))))
2997
2998 (defun gnus-group-describe-briefly ()
2999   "Give a one line description of the Group mode commands."
3000   (interactive)
3001   (message
3002    (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")))
3003
3004 (defun gnus-group-browse-foreign-server (method)
3005   "Browse a foreign news server.
3006 If called interactively, this function will ask for a select method
3007  (nntp, nnspool, etc.) and a server address (eg. nntp.some.where). 
3008 If not, METHOD should be a list where the first element is the method
3009 and the second element is the address."
3010   (interactive
3011    (list (list (completing-read "Select method: "
3012                                 gnus-valid-select-methods
3013                                 nil t "nntp")
3014                (read-string "Server name: "))))
3015   (gnus-browse-foreign-server method))
3016
3017 \f
3018 ;;;
3019 ;;; Browse Server Mode
3020 ;;;
3021
3022 (defvar gnus-browse-server-mode-hook nil)
3023 (defvar gnus-browse-server-mode-map nil)
3024
3025 (if gnus-browse-server-mode-map
3026     nil
3027   (setq gnus-browse-server-mode-map (make-keymap))
3028   (suppress-keymap gnus-browse-server-mode-map)
3029   (define-key gnus-browse-server-mode-map " " 'gnus-browse-read-group)
3030   (define-key gnus-browse-server-mode-map "=" 'gnus-browse-read-group)
3031   (define-key gnus-browse-server-mode-map "n" 'gnus-group-next-group)
3032   (define-key gnus-browse-server-mode-map "p" 'gnus-group-prev-group)
3033   (define-key gnus-browse-server-mode-map [del] 'gnus-group-prev-group)
3034   (define-key gnus-browse-server-mode-map "N" 'gnus-group-next-group)
3035   (define-key gnus-browse-server-mode-map "P" 'gnus-group-prev-group)
3036   (define-key gnus-browse-server-mode-map "\M-n" 'gnus-group-next-group)
3037   (define-key gnus-browse-server-mode-map "\M-p" 'gnus-group-prev-group)
3038   (define-key gnus-browse-server-mode-map [down] 'gnus-group-next-group)
3039   (define-key gnus-browse-server-mode-map [up] 'gnus-group-prev-group)
3040   (define-key gnus-browse-server-mode-map "\r" 'gnus-group-next-group)
3041   (define-key gnus-browse-server-mode-map "u" 'gnus-browse-unsubscribe-current-group)
3042   (define-key gnus-browse-server-mode-map "q" 'gnus-browse-exit)
3043   (define-key gnus-browse-server-mode-map "Q" 'gnus-browse-exit)
3044   (define-key gnus-browse-server-mode-map "\C-c\C-c" 'gnus-browse-quit)
3045   (define-key gnus-browse-server-mode-map "?" 'gnus-browse-describe-briefly)
3046   (define-key gnus-browse-server-mode-map "\C-c\C-i" 'gnus-info-find-node)
3047   )
3048
3049 (defvar gnus-browse-current-method nil)
3050
3051 (defun gnus-browse-foreign-server (method)
3052   (setq gnus-browse-current-method method)
3053   (let ((gnus-select-method method)
3054         groups group)
3055     (message "Connecting to %s..." (nth 1 method))
3056     (if (not (gnus-request-list method))
3057         (error "Unable to contact server: " (gnus-status-message method)))
3058     (set-buffer (get-buffer-create "*Gnus Browse Server*"))
3059     (gnus-add-current-to-buffer-list)
3060     (buffer-disable-undo (current-buffer))
3061     (let ((buffer-read-only nil))
3062       (erase-buffer))
3063     (gnus-browse-server-mode)
3064     (setq mode-line-buffer-identification
3065           (format
3066            "(ding) Browse Server {%s:%s}" (car method) (car (cdr method))))
3067     (save-excursion
3068       (set-buffer nntp-server-buffer)
3069       (let ((cur (current-buffer)))
3070         (goto-char 1)
3071         (while (re-search-forward 
3072                 "\\(^[^ \t]+\\)[ \t]+[0-9]+[ \t]+[0-9]+" nil t)
3073           (goto-char (match-end 1))
3074           (setq groups (cons (cons (buffer-substring (match-beginning 1)
3075                                                      (match-end 1))
3076                                    (- (read cur) (read cur)))
3077                              groups)))))
3078     (setq groups (sort groups 
3079                        (lambda (l1 l2)
3080                          (string< (car l1) (car l2)))))
3081     (let ((buffer-read-only nil))
3082       (while groups
3083         (setq group (car groups))
3084         (insert 
3085          (format "K%7d: %s\n" (cdr group) (car group)))
3086         (setq groups (cdr groups))))
3087     (switch-to-buffer (current-buffer))
3088     (goto-char 1)
3089     (gnus-group-position-cursor)))
3090
3091 (defun gnus-browse-server-mode ()
3092   "Major mode for reading network news."
3093   (interactive)
3094   (kill-all-local-variables)
3095   (setq mode-line-modified "--- ")
3096   (setq major-mode 'gnus-browse-server-mode)
3097   (setq mode-name "Browse Server")
3098   (setq mode-line-process nil)
3099   (use-local-map gnus-browse-server-mode-map)
3100   (buffer-disable-undo (current-buffer))
3101   (setq truncate-lines t)
3102   (setq buffer-read-only t)
3103   (run-hooks 'gnus-browse-server-mode-hook))
3104
3105 (defun gnus-browse-read-group ()
3106   "Not implemented, and will probably never be."
3107   (interactive)
3108   (error "You can't read while browsing"))
3109
3110 (defun gnus-browse-unsubscribe-current-group (arg)
3111   "(Un)subscribe to the next ARG groups."
3112   (interactive "p")
3113   (let ((ward (if (< arg 0) -1 1))
3114         (arg (abs arg)))
3115     (while (and (> arg 0)
3116                 (gnus-browse-unsubscribe-group)
3117                 (= (gnus-group-next-group ward) 0))
3118       (setq arg (1- arg)))
3119     (gnus-group-position-cursor)
3120     (if (/= 0 arg) (message "No more newsgroups" ))
3121     arg))
3122   
3123 (defun gnus-browse-unsubscribe-group ()
3124   (let ((sub nil)
3125         (buffer-read-only nil)
3126         group)
3127     (save-excursion
3128       (beginning-of-line)
3129       (if (= (following-char) ?K) (setq sub t))
3130       (re-search-forward ": \\(.*\\)$" nil t)
3131       (setq group 
3132             (concat gnus-foreign-group-prefix 
3133                     (buffer-substring (match-beginning 1) (match-end 1))))
3134       (beginning-of-line)
3135       (delete-char 1)
3136       (if sub
3137           (progn
3138             (gnus-group-change-level 
3139              (list t group 3 nil nil gnus-browse-current-method) 3 9 
3140              (gnus-gethash (car (nth 1 gnus-newsrc-assoc)) gnus-newsrc-hashtb)
3141              t)
3142             (insert ? ))
3143         (gnus-group-change-level group 9 3)
3144         (insert ?K)))
3145     t))
3146
3147 (defun gnus-browse-exit ()
3148   "Quit browsing and return to the Newsgroup buffer."
3149   (interactive)
3150   (if (eq major-mode 'gnus-browse-server-mode)
3151       (kill-buffer (current-buffer)))
3152   (switch-to-buffer gnus-group-buffer)
3153   (gnus-group-list-groups 5))
3154
3155 (defun gnus-browse-describe-briefly ()
3156   "Give a one line description of the Group mode commands."
3157   (interactive)
3158   (message
3159    (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")))
3160       
3161 \f
3162 ;;;
3163 ;;; Gnus Summary Mode
3164 ;;;
3165
3166 (defvar gnus-summary-kill-map nil)
3167 (define-prefix-command 'gnus-summary-kill-map)
3168
3169 (if gnus-summary-mode-map
3170     nil
3171   (setq gnus-summary-mode-map (make-keymap))
3172   (suppress-keymap gnus-summary-mode-map)
3173   (define-key gnus-summary-mode-map "\C-c\C-k" gnus-summary-kill-map)
3174   (define-key gnus-summary-mode-map "\C-c\C-v" 'gnus-uu-ctl-map)
3175   (define-key gnus-summary-mode-map "#" 'gnus-summary-mark-as-processable)
3176   (define-key gnus-summary-mode-map "\M-#" 'gnus-summary-unmark-as-processable)
3177   (define-key gnus-summary-mode-map "\C-c\M-#" 'gnus-summary-unmark-all-processable)
3178   (define-key gnus-summary-mode-map " " 'gnus-summary-next-page)
3179   (define-key gnus-summary-mode-map "\177" 'gnus-summary-prev-page)
3180   (define-key gnus-summary-mode-map "\r" 'gnus-summary-scroll-up)
3181   (define-key gnus-summary-mode-map "n" 'gnus-summary-next-unread-article)
3182   (define-key gnus-summary-mode-map "p" 'gnus-summary-prev-unread-article)
3183   (define-key gnus-summary-mode-map "N" 'gnus-summary-next-article)
3184   (define-key gnus-summary-mode-map "P" 'gnus-summary-prev-article)
3185   (define-key gnus-summary-mode-map "\M-\C-n" 'gnus-summary-next-same-subject)
3186   (define-key gnus-summary-mode-map "\M-\C-p" 'gnus-summary-prev-same-subject)
3187   (define-key gnus-summary-mode-map "\C-c\C-n" 'gnus-summary-next-digest)
3188   (define-key gnus-summary-mode-map "\C-c\C-p" 'gnus-summary-prev-digest)
3189   (define-key gnus-summary-mode-map "\M-n" 'gnus-summary-next-unread-subject)
3190   (define-key gnus-summary-mode-map "\M-p" 'gnus-summary-prev-unread-subject)
3191   (define-key gnus-summary-mode-map "." 'gnus-summary-first-unread-article)
3192   (define-key gnus-summary-mode-map "s" 'gnus-summary-isearch-article)
3193   (define-key gnus-summary-mode-map "\M-s" 'gnus-summary-search-article-forward)
3194   (define-key gnus-summary-mode-map "\M-r" 'gnus-summary-search-article-backward)
3195   (define-key gnus-summary-mode-map "<" 'gnus-summary-beginning-of-article)
3196   (define-key gnus-summary-mode-map ">" 'gnus-summary-end-of-article)
3197   (define-key gnus-summary-mode-map "j" 'gnus-summary-goto-subject)
3198   (define-key gnus-summary-mode-map "l" 'gnus-summary-goto-last-article)
3199   (define-key gnus-summary-mode-map "^" 'gnus-summary-refer-parent-article)
3200   (define-key gnus-summary-mode-map "\M-^" 'gnus-summary-refer-article)
3201   (define-key gnus-summary-mode-map "'" 'gnus-summary-tick-article-forward)
3202   (define-key gnus-summary-mode-map "u" 'gnus-summary-tick-article-forward)
3203   (define-key gnus-summary-mode-map "U" 'gnus-summary-tick-article-backward)
3204   (define-key gnus-summary-mode-map "d" 'gnus-summary-mark-as-read-forward)
3205   (define-key gnus-summary-mode-map "D" 'gnus-summary-mark-as-read-backward)
3206   (define-key gnus-summary-mode-map "\M-u" 'gnus-summary-clear-mark-forward)
3207   (define-key gnus-summary-mode-map "\M-U" 'gnus-summary-clear-mark-backward)
3208   (define-key gnus-summary-mode-map "k" 'gnus-summary-kill-same-subject-and-select)
3209   (define-key gnus-summary-mode-map "\C-k" 'gnus-summary-kill-same-subject)
3210   (define-key gnus-summary-mode-map "\M-\C-t" 'gnus-summary-toggle-threads)
3211   (define-key gnus-summary-mode-map "\M-\C-s" 'gnus-summary-show-thread)
3212   (define-key gnus-summary-mode-map "\M-\C-h" 'gnus-summary-hide-thread)
3213   (define-key gnus-summary-mode-map "\M-\C-f" 'gnus-summary-next-thread)
3214   (define-key gnus-summary-mode-map "\M-\C-b" 'gnus-summary-prev-thread)
3215   (define-key gnus-summary-mode-map "\M-\C-u" 'gnus-summary-up-thread)
3216   (define-key gnus-summary-mode-map "\M-\C-d" 'gnus-summary-down-thread)
3217   (define-key gnus-summary-mode-map "\M-\C-k" 'gnus-summary-kill-thread)
3218   (define-key gnus-summary-mode-map "&" 'gnus-summary-execute-command)
3219   (define-key gnus-summary-mode-map "c" 'gnus-summary-catchup-and-exit)
3220   (define-key gnus-summary-mode-map "\C-t" 'gnus-summary-toggle-truncation)
3221   (define-key gnus-summary-mode-map "\M-d" 'gnus-summary-delete-marked-as-read)
3222   (define-key gnus-summary-mode-map "\C-c\M-\C-d" 'gnus-summary-delete-marked-with)
3223   (define-key gnus-summary-mode-map "x" 'gnus-summary-mark-as-expirable)
3224   (define-key gnus-summary-mode-map "X" 'gnus-summary-unmark-as-expirable)
3225   (define-key gnus-summary-mode-map "b" 'gnus-summary-set-bookmark)
3226   (define-key gnus-summary-mode-map "B" 'gnus-summary-remove-bookmark)
3227   (define-key gnus-summary-mode-map "i" 'gnus-summary-mark-as-interesting)
3228   (define-key gnus-summary-mode-map "\M-i" 'gnus-summary-show-all-interesting)
3229   (define-key gnus-summary-mode-map "\C-c\C-sn" 'gnus-summary-sort-by-number)
3230   (define-key gnus-summary-mode-map "\C-c\C-sa" 'gnus-summary-sort-by-author)
3231   (define-key gnus-summary-mode-map "\C-c\C-ss" 'gnus-summary-sort-by-subject)
3232   (define-key gnus-summary-mode-map "\C-c\C-sd" 'gnus-summary-sort-by-date)
3233   (define-key gnus-summary-mode-map "\C-c\C-s\C-n" 'gnus-summary-sort-by-number)
3234   (define-key gnus-summary-mode-map "\C-c\C-s\C-a" 'gnus-summary-sort-by-author)
3235   (define-key gnus-summary-mode-map "\C-c\C-s\C-s" 'gnus-summary-sort-by-subject)
3236   (define-key gnus-summary-mode-map "\C-c\C-s\C-d" 'gnus-summary-sort-by-date)
3237   (define-key gnus-summary-mode-map "=" 'gnus-summary-expand-window)
3238   (define-key gnus-summary-mode-map "\C-x\C-s" 'gnus-summary-reselect-current-group)
3239   (define-key gnus-summary-mode-map "\M-g" 'gnus-summary-rescan-group)
3240   (define-key gnus-summary-mode-map "w" 'gnus-summary-stop-page-breaking)
3241   (define-key gnus-summary-mode-map "\C-c\C-r" 'gnus-summary-caesar-message)
3242   (define-key gnus-summary-mode-map "g" 'gnus-summary-show-article)
3243   (define-key gnus-summary-mode-map "t" 'gnus-summary-toggle-header)
3244   (define-key gnus-summary-mode-map "\M-t" 'gnus-summary-toggle-mime)
3245   (define-key gnus-summary-mode-map "\C-d" 'gnus-summary-rmail-digest)
3246   (define-key gnus-summary-mode-map "a" 'gnus-summary-post-news)
3247   (define-key gnus-summary-mode-map "f" 'gnus-summary-followup)
3248   (define-key gnus-summary-mode-map "F" 'gnus-summary-followup-with-original)
3249   (define-key gnus-summary-mode-map "C" 'gnus-summary-cancel-article)
3250   (define-key gnus-summary-mode-map "S" 'gnus-summary-supersede-article)
3251   (define-key gnus-summary-mode-map "r" 'gnus-summary-reply)
3252   (define-key gnus-summary-mode-map "R" 'gnus-summary-reply-with-original)
3253   (define-key gnus-summary-mode-map "\C-c\C-f" 'gnus-summary-mail-forward)
3254   (define-key gnus-summary-mode-map "m" 'gnus-summary-mail-other-window)
3255   (define-key gnus-summary-mode-map "o" 'gnus-summary-save-article)
3256   (define-key gnus-summary-mode-map "\C-o" 'gnus-summary-save-article-rmail)
3257   (define-key gnus-summary-mode-map "|" 'gnus-summary-pipe-output)
3258   (define-key gnus-summary-mode-map "\M-m" 'gnus-summary-move-article)
3259   (define-key gnus-summary-mode-map "\M-\C-m" 'gnus-summary-respool-article)
3260   (define-key gnus-summary-mode-map "\M-k" 'gnus-summary-edit-local-kill)
3261   (define-key gnus-summary-mode-map "\M-K" 'gnus-summary-edit-global-kill)
3262   (define-key gnus-summary-mode-map "V" 'gnus-version)
3263   (define-key gnus-summary-mode-map "\C-c\C-d" 'gnus-summary-describe-group)
3264   (define-key gnus-summary-mode-map "q" 'gnus-summary-exit)
3265   (define-key gnus-summary-mode-map "Q" 'gnus-summary-quit)
3266   (define-key gnus-summary-mode-map "?" 'gnus-summary-describe-briefly)
3267   (define-key gnus-summary-mode-map "\C-c\C-i" 'gnus-info-find-node)
3268   (define-key gnus-summary-mode-map [mouse-2] 'gnus-mouse-pick-article)
3269   (define-key gnus-summary-kill-map "\C-s" 'gnus-kill-file-kill-by-subject)
3270   (define-key gnus-summary-kill-map "\C-a" 'gnus-kill-file-kill-by-author)
3271   (define-key gnus-summary-kill-map "\C-t" 'gnus-kill-file-kill-by-thread)
3272   (define-key gnus-summary-kill-map "\C-x" 'gnus-kill-file-kill-by-xref)
3273
3274   (define-key gnus-summary-mode-map [menu-bar misc]
3275         (cons "Misc" (make-sparse-keymap "misc")))
3276
3277   (define-key gnus-summary-mode-map [menu-bar misc caesar-message]
3278         '("Caesar Message" . gnus-summary-caesar-message))
3279   (define-key gnus-summary-mode-map [menu-bar misc cancel-article]
3280         '("Cancel Article" . gnus-summary-cancel-article))
3281   (define-key gnus-summary-mode-map [menu-bar misc edit-local-kill]
3282         '("Edit Kill File" . gnus-summary-edit-local-kill))
3283
3284   (define-key gnus-summary-mode-map [menu-bar misc tick]
3285         '("Tick" . gnus-summary-tick-article-forward))
3286   (define-key gnus-summary-mode-map [menu-bar misc mark-as-read]
3287         '("Mark as Read" . gnus-summary-mark-as-read))
3288
3289   (define-key gnus-summary-mode-map [menu-bar misc quit]
3290         '("Quit Group" . gnus-summary-quit))
3291   (define-key gnus-summary-mode-map [menu-bar misc exit]
3292         '("Exit Group" . gnus-summary-exit))
3293
3294   (define-key gnus-summary-mode-map [menu-bar sort]
3295         (cons "Sort" (make-sparse-keymap "sort")))
3296
3297   (define-key gnus-summary-mode-map [menu-bar sort sort-by-author]
3298         '("Sort by Author" . gnus-summary-sort-by-author))
3299   (define-key gnus-summary-mode-map [menu-bar sort sort-by-date]
3300         '("Sort by Date" . gnus-summary-sort-by-date))
3301   (define-key gnus-summary-mode-map [menu-bar sort sort-by-number]
3302         '("Sort by Number" . gnus-summary-sort-by-number))
3303   (define-key gnus-summary-mode-map [menu-bar sort sort-by-subject]
3304         '("Sort by Subject" . gnus-summary-sort-by-subject))
3305
3306   (define-key gnus-summary-mode-map [menu-bar show/hide]
3307         (cons "Show/Hide" (make-sparse-keymap "show/hide")))
3308
3309   (define-key gnus-summary-mode-map [menu-bar show/hide hide-all-threads]
3310         '("Hide All Threads" . gnus-summary-hide-all-threads))
3311   (define-key gnus-summary-mode-map [menu-bar show/hide hide-thread]
3312         '("Hide Thread" . gnus-summary-hide-thread))
3313   (define-key gnus-summary-mode-map [menu-bar show/hide show-all-threads]
3314         '("Show All Threads" . gnus-summary-show-all-threads))
3315   (define-key gnus-summary-mode-map [menu-bar show/hide show-all-headers]
3316         '("Show All Headers" . gnus-summary-show-all-headers))
3317   (define-key gnus-summary-mode-map [menu-bar show/hide show-thread]
3318         '("Show Thread" . gnus-summary-show-thread))
3319   (define-key gnus-summary-mode-map [menu-bar show/hide show-article]
3320         '("Show Article" . gnus-summary-show-article))
3321   (define-key gnus-summary-mode-map [menu-bar show/hide toggle-truncation]
3322         '("Toggle Truncation" . gnus-summary-toggle-truncation))
3323   (define-key gnus-summary-mode-map [menu-bar show/hide toggle-mime]
3324         '("Toggle Mime" . gnus-summary-toggle-mime))
3325   (define-key gnus-summary-mode-map [menu-bar show/hide toggle-header]
3326         '("Toggle Header" . gnus-summary-toggle-header))
3327
3328   (define-key gnus-summary-mode-map [menu-bar action]
3329         (cons "Action" (make-sparse-keymap "action")))
3330
3331   (define-key gnus-summary-mode-map [menu-bar action kill-same-subject]
3332         '("Kill Same Subject" . gnus-summary-kill-same-subject))
3333   (define-key gnus-summary-mode-map [menu-bar action kill-thread]
3334         '("Kill Thread" . gnus-summary-kill-thread))
3335   (define-key gnus-summary-mode-map [menu-bar action delete-marked-with]
3336         '("Delete Marked With" . gnus-summary-delete-marked-with))
3337   (define-key gnus-summary-mode-map [menu-bar action delete-marked-as-read]
3338         '("Delete Marked As Read" . gnus-summary-delete-marked-as-read))
3339   (define-key gnus-summary-mode-map [menu-bar action catchup-and-exit]
3340         '("Catchup And Exit" . gnus-summary-catchup-and-exit))
3341   (define-key gnus-summary-mode-map [menu-bar action catchup-to-here]
3342         '("Catchup to Here" . gnus-summary-catchup-to-here))
3343
3344   (define-key gnus-summary-mode-map [menu-bar action ignore]
3345     '("---"))
3346
3347   (define-key gnus-summary-mode-map [menu-bar action save-in-file]
3348         '("Save in File" . gnus-summary-save-in-file))
3349   (define-key gnus-summary-mode-map [menu-bar action save-article]
3350         '("Save Article" . gnus-summary-save-article))
3351
3352   (define-key gnus-summary-mode-map [menu-bar action lambda]
3353     '("---"))
3354
3355   (define-key gnus-summary-mode-map [menu-bar action forward]
3356         '("Forward" . gnus-summary-mail-forward))
3357   (define-key gnus-summary-mode-map [menu-bar action followup-with-original]
3358         '("Followup with Original" . gnus-summary-followup-with-original))
3359   (define-key gnus-summary-mode-map [menu-bar action followup]
3360         '("Followup" . gnus-summary-followup))
3361   (define-key gnus-summary-mode-map [menu-bar action reply-with-original]
3362         '("Reply with Original" . gnus-summary-reply-with-original))
3363   (define-key gnus-summary-mode-map [menu-bar action reply]
3364         '("Reply" . gnus-summary-reply))
3365   (define-key gnus-summary-mode-map [menu-bar action post]
3366         '("Post News" . gnus-summary-post-news))
3367
3368   (define-key gnus-summary-mode-map [menu-bar move]
3369         (cons "Move" (make-sparse-keymap "move")))
3370
3371   (define-key gnus-summary-mode-map [menu-bar move isearch-article]
3372         '("Search in Article" . gnus-summary-isearch-article))
3373   (define-key gnus-summary-mode-map [menu-bar move search-through-articles]
3374         '("Search through Articles" . gnus-summary-search-article-forward))
3375   (define-key gnus-summary-mode-map [menu-bar move down-thread]
3376         '("Down Thread" . gnus-summary-down-thread))
3377   (define-key gnus-summary-mode-map [menu-bar move prev-same-subject]
3378         '("Prev Same Subject" . gnus-summary-prev-same-subject))
3379   (define-key gnus-summary-mode-map [menu-bar move prev-group]
3380         '("Prev Group" . gnus-summary-prev-group))
3381   (define-key gnus-summary-mode-map [menu-bar move next-unread-same-subject]
3382         '("Next Unread Same Subject" . gnus-summary-next-unread-same-subject))
3383   (define-key gnus-summary-mode-map [menu-bar move next-unread-article]
3384         '("Next Unread Article" . gnus-summary-next-unread-article))
3385   (define-key gnus-summary-mode-map [menu-bar move next-thread]
3386         '("Next Thread" . gnus-summary-next-thread))
3387   (define-key gnus-summary-mode-map [menu-bar move next-group]
3388         '("Next Group" . gnus-summary-next-group))
3389   (define-key gnus-summary-mode-map [menu-bar move first-unread-article]
3390         '("First Unread Article" . gnus-summary-first-unread-article))
3391   )
3392 \f
3393
3394 (defun gnus-summary-mode ()
3395   "Major mode for reading articles in this newsgroup.
3396 All normal editing commands are switched off.
3397 The following commands are available:
3398
3399 \\<gnus-summary-mode-map>
3400 \\[gnus-summary-next-page]\t Scroll the article buffer a page forwards
3401 \\[gnus-summary-prev-page]\t Scroll the article buffer a page backwards
3402 \\[gnus-summary-scroll-up]\t Scroll the article buffer one line forwards
3403 \\[gnus-summary-next-unread-article]\t Go to the next unread article
3404 \\[gnus-summary-prev-unread-article]\t Go to the previous unread article
3405 \\[gnus-summary-next-article]\t Go to the next article
3406 \\[gnus-summary-prev-article]\t Go to the previous article
3407 \\[gnus-summary-next-same-subject]\t Go to the next summary line with the same subject
3408 \\[gnus-summary-prev-same-subject]\t Go to the previous summary line with the same subject
3409 \\[gnus-summary-next-digest]\t Go to the next digest
3410 \\[gnus-summary-prev-digest]\t Go to the previous digest
3411 \\[gnus-summary-next-subject]\t Go to the next summary line
3412 \\[gnus-summary-prev-subject]\t Go to the previous summary line
3413 \\[gnus-summary-next-unread-subject]\t Go to the next unread summary line
3414 \\[gnus-summary-prev-unread-subject]\t Go to the previous unread summary line
3415 \\[gnus-summary-first-unread-article]\t Go to the first unread article
3416 \\[gnus-summary-goto-subject]\t Go to some subject
3417 \\[gnus-summary-goto-last-article]\t Go to the previous article
3418
3419 \\[gnus-summary-beginning-of-article]\t Go to the beginning of the article
3420 \\[gnus-summary-end-of-article]\t Go to the end of the article
3421
3422 \\[gnus-summary-refer-parent-article]\t Get the parent of the current article from the server
3423 \\[gnus-summary-refer-article]\t Request some article by Message-ID from the server
3424
3425 \\[gnus-summary-isearch-article]\t Do an interactive search on the current article
3426 \\[gnus-summary-search-article-forward]\t Search all articles forward for a regular expression
3427 \\[gnus-summary-search-article-backward]\t Search all articles backward for a regular expression
3428
3429 \\[gnus-summary-tick-article-forward]\t Tick current article and move forward
3430 \\[gnus-summary-tick-article-backward]\t Tick current article and move backward
3431 \\[gnus-summary-mark-as-read-forward]\t Mark the current article as read and move forward
3432 \\[gnus-summary-mark-as-read-backward]\t Mark the current article as read and move backward
3433 \\[gnus-summary-clear-mark-forward]\t Clear tick and read marks and move forward
3434 \\[gnus-summary-clear-mark-backward]\t Clear tick and read marks and move backward
3435 \\[gnus-summary-mark-as-processable]\t Set the process mark on the current article
3436 \\[gnus-summary-unmark-as-processable]\t Remove the process mark from the current article
3437 \\[gnus-summary-unmark-all-processable]\t Remove the process mark from all articles
3438
3439 \\[gnus-summary-kill-same-subject-and-select]\t Kill all articles with the current subject and select the next article
3440 \\[gnus-summary-kill-same-subject]\t Kill all articles with the current subject
3441
3442 \\[gnus-summary-toggle-threads]\t Toggle thread display
3443 \\[gnus-summary-show-thread]\t Show the current thread
3444 \\[gnus-summary-hide-thread]\t Hide the current thread
3445 \\[gnus-summary-next-thread]\t Go to the next thread
3446 \\[gnus-summary-prev-thread]\t Go to the previous thread
3447 \\[gnus-summary-up-thread]\t Go up the current thread
3448 \\[gnus-summary-down-thread]\t Descend the current thread
3449 \\[gnus-summary-kill-thread]\t Kill the current thread
3450 \\[gnus-summary-mark-as-expirable]\t Mark the current artivles as expirable
3451 \\[gnus-summary-unmark-as-expirable]\t Remove the expirable mark from the current article
3452 \\[gnus-summary-delete-marked-as-read]\t Delete all articles that are marked as read
3453 \\[gnus-summary-delete-marked-with]\t Delete all articles that have some mark
3454
3455 \\[gnus-summary-execute-command]\t Execute a command
3456 \\[gnus-summary-catchup-and-exit]\t Mark all unread articles as read and exit
3457 \\[gnus-summary-toggle-truncation]\t Toggle truncation of summary lines
3458 \\[gnus-summary-expand-window]\t Expand the summary window
3459
3460 \\[gnus-summary-sort-by-number]\t Sort the Summary buffer by article number
3461 \\[gnus-summary-sort-by-author]\t Sort the Summary buffer by author
3462 \\[gnus-summary-sort-by-subject]\t Sort the Summary buffer by subject
3463 \\[gnus-summary-sort-by-date]\t Sort the Summary buffer by date
3464
3465 \\[gnus-summary-reselect-current-group]\t Exit and reselect the current group
3466 \\[gnus-summary-rescan-group]\t Exit, get new articles and reselect the group
3467 \\[gnus-summary-stop-page-breaking]\t Stop page breaking of the current article
3468 \\[gnus-summary-caesar-message]\t Caesar rotate (rot13) the current article
3469 \\[gnus-summary-show-article]\t Reselect the current article
3470 \\[gnus-summary-toggle-header]\t Toggle header display
3471 \\[gnus-summary-toggle-mime]\t Toggle whether to use MIME
3472 \\[gnus-summary-rmail-digest]\t Use rmail digest
3473 \\[gnus-summary-post-news]\t Post an article to the current group
3474 \\[gnus-summary-followup]\t Post a followup to the current article
3475 \\[gnus-summary-followup-with-original]\t Post a followup and include the original article
3476 \\[gnus-summary-cancel-article]\t Cancel the current article
3477 \\[gnus-summary-supersede-article]\t Supersede the current article
3478 \\[gnus-summary-reply]\t Mail a reply to the author of the current article
3479 \\[gnus-summary-reply-with-original]\t Mail a reply and include the current article
3480 \\[gnus-summary-mail-forward]\t Forward the current article
3481 \\[gnus-summary-mail-other-window]\t Mail in the other window
3482 \\[gnus-summary-save-article]\t Save the current article
3483 \\[gnus-summary-save-article-rmail]\t Save the current article in rmail format
3484 \\[gnus-summary-pipe-output]\t Pipe the current article to a process
3485 \\[gnus-summary-move-article]\t Move the article to a different newsgroup
3486 \\[gnus-summary-respool-article]\t Respool the article
3487 \\[gnus-summary-edit-local-kill]\t Edit the local kill file
3488 \\[gnus-summary-edit-global-kill]\t Edit the global kill file
3489 \\[gnus-version]\t Display the current Gnus version
3490 \\[gnus-summary-exit]\t Exit the Summary buffer 
3491 \\[gnus-summary-quit]\t Exit the Summary buffer without saving any changes
3492 \\[gnus-summary-describe-group]\t Describe the current newsgroup
3493 \\[gnus-summary-describe-briefly]\t Give a brief key overview
3494 \\[gnus-info-find-node]\t Go to the Gnus info node
3495 \\[gnus-kill-file-kill-by-subject]\t Kill articles with the current subject
3496 \\[gnus-kill-file-kill-by-author]\t Kill articles from the current author
3497 \\[gnus-kill-file-kill-by-thread]\t Kill articles in the current thread
3498 \\[gnus-kill-file-kill-by-xref]\t Kill articles with the current cross-posting
3499 "
3500   (interactive)
3501   (kill-all-local-variables)
3502   (let ((locals gnus-summary-local-variables))
3503     (while locals
3504       (make-local-variable (car locals))
3505       (set (car locals) nil)
3506       (setq locals (cdr locals))))
3507   (gnus-update-format-specifications)
3508   (setq mode-line-modified "--- ")
3509   (setq major-mode 'gnus-summary-mode)
3510   (setq mode-name "Summary")
3511   (make-local-variable 'minor-mode-alist)
3512   (or (assq 'gnus-show-threads minor-mode-alist)
3513       (setq minor-mode-alist
3514             (cons (list 'gnus-show-threads " Thread") minor-mode-alist)))
3515   (gnus-set-mode-line 'summary)
3516   (use-local-map gnus-summary-mode-map)
3517   (buffer-disable-undo (current-buffer))
3518   (setq buffer-read-only t)             ;Disable modification
3519   (setq truncate-lines t)
3520   (setq selective-display t)
3521   (setq selective-display-ellipses t)   ;Display `...'
3522   (run-hooks 'gnus-summary-mode-hook))
3523
3524 (defun gnus-mouse-pick-article (e)
3525   (interactive "e")
3526   (mouse-set-point e)
3527   (gnus-summary-next-page nil))
3528
3529 (defun gnus-summary-setup-buffer (group)
3530   "Initialize Summary buffer."
3531   (let ((buffer (concat "*Summary " group "*")))
3532     ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
3533     (setq gnus-summary-buffer (set-buffer (get-buffer-create buffer)))
3534     (gnus-add-current-to-buffer-list)
3535     (gnus-summary-mode)))
3536
3537 (defun gnus-summary-insert-dummy-line (sformat subject number)
3538   (if (not sformat) 
3539       (setq sformat gnus-summary-dummy-line-format-spec))
3540   (let (b)
3541     (beginning-of-line)
3542     (insert (eval sformat))
3543     (forward-char -1)
3544     (setq b (point))
3545     (insert (format "%s Z %d 0" subject number))
3546     (set-text-properties b (point) '(invisible t))
3547     (forward-char 1)))
3548
3549 (defun gnus-summary-insert-line 
3550   (sformat header level current unread replied expirable print-subject
3551            &optional dummy)
3552   (if (not sformat) 
3553       (setq sformat gnus-summary-line-format-spec))
3554   (let* ((thread-space (if (< level 1) "" (make-string (frame-width) ? )))
3555          (indentation 
3556           (make-string (* level gnus-thread-indent-level) ? ))
3557          (lines (or (header-lines header) 0))
3558          (current (if current ?+ ? ))
3559          (replied (if replied ?R ? ))
3560          (expirable (if expirable ?X ? ))
3561          (from (header-from header))
3562          (name-address (gnus-extract-address-components from))
3563          (address (cdr name-address))
3564          (name (car name-address))
3565          (number (header-number header))
3566          (subject (header-subject header))
3567          (subject-or-nil (if print-subject subject ""))
3568          (buffer-read-only nil)
3569          (closing-bracket (if dummy ?= ?\]))
3570          (opening-bracket (if dummy ?= ?\[))
3571          b)
3572     ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
3573     (if (not (numberp lines)) (setq lines 0))
3574     (beginning-of-line)
3575     (insert (eval sformat))
3576     (forward-char -1)
3577     (setq b (point))
3578     ;; Info format SUBJECT UNREAD NUMBER LEVEL
3579     (insert (format "%s %c %d %d" (gnus-simplify-subject-re subject)
3580                      unread number level))
3581     (set-text-properties b (point) '(invisible t))
3582     (forward-char 1)))
3583
3584 (defun gnus-summary-read-group (group &optional show-all no-article kill-buffer)
3585   "Start reading news in newsgroup GROUP.
3586 If SHOW-ALL is non-nil, already read articles are also listed.
3587 If NO-ARTICLE is non-nil, no article is selected initially."
3588   (message "Retrieving newsgroup: %s..." (gnus-group-real-name group))
3589   (gnus-summary-setup-buffer group)
3590   (if (gnus-select-newsgroup group show-all)
3591       (progn
3592         ;; You can change the order of subjects in this hook.
3593         (run-hooks 'gnus-select-group-hook)
3594         (gnus-summary-prepare)
3595         (let ((killed 
3596                (gnus-add-to-range 
3597                 gnus-newsgroup-killed 
3598                 (setq gnus-newsgroup-unreads
3599                       (sort gnus-newsgroup-unreads (function <)))))
3600               (gnus-newsgroup-killed 
3601                (if gnus-kill-killed nil gnus-newsgroup-killed)))
3602           (if (not (consp (car killed))) (setq killed (list killed)))
3603           ;; Function `gnus-apply-kill-file' must be called in this hook.
3604           (run-hooks 'gnus-apply-kill-hook)
3605           (setq gnus-newsgroup-killed killed))
3606         (if (zerop (buffer-size))
3607             ;; This newsgroup is empty.
3608             (progn
3609               (gnus-summary-catchup-and-exit nil t) ;Without confirmations.
3610               (message "No unread news"))
3611           ;; Hide conversation thread subtrees.  We cannot do this in
3612           ;; gnus-summary-prepare-hook since kill processing may not
3613           ;; work with hidden articles.
3614           (and gnus-show-threads
3615                gnus-thread-hide-subtree
3616                (gnus-summary-hide-all-threads))
3617           ;; Show first unread article if requested.
3618           (goto-char (point-min))
3619           (if (and (not no-article)
3620                    gnus-auto-select-first
3621                    (gnus-summary-first-unread-article))
3622               ;; Window is configured automatically.
3623               ;; Current buffer may be changed as a result of hook
3624               ;; evaluation, especially by gnus-summary-rmail-digest
3625               ;; command, so we should adjust cursor point carefully.
3626               (if (eq major-mode 'gnus-summary-mode)
3627                   (gnus-summary-position-cursor))
3628             (gnus-configure-windows 'summary)
3629             (pop-to-buffer gnus-summary-buffer)
3630             (gnus-set-mode-line 'summary)
3631             (gnus-summary-position-cursor))
3632           (if (and kill-buffer
3633                    (get-buffer kill-buffer)
3634                    ;; Bug by Sudish Joseph <joseph@cis.ohio-state.edu>
3635                    (buffer-name (get-buffer kill-buffer)))
3636               (progn
3637                 (kill-buffer (get-buffer kill-buffer))))))
3638     ;; Cannot select newsgroup GROUP.
3639     (message "Couldn't select newsgroup")
3640     (gnus-summary-position-cursor)))
3641
3642 (defun gnus-summary-prepare ()
3643   "Prepare summary list of current newsgroup in Summary buffer."
3644   (let ((buffer-read-only nil))
3645     (erase-buffer)
3646     (gnus-summary-prepare-threads 
3647           (if gnus-show-threads
3648               (gnus-gather-threads (gnus-make-threads))
3649             gnus-newsgroup-headers)
3650           0)
3651     (gnus-summary-delete-interesting)
3652     ;; Erase header retrieval message.
3653     (message "")
3654     ;; Call hooks for modifying Summary buffer.
3655     ;; Suggested by sven@tde.LTH.Se (Sven Mattisson).
3656     (goto-char (point-min))
3657     (run-hooks 'gnus-summary-prepare-hook)))
3658
3659 (defun gnus-summary-delete-interesting ()
3660   (let ((int gnus-newsgroup-interesting)
3661         (buffer-read-only nil)
3662         beg cur-level)
3663     (while int
3664       (if (gnus-summary-goto-subject (car int))
3665           (progn
3666             (beginning-of-line)
3667             (setq cur-level (gnus-summary-thread-level))
3668             (setq beg (point))
3669             (re-search-forward "[\n\r]")
3670             (if (<= (gnus-summary-thread-level) cur-level)
3671                 ;; If the level of the next article is greater than the
3672                 ;; level of this article, then it has to be the child of this
3673                 ;; article, so we do not delete this article.
3674                 (progn
3675                   (setq gnus-newsgroup-interesting-subjects
3676                         (cons (cons (car int) (buffer-substring beg (point)))
3677                               gnus-newsgroup-interesting-subjects))
3678                   (delete-region beg (point))))))
3679       (setq int (cdr int)))))
3680
3681 (defun gnus-gather-threads (threads)
3682   "Gather threads that have lost their roots."
3683   (if (not gnus-gather-loose-threads)
3684       threads 
3685     (let ((hashtb (gnus-make-hashtable 1023))
3686           (prev threads)
3687           (result threads)
3688           thread subject hthread)
3689       (while threads
3690         (setq subject (header-subject (car (car threads))))
3691         (if (setq hthread (gnus-gethash subject hashtb))
3692             (progn
3693               (if (not (stringp (car (car hthread))))
3694                   (setcar hthread (list subject (car hthread))))
3695               (setcar hthread
3696                       (append (car hthread) (cons (car threads) nil)))
3697               (setcdr prev (cdr threads))
3698               (setq threads prev))
3699           (gnus-sethash subject threads hashtb))
3700         (setq prev threads)
3701         (setq threads (cdr threads)))
3702       result)))
3703
3704 (defun gnus-make-threads ()
3705   ;; This function takes the dependencies already made by
3706   ;; `gnus-get-newsgroup-headers' and builds the trees. First we go
3707   ;; through the dependecies in the hash table and finds all the
3708   ;; roots. Roots do not refer back to any valid articles. 
3709   (let (roots mroots)
3710     (mapatoms
3711      (lambda (refs)
3712        (if (not (car (symbol-value refs)))
3713            (setq mroots (nconc (cdr (symbol-value refs)) mroots))
3714          ;; Ok, these refer back to valid articles, but if
3715          ;; `gnus-thread-ignore-subject' is nil, we have to check that
3716          ;; the root has the same subject as its children. The clidren
3717          ;; that do not are made into roots and remove from the list
3718          ;; of children. 
3719          (or gnus-thread-ignore-subject
3720              (let* ((prev (symbol-value refs))
3721                     (subject (gnus-simplify-subject-re 
3722                               (header-subject (car prev))))
3723                     (headers (cdr prev)))
3724                (while headers
3725                  (if (not (string= subject
3726                                    (gnus-simplify-subject-re 
3727                                     (header-subject (car headers)))))
3728                      (progn
3729                        (setq mroots (cons (car headers) mroots))
3730                        (setcdr prev (cdr headers))))
3731                  (setq prev headers
3732                        headers (cdr headers)))))))
3733      gnus-newsgroup-dependencies)
3734
3735     ;; We sort the roots according to article number. (This has to be
3736     ;; done because all sequencing information was lost when we built
3737     ;; the dependecies hash table.)
3738     (setq roots
3739           (sort
3740            mroots
3741            (lambda (h1 h2)
3742              (< (header-number h1) (header-number h2)))))
3743     ;; Now we have all the roots, so we go through all them all and
3744     ;; build the trees. 
3745     (mapcar (lambda (root) (gnus-make-sub-thread root)) roots)))
3746
3747 (defun gnus-make-sub-thread (root)
3748   ;; This function makes a sub-tree for a node in the tree.
3749   (let ((children (nreverse (cdr (gnus-gethash (header-id root) 
3750                                                gnus-newsgroup-dependencies)))))
3751     (if (not children)
3752         (list root)
3753       (cons root (mapcar 
3754                   (lambda (top) (gnus-make-sub-thread top)) children)))))
3755
3756 ;; Basic ideas by Paul Dworkin <paul@media-lab.media.mit.edu>
3757 ;; Subject bug fix by jbw@bigbird.bu.edu (Joe Wells)
3758 (defun gnus-summary-prepare-threads (threads level &optional not-child)
3759   "Prepare Summary buffer from THREADS and indentation LEVEL.  
3760 THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'  
3761 or a straight list of headers."
3762   (let ((old-subject "")
3763         thread header number subject clevel)
3764     (while threads
3765       (setq thread (car threads))
3766       (setq threads (cdr threads))
3767       ;; If `thread' is a cons, hierarchical threads are used.  If not,
3768       ;; `thread' is the header.
3769       (if (consp thread)
3770           (setq header (car thread))
3771         (setq header thread))
3772       (if (stringp header)
3773           ;; The header is a dummy root.
3774           (progn
3775             (cond ((eq gnus-summary-make-false-root 'dummy)
3776                    ;; We output a dummy root.
3777                    (gnus-summary-insert-dummy-line 
3778                     nil header (header-number (car (car (cdr thread)))))
3779                    (setq clevel 1))
3780                   ((eq gnus-summary-make-false-root 'adopt)
3781                    ;; We let the first article adopt the rest.
3782                    (gnus-summary-prepare-threads (list (car (cdr thread))) 0)
3783                    (setq thread (cdr (cdr thread)))
3784                    (while thread
3785                      (gnus-summary-prepare-threads (list (car thread)) 1 t)
3786                      (setq thread (cdr thread))))
3787                   (t
3788                    ;; We do not make a root for the gathered
3789                    ;; sub-threads at all.  
3790                    (setq clevel 0)))
3791             ;; Print the sub-threads.
3792             (and (consp thread)
3793                  (cdr thread)
3794                  (gnus-summary-prepare-threads
3795                   (cdr thread) clevel)))
3796         ;; The header is a real article.
3797         (setq number (header-number header))
3798         (setq subject (header-subject header))
3799         (gnus-summary-insert-line
3800          nil header level nil 
3801          (cond ((memq number gnus-newsgroup-marked) ?-)
3802                ((memq number gnus-newsgroup-interesting) ?I)
3803                ((memq number gnus-newsgroup-unreads) ? )
3804                (t ?D))
3805          (memq number gnus-newsgroup-replied)
3806          (memq number gnus-newsgroup-expirable)
3807          (or (= level 0)
3808              (and gnus-thread-ignore-subject
3809                   (not (string= (gnus-simplify-subject-re old-subject)
3810                                 (gnus-simplify-subject-re subject)))))
3811          not-child)
3812         (setq old-subject subject)
3813         ;; Recursively print subthreads.
3814         (and (consp thread)
3815              (cdr thread)
3816              (gnus-summary-prepare-threads
3817               (cdr thread) (1+ level)))))))
3818
3819 (defun gnus-select-newsgroup (group &optional show-all)
3820   "Select newsgroup GROUP.
3821 If SHOW-ALL is non-nil, all articles in the group are selected."
3822   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
3823          (real-group (gnus-group-real-name group))
3824          (info (nth 2 entry))
3825          articles header-marks)
3826     (if (eq (car entry) t)
3827         (or (if (nth 4 info) 
3828                 (gnus-activate-foreign-newsgroup info)
3829               (gnus-activate-newsgroup (car info)))
3830             (error "Couldn't request newsgroup %s" group)))
3831     (setq gnus-current-select-method (or (nth 4 info)
3832                                          gnus-select-method))
3833     (gnus-check-news-server (nth 4 info))
3834     (if (not (gnus-request-group group t))
3835         (error "Couldn't request newsgroup %s" group))
3836     (setq gnus-newsgroup-name group)
3837     (setq gnus-newsgroup-unselected nil)
3838     (setq gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
3839     (cond (show-all
3840            ;; Select all active articles.
3841            (setq articles (gnus-uncompress-sequence 
3842                            (gnus-gethash group gnus-active-hashtb))))
3843           (t
3844            ;; Select unread articles only.
3845            (setq articles gnus-newsgroup-unreads)))
3846     ;; Require confirmation if selecting large newsgroup.
3847     (if (not (numberp gnus-large-newsgroup))
3848         nil
3849       (let ((number (length articles))
3850             selected break)
3851         (if (> number gnus-large-newsgroup)
3852             (progn
3853               (condition-case ()
3854                   (let ((input
3855                          (read-string
3856                           (format
3857                            "How many articles from %s (default %d): "
3858                            gnus-newsgroup-name number))))
3859                     (setq selected
3860                           (if (string-equal input "")
3861                               number (string-to-int input))))
3862                 (quit
3863                  (setq selected 0)))
3864               (if (< (abs selected) number)
3865                   (progn
3866                     (cond 
3867                      ((< selected 0) 
3868                       ;; Select the N oldest articles.
3869                       (setq articles (copy-sequence articles))
3870                       (setq break (nthcdr (1- (abs selected)) articles))
3871                       (setq gnus-newsgroup-unselected 
3872                             (gnus-intersection
3873                              (cdr break)
3874                              gnus-newsgroup-unreads))
3875                       (setcdr break nil))
3876                      ((> selected 0)
3877                       ;; Select the N most recent articles.
3878                       (setq gnus-newsgroup-unselected  
3879                             (copy-sequence articles))
3880                       (setq break (nthcdr (- number (1+ selected))
3881                                           gnus-newsgroup-unselected))
3882                       (setq articles (cdr break))
3883                       (setcdr break nil)
3884                       (setq gnus-newsgroup-unselected
3885                             (gnus-intersection
3886                              gnus-newsgroup-unselected
3887                              gnus-newsgroup-unreads)))
3888                      
3889                      (t
3890                       ;; Select no articles.
3891                       (setq gnus-newsgroup-unselected articles)
3892                       (setq articles nil)))))))
3893         ))
3894     (if (not articles)
3895         nil
3896       ;; Create the list of headers from the headers.
3897       (setq gnus-newsgroup-headers 
3898             (if (eq (gnus-retrieve-headers articles gnus-newsgroup-name) 'nov)
3899                 (progn
3900                   (gnus-get-newsgroup-headers-xover articles))
3901               (gnus-get-newsgroup-headers)))
3902       ;; Remove cancelled articles from the list of unread articles.
3903       (setq gnus-newsgroup-unreads
3904             (gnus-intersection gnus-newsgroup-unreads
3905                                (mapcar
3906                                 (lambda (headers)
3907                                   (header-number headers))
3908                                 gnus-newsgroup-headers)))
3909       ;; Ticked articles must be a subset of unread articles.
3910       (if info
3911           (progn
3912             (gnus-adjust-marked-articles info)
3913             (setq gnus-newsgroup-marked (cdr (assq 'tick (nth 3 info))))
3914             (setq gnus-newsgroup-replied (cdr (assq 'reply (nth 3 info))))
3915             (setq gnus-newsgroup-expirable (cdr (assq 'expire (nth 3 info))))
3916             (setq gnus-newsgroup-killed (cdr (assq 'killed (nth 3 info))))
3917             (setq gnus-newsgroup-bookmarks (cdr (assq 'bookmark (nth 3 info))))
3918             (setq gnus-newsgroup-interesting (cdr (assq 'interesting (nth 3 info))))
3919             (setq gnus-newsgroup-processable nil)))
3920       ;; Check whether auto-expire is to be done in this group.
3921       (setq gnus-newsgroup-auto-expire
3922             (and (stringp gnus-auto-expirable-newsgroups)
3923                  (string-match gnus-auto-expirable-newsgroups real-group)))
3924       ;; First and last article in this newsgroup.
3925       (setq gnus-newsgroup-begin
3926             (if gnus-newsgroup-headers
3927                 (header-number (car gnus-newsgroup-headers))
3928               0))
3929       (setq gnus-newsgroup-end
3930             (if gnus-newsgroup-headers
3931                 (header-number (gnus-last-element gnus-newsgroup-headers))
3932               0))
3933       ;; File name of the last saved article.
3934       (setq gnus-newsgroup-last-rmail nil)
3935       (setq gnus-newsgroup-last-mail nil)
3936       (setq gnus-newsgroup-last-folder nil)
3937       (setq gnus-newsgroup-last-file nil)
3938       ;; Reset article pointers etc.
3939       (setq gnus-current-article nil)
3940       (setq gnus-current-headers nil)
3941       (setq gnus-have-all-headers nil)
3942       (setq gnus-last-article nil)
3943       (setq gnus-xref-hashtb nil)
3944       (setq gnus-reffed-article-number -1)
3945       (setq gnus-newsgroup-headers-hashtb-by-number nil)
3946       ;; Update the format specifiers.
3947       (gnus-update-format-specifications)
3948       ;; GROUP is successfully selected.
3949       t)))
3950
3951 (defun gnus-adjust-marked-articles (info)
3952   "Remove all marked articles that are no longer legal."
3953   (let ((marked-lists (nth 3 info))
3954         (active (gnus-gethash (car info) gnus-active-hashtb))
3955         marked m prev)
3956     ;; There are four types of marked articles - ticked, replied,
3957     ;; expirable and interesting.  
3958     (while marked-lists
3959       (setq m (cdr (setq prev (car marked-lists))))
3960       (cond ((or (eq 'tick (car prev)) (eq 'interesting (car prev)))
3961              ;; Make sure that all ticked articles are a subset of the
3962              ;; unread/unselected articles.
3963              (while m
3964                (if (or (memq (car m) gnus-newsgroup-unreads)
3965                        (memq (car m) gnus-newsgroup-unselected))
3966                    (setq prev m)
3967                  (setcdr prev (cdr m)))
3968                (setq m (cdr m))))
3969             ((eq 'bookmark (car prev))
3970              ;; Bookmarks should be a subset of active articles.
3971              (while m
3972                (if (< (car (car m)) (car active))
3973                    (setcdr prev (cdr m))
3974                  (setq prev m))
3975                (setq m (cdr m))))
3976             ((eq 'killed (car prev))
3977              ;; Articles that have been through the kill process are
3978              ;; to be a subset of active articles.
3979              (while (and m (< (cdr (car m)) (car active)))
3980                (setcdr prev (cdr m)))
3981              (if (and m (< (car (car m)) (car active))) 
3982                  (setcar (car m) (car active))))
3983             ((or (eq 'reply (car marked)) (eq 'expire (car marked)))
3984              ;; The replied and expirable articles have to be articles
3985              ;; that are active. 
3986              (while m
3987                (if (< (car m) (car active))
3988                    (setcdr prev (cdr m))
3989                  (setq prev m))
3990                (setq m (cdr m)))))
3991       (setq marked-lists (cdr marked-lists)))
3992     ;; Remove all lists that are empty.
3993     (setq marked-lists (nth 3 info))
3994     (if marked-lists
3995         (progn
3996           (while (= 1 (length (car marked-lists)))
3997             (setq marked-lists (cdr marked-lists)))
3998           (setq m (cdr (setq prev marked-lists)))
3999           (while m
4000             (if (= 1 (length (car m)))
4001                 (setcdr prev (cdr m))
4002               (setq prev m))
4003             (setq m (cdr m)))
4004           (setcar (nthcdr 3 info) marked-lists)))
4005     ;; Finally, if there are no marked lists at all left, and if there
4006     ;; are no elements after the lists in the info list, we just chop
4007     ;; the info list off before the marked lists.
4008     (if (and (null marked-lists) (not (nthcdr 4 info)))
4009         (setcdr (nthcdr 2 info) nil)))
4010   info)
4011
4012 (defun gnus-set-marked-articles 
4013   (info ticked replied expirable killed interesting bookmark) 
4014   "Enter the various lists of marked articles into the newsgroup info list."
4015   (let (newmarked)
4016     (if ticked
4017         (setq newmarked (cons (cons 'tick ticked) nil)))
4018     (if replied 
4019         (setq newmarked (cons (cons 'reply replied) newmarked)))
4020     (if expirable 
4021         (setq newmarked (cons (cons 'expire expirable) newmarked)))
4022     (if killed
4023         (setq newmarked (cons (cons 'killed killed) newmarked)))
4024     (if interesting
4025         (setq newmarked (cons (cons 'interesting interesting) newmarked)))
4026     (if bookmark
4027         (setq newmarked (cons (cons 'bookmark bookmark) newmarked)))
4028     (if (nthcdr 3 info)
4029         (if newmarked
4030             (setcar (nthcdr 3 info) newmarked)
4031           (if (not (nthcdr 4 info))
4032               (setcdr (nthcdr 2 info) nil)
4033             (setcar (nthcdr 3 info) nil)))
4034       (if newmarked
4035           (setcdr (nthcdr 2 info) (cons newmarked nil))))))
4036
4037 (defun gnus-set-mode-line (where)
4038   "This function sets the mode line of the Article or Summary buffers.
4039 If WHERE is `summary', the summary mode line format will be used."
4040   (if (memq where gnus-updated-mode-lines)
4041       (let (mode-string)
4042         (save-excursion
4043           (set-buffer gnus-summary-buffer)
4044           (let* ((mformat (if (eq where 'article) 
4045                               gnus-article-mode-line-format-spec
4046                             gnus-summary-mode-line-format-spec))
4047                  (group-name gnus-newsgroup-name)
4048                  (article-number (or gnus-current-article 0))
4049                  (unread (length gnus-newsgroup-unreads))
4050                  (unselected (length gnus-newsgroup-unselected))
4051                  (unread-and-unselected
4052                   (cond ((and (zerop unread) (zerop unselected)) "")
4053                         ((zerop unselected) (format "{%d more}" unread))
4054                         (t (format "{%d(+%d) more}" unread unselected))))
4055                  (subject
4056                   (if gnus-current-headers
4057                       (header-subject gnus-current-headers) ""))
4058                  (max-len (if (eq where 'summary) 45 52)))
4059             (setq mode-string (eval mformat))
4060             (if (> (length mode-string) max-len) 
4061                 (setq mode-string 
4062                       (concat (substring mode-string 0 (- max-len 4)) "...")))
4063             (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
4064         (setq mode-line-buffer-identification mode-string)
4065         (set-buffer-modified-p t))))
4066
4067 (defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
4068   "Go through the HEADERS list and add all Xrefs to a hash table.
4069 The resulting hash table is returned, or nil if no Xrefs were found."
4070   (let ((prefix (if (and 
4071                      (string-match gnus-foreign-group-prefix from-newsgroup)
4072                      (not (eq 'nnvirtual (car gnus-current-select-method))))
4073                     gnus-foreign-group-prefix))
4074         (xref-hashtb (make-vector 63 0))
4075         start group entry number xrefs header)
4076     (while headers
4077       (setq header (car headers))
4078       (if (and (setq xrefs (header-xref header))
4079                (not (memq (header-number header) unreads)))
4080           (progn
4081             (setq start 0)
4082             (while (string-match "\\([^ :]+\\):\\([0-9]+\\)" xrefs start)
4083               (setq start (match-end 0))
4084               (setq group (concat prefix (substring xrefs (match-beginning 1) 
4085                                             (match-end 1))))
4086               (setq number 
4087                     (string-to-int (substring xrefs (match-beginning 2) 
4088                                               (match-end 2))))
4089               (if (setq entry (gnus-gethash group xref-hashtb))
4090                   (setcdr entry (cons number (cdr entry)))
4091                 (gnus-sethash group (cons number nil) xref-hashtb)))))
4092       (setq headers (cdr headers)))
4093     (if start xref-hashtb nil)))
4094
4095 (defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
4096   "Look through all the headers and mark the Xrefs as read."
4097   (let (name entry read info xref-hashtb idlist active num range)
4098     (set-buffer gnus-group-buffer)
4099     (if (setq xref-hashtb 
4100               (gnus-create-xref-hashtb from-newsgroup headers unreads))
4101         (mapatoms 
4102          (lambda (group)
4103            (if (string= from-newsgroup (setq name (symbol-name group)))
4104                ()
4105              (setq idlist (symbol-value group))
4106              ;; Dead groups are not updated.
4107              (if (and (setq entry (gnus-gethash name gnus-newsrc-hashtb))
4108                       ;; Only do the xrefs if the group has the same
4109                       ;; select method as the group we have just read.
4110                       (or (and (not (nth 4 (setq info (nth 2 entry))))
4111                                (eq gnus-current-select-method
4112                                    gnus-select-method))
4113                           (eq (car gnus-current-select-method) 'nnvirtual)
4114                           (equal (nth 4 info) 
4115                                  gnus-current-select-method)))
4116                  (progn
4117                    (setq num 0)
4118                    ;; Set the new list of read articles in this group.
4119                    (setcar (nthcdr 2 info)
4120                            (setq range
4121                                  (gnus-add-to-range 
4122                                   (nth 2 info) 
4123                                   (setq idlist (sort idlist '<)))))
4124                    ;; Then we have to re-compute how many unread
4125                    ;; articles there are in this group.
4126                    (if (setq active (gnus-gethash name gnus-active-hashtb))
4127                        (progn
4128                          (if (atom (car range))
4129                              (progn
4130                                (setq num (- (cdr active) (- (1+ (cdr range)) 
4131                                                             (car range))))
4132                                (if (< num 0) (setq num 0)))
4133                            (while range
4134                              (setq num (+ num (- (1+ (cdr (car range))) 
4135                                                  (car (car range)))))
4136                              (setq range (cdr range)))
4137                            (setq num (- (cdr active) num)))
4138                          ;; Update the number of unread articles.
4139                          (setcar entry num)
4140                          ;; Update the Newsgroup buffer.
4141                          (gnus-group-update-group name t)))))))
4142          xref-hashtb))))
4143
4144 (defsubst gnus-header-value ()
4145   (buffer-substring (match-end 0) (save-excursion (end-of-line) (point))))
4146
4147 ;; Felix Lee function with jwz rewrites (and some lmi rewrites to boot).
4148 ;; Goes through the newsgroups headers and returns a list of arrays:
4149 (defun gnus-get-newsgroup-headers ()
4150   (setq gnus-article-internal-prepare-hook nil)
4151   (save-excursion
4152     (let ((cur nntp-server-buffer)
4153           (dependencies (gnus-make-hashtable (length gnus-newsgroup-unreads)))
4154           headers header subject from char c article unreads in-reply-to
4155           references end-header id dep ref end)
4156       (set-buffer nntp-server-buffer)
4157       (goto-char 1)
4158       (while (re-search-forward "^[23][0-9]+ \\([0-9]+\\)" nil t)
4159         (setq from nil
4160               subject nil
4161               in-reply-to nil
4162               references nil
4163               ref nil
4164               header (make-vector 9 nil)
4165               c (following-char))
4166         (goto-char (match-beginning 1))
4167         (header-set-number 
4168          header (setq article (read cur)))
4169         (setq end-header (save-excursion (search-forward "\n.\n" nil t)))
4170         (while (re-search-forward "^\\(from\\|subject\\|message-id\\|date\\|lines\\|xref\\|references\\|in-reply-to\\): "
4171                                    end-header t)
4172           (beginning-of-line)
4173           (setq char (downcase (following-char))) 
4174           (cond
4175            ((eq char ?s)
4176             (header-set-subject header 
4177                                 (setq subject (gnus-header-value))))
4178            ((eq char ?f)
4179             (header-set-from header (setq from (gnus-header-value))))
4180            ((eq char ?x)
4181             (header-set-xref header (gnus-header-value)))
4182            ((eq char ?l)
4183             (header-set-lines header 
4184                                    (string-to-int (gnus-header-value))))
4185            ((eq char ?d)
4186             (header-set-date header (gnus-header-value)))
4187            ((eq char ?m)
4188             (header-set-id header (setq id (gnus-header-value))))
4189            ((eq char ?r)
4190             (setq references (gnus-header-value))
4191             (setq end (match-end 0))
4192             (save-excursion
4193               (setq ref 
4194                     (buffer-substring
4195                      (progn 
4196                        (end-of-line)
4197                        (search-backward ">" end t)
4198                        (1+ (point)))
4199                      (progn
4200                        (search-backward "<" end t)
4201                        (point))))))
4202            ((eq char ?i)
4203             (setq in-reply-to (gnus-header-value))))
4204           (forward-line 1))
4205         (if references
4206             (header-set-references header references)
4207           (and in-reply-to
4208                (string-match "<[^>]+>" in-reply-to)
4209                (header-set-references 
4210                 header
4211                 (substring in-reply-to (match-beginning 0)
4212                            (match-end 0)))))
4213         (or subject (header-set-subject header "(none)"))
4214         (or from (header-set-from header "(nobody)"))
4215         ;; We build the thread tree.
4216         (if (boundp (setq dep (intern id dependencies)))
4217             (setcar (symbol-value dep) header)
4218           (set dep (list header)))
4219         (if (boundp (setq dep (intern (or ref "none") dependencies)))
4220             (setcdr (symbol-value dep) 
4221                     (cons header (cdr (symbol-value dep))))
4222           (set dep (list nil header)))
4223         (setq headers (cons header headers))
4224         (forward-line -1)
4225         (search-forward "\n.\n" nil t))
4226       (setq gnus-newsgroup-dependencies dependencies)
4227       (nreverse headers))))
4228
4229 ;; The following macros and functions were written by Felix Lee
4230 ;; <flee@cse.psu.edu>. 
4231
4232 ;; This is almost 4x faster than (string-to-int (buffer-substring ... ))
4233 ;; primarily because of garbage collection.  -jwz
4234 (defmacro gnus-read-integer (&optional point move-p)
4235   (` ((, (if move-p 'progn 'save-excursion))
4236       (,@ (if point (list (list 'goto-char point))))
4237       (if (and (<= (following-char) ?9)
4238                (>= (following-char) ?0))
4239           (read (current-buffer))
4240         0))))
4241
4242 (defmacro gnus-nov-skip-field ()
4243   '(search-forward "\t" eol 'end))
4244
4245 (defmacro gnus-nov-field ()
4246   '(buffer-substring
4247     (point)
4248     (progn (gnus-nov-skip-field) (1- (point)))))
4249
4250 ;; Goes through the xover lines and returns a list of vectors
4251 (defun gnus-get-newsgroup-headers-xover (sequence)
4252   "Parse the news overview data in the server buffer, and return a
4253 list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
4254   ;; Get the Xref when the users reads the articles since most/some
4255   ;; NNTP servers do not include Xrefs when using XOVER.
4256   (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
4257   (save-excursion
4258     (set-buffer nntp-server-buffer)
4259     (let ((cur (current-buffer))
4260           (dependencies (gnus-make-hashtable (length gnus-newsgroup-unreads)))
4261           number header headers eol header id ref dep)
4262       (goto-char (point-min))
4263       (while (and sequence (not (eobp)))
4264         (setq number (read cur))
4265         (while (and sequence (< (car sequence) number))
4266           (setq sequence (cdr sequence)))
4267         (and sequence 
4268              (eq number (car sequence))
4269              (progn
4270                (setq sequence (cdr sequence))
4271                (save-excursion
4272                  (end-of-line)
4273                  (setq eol (point)))
4274                (forward-char)
4275                ;; overview: [num subject from date id refs chars lines misc]
4276                (setq header
4277                      (vector 
4278                       number           ; number
4279                       (gnus-nov-field) ; subject
4280                       (gnus-nov-field) ; from
4281                       (gnus-nov-field) ; date
4282                       (setq id (gnus-nov-field)) ; id
4283                       (progn
4284                         (save-excursion
4285                           (let ((beg (point)))
4286                           (search-forward "\t" eol)
4287                           (if (search-backward ">" beg t)
4288                               (setq ref (buffer-substring 
4289                                          (1+ (point))
4290                                          (progn
4291                                            (search-backward "<" beg t)
4292                                            (point))))
4293                             (setq ref nil))))
4294                         (gnus-nov-field)) ; refs
4295                       (read cur)       ; chars
4296                       (read cur)       ; lines
4297                       (if (/= (following-char) ?\t)
4298                           nil
4299                         (forward-char 1)
4300                         (gnus-nov-field)) ; misc
4301                       ))
4302                ;; We build the thread tree.
4303                (if (boundp (setq dep (intern id dependencies)))
4304                    (setcar (symbol-value dep) header)
4305                  (set dep (list header)))
4306                (if (boundp (setq dep (intern (or ref "none") dependencies)))
4307                    (setcdr (symbol-value dep) 
4308                            (cons header (cdr (symbol-value dep))))
4309                  (set dep (list nil header)))
4310                (setq headers (cons header headers))))
4311         (forward-line 1))
4312       (setq headers (nreverse headers))
4313       (setq gnus-newsgroup-dependencies dependencies)
4314       headers)))
4315
4316 (defun gnus-article-get-xrefs ()
4317   "Fill in the Xref value in `gnus-current-headers', if necessary.
4318 This is meant to be called in `gnus-article-internal-prepare-hook'."
4319   (or (not gnus-use-cross-reference)
4320       (let ((case-fold-search t)
4321             xref)
4322         (save-restriction
4323           (gnus-narrow-to-headers)
4324           (goto-char (point-min))
4325           (if (or (and (eq (downcase (following-char)) ?x)
4326                        (looking-at "Xref:"))
4327                   (search-forward "\nXref:" nil t))
4328               (progn
4329                 (goto-char (1+ (match-end 0)))
4330                 (setq xref (buffer-substring (point) 
4331                                              (progn (end-of-line) (point))))
4332                 (save-excursion
4333                   (set-buffer gnus-summary-buffer)
4334                   (header-set-xref gnus-current-headers xref))))))))
4335
4336 (defalias 'gnus-find-header-by-number 'gnus-get-header-by-number)
4337 (make-obsolete 'gnus-find-header-by-number 'gnus-get-header-by-number)
4338
4339 ;; Return a header specified by a NUMBER.
4340 (defun gnus-get-header-by-number (number)
4341   (or gnus-newsgroup-headers-hashtb-by-number
4342       (gnus-make-headers-hashtable-by-number))
4343   (gnus-gethash (int-to-string number)
4344                 gnus-newsgroup-headers-hashtb-by-number))
4345
4346 (defun gnus-make-headers-hashtable-by-number ()
4347   "Make hashtable for the variable gnus-newsgroup-headers by number."
4348   (let ((header nil)
4349         (headers gnus-newsgroup-headers))
4350     (setq gnus-newsgroup-headers-hashtb-by-number
4351           (gnus-make-hashtable (length headers)))
4352     (while headers
4353       (setq header (car headers))
4354       (gnus-sethash (int-to-string (header-number header))
4355                     header gnus-newsgroup-headers-hashtb-by-number)
4356       (setq headers (cdr headers))
4357       )))
4358
4359 (defun gnus-more-header-backward ()
4360   "Find new header backward."
4361   (let ((first (car (gnus-gethash gnus-newsgroup-name gnus-active-hashtb)))
4362         (artnum gnus-newsgroup-begin)
4363         (header nil))
4364     (while (and (not header)
4365                 (> artnum first))
4366       (setq artnum (1- artnum))
4367       (setq header (gnus-read-header artnum)))
4368     header))
4369
4370 (defun gnus-more-header-forward ()
4371   "Find new header forward."
4372   (let ((last (cdr (gnus-gethash gnus-newsgroup-name gnus-active-hashtb)))
4373         (artnum gnus-newsgroup-end)
4374         (header nil))
4375     (while (and (not header)
4376                 (< artnum last))
4377       (setq artnum (1+ artnum))
4378       (setq header (gnus-read-header artnum)))
4379     header))
4380
4381 (defun gnus-extend-newsgroup (header &optional backward)
4382   "Extend newsgroup selection with HEADER.
4383 Optional argument BACKWARD means extend toward backward."
4384   (if header
4385       (let ((artnum (header-number header)))
4386         (setq gnus-newsgroup-headers
4387               (if backward
4388                   (cons header gnus-newsgroup-headers)
4389                 (nconc gnus-newsgroup-headers (list header))))
4390         (setq gnus-newsgroup-unselected
4391               (delq artnum gnus-newsgroup-unselected))
4392         (setq gnus-newsgroup-begin (min gnus-newsgroup-begin artnum))
4393         (setq gnus-newsgroup-end (max gnus-newsgroup-end artnum)))))
4394
4395
4396 (defun gnus-summary-search-group (&optional backward use-level)
4397   "Search for next unread newsgroup.
4398 If optional argument BACKWARD is non-nil, search backward instead."
4399   (save-excursion
4400     (set-buffer gnus-group-buffer)
4401     (save-excursion
4402       ;; We don't want to alter current point of Group mode buffer.
4403       (if (gnus-group-search-forward 
4404            backward nil 
4405            (if use-level (gnus-group-group-level) nil))
4406           (gnus-group-group-name))
4407       )))
4408
4409 (defun gnus-summary-search-subject (&optional backward unread subject)
4410   "Search for article forward.
4411 If BACKWARD is non-nil, search backward.
4412 If UNREAD is non-nil, only unread articles are selected.
4413 If SUBJECT is non-nil, the article which has the same subject will be
4414 searched for." 
4415   (let ((func
4416          (if backward
4417              (function re-search-backward) (function re-search-forward)))
4418         ;; We have to take care of hidden lines.
4419         (regexp 
4420          (if subject 
4421              (format "%s %s \\([-0-9 ]+\\) [0-9]+[\n\r]"
4422                      (regexp-quote (gnus-simplify-subject-re subject))
4423                      (if unread " " "."))
4424            ;; Bug by Daniel Quinlan <quinlan@best.com>.
4425            (if unread "^[- ]" "^."))))
4426     (if backward
4427         (beginning-of-line)
4428       (end-of-line))
4429     (prog1
4430         (if (funcall func regexp nil t)
4431             (progn
4432               (goto-char (match-beginning 0))
4433               (gnus-summary-article-number))
4434           nil)
4435       ;; Adjust cursor point.
4436       (gnus-summary-position-cursor))))
4437
4438 (defun gnus-summary-search-forward (&optional unread subject backward)
4439   "Search for article forward.
4440 If UNREAD is non-nil, only unread articles are selected.
4441 If SUBJECT is non-nil, the article which has the same subject will be
4442 searched for. 
4443 If BACKWARD is non-nil, the search will be performed backwards instead."
4444   (gnus-summary-search-subject backward unread subject))
4445
4446 (defun gnus-summary-search-backward (&optional unread subject)
4447   "Search for article backward.
4448 If 1st optional argument UNREAD is non-nil, only unread article is selected.
4449 If 2nd optional argument SUBJECT is non-nil, the article which has
4450 the same subject will be searched for."
4451   (gnus-summary-search-forward unread subject t))
4452
4453 (defun gnus-summary-article-number ()
4454   "The article number of the article on the current line.
4455 If there isn's an article number here, then we return the current
4456 article number."
4457   (save-excursion
4458     (beginning-of-line)
4459     (if (re-search-forward " [-0-9]+ [0-9]+[\n\r]" nil t)
4460         (progn
4461           ;; jwz: this is faster than string-to-int/buffer-substring
4462           (goto-char (match-beginning 0))
4463           (read (current-buffer)))
4464         ;; We return the current if we couldn't find anything.
4465         gnus-current-article)))
4466
4467 (defun gnus-summary-thread-level ()
4468   "The thread level of the article on the current line."
4469   (save-excursion
4470     (beginning-of-line)
4471     (if (re-search-forward " [0-9]+[\n\r]" nil t)
4472         (progn
4473           (goto-char (match-beginning 0))
4474           (read (current-buffer)))
4475       ;; We return zero if we couldn't find anything.
4476       0)))
4477
4478 (defun gnus-summary-article-mark ()
4479   "The mark on the current line."
4480   (save-excursion
4481     (beginning-of-line)
4482     (if (re-search-forward ". [-0-9]+ [0-9]+[\n\r]" nil t)
4483         (char-after (match-beginning 0)))))
4484
4485 (defun gnus-summary-subject-string ()
4486   "Return current subject string or nil if nothing."
4487   (save-excursion
4488     (beginning-of-line)
4489     (if (re-search-forward ". [-0-9]+ [0-9]+[\n\r]" nil t)
4490         (let ((beg (previous-property-change (match-beginning 0)))
4491               (end (1- (match-beginning 0)))
4492               (buffer-read-only nil))
4493           (set-text-properties beg end nil)
4494           (prog1
4495               (buffer-substring beg end)
4496             (set-text-properties beg end '(invisible t))))
4497       nil)))
4498
4499 (defun gnus-summary-recenter ()
4500   "Center point in Summary window."
4501   ;; Scroll window so as to cursor comes center of Summary window
4502   ;;  only when article is displayed.
4503   ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
4504   ;; Recenter only when requested.
4505   ;; Subbested by popovich@park.cs.columbia.edu
4506   (and gnus-auto-center-summary
4507        (get-buffer-window gnus-article-buffer)
4508        (< (/ (- (window-height) 1) 2)
4509           (count-lines (point) (point-max)))
4510        (recenter (/ (- (window-height) 2) 2))))
4511
4512 (defun gnus-summary-jump-to-group (newsgroup)
4513   "Move point to NEWSGROUP in Group mode buffer."
4514   ;; Keep update point of Group mode buffer if visible.
4515   (if (eq (current-buffer)
4516           (get-buffer gnus-group-buffer))
4517       (save-window-excursion
4518         ;; Take care of tree window mode.
4519         (if (get-buffer-window gnus-group-buffer)
4520             (pop-to-buffer gnus-group-buffer))
4521         (gnus-group-jump-to-group newsgroup))
4522     (save-excursion
4523       ;; Take care of tree window mode.
4524       (if (get-buffer-window gnus-group-buffer)
4525           (pop-to-buffer gnus-group-buffer)
4526         (set-buffer gnus-group-buffer))
4527       (gnus-group-jump-to-group newsgroup))))
4528
4529 ;; This function returns a list of article numbers based on the
4530 ;; difference between the ranges of read articles in this group and
4531 ;; the range of active articles.
4532 (defun gnus-list-of-unread-articles (group)
4533   (let* ((read (nth 2 (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
4534          (active (gnus-gethash group gnus-active-hashtb))
4535          (last (cdr active))
4536          unread first nlast unread)
4537     ;; If none are read, then all are unread. 
4538     (if (not read)
4539           (setq first (car active))
4540       ;; If the range of read articles is a single range, then the
4541       ;; first unread article is the article after the last read
4542       ;; article. Sounds logical, doesn't it?
4543       (if (atom (car read))
4544           (setq first (1+ (cdr read)))
4545         ;; `read' is a list of ranges.
4546         (while read
4547           (if first 
4548               (while (< first nlast)
4549                 (setq unread (cons first unread))
4550                 (setq first (1+ first))))
4551           (setq first (1+ (cdr (car read))))
4552           (setq nlast (car (car (cdr read))))
4553           (setq read (cdr read)))))
4554     ;; And add the last unread articles.
4555     (while (<= first last)
4556       (setq unread (cons first unread))
4557       (setq first (1+ first)))
4558     ;; Return the list of unread articles.
4559     (nreverse unread)))
4560
4561
4562 ;; Gnus Summary mode commands.
4563
4564 ;; Various summary commands
4565
4566 (defun gnus-summary-catchup-and-exit (all &optional quietly)
4567   "Mark all articles not marked as unread in this newsgroup as read, then exit.
4568 If prefix argument ALL is non-nil, all articles are marked as read."
4569   (interactive "P")
4570   (if (or quietly
4571           (not gnus-interactive-catchup) ;Without confirmation?
4572           gnus-expert-user
4573           (y-or-n-p
4574            (if all
4575                "Do you really want to mark everything as read? "
4576              "Delete all articles not marked as unread? ")))
4577       (let ((unmarked
4578              (gnus-set-difference gnus-newsgroup-unreads
4579                                   (if (not all) gnus-newsgroup-marked))))
4580         (message "")                    ;Erase "Yes or No" question.
4581         (while unmarked
4582           (gnus-mark-article-as-read (car unmarked))
4583           (setq unmarked (cdr unmarked)))
4584         ;; Select next newsgroup or exit.
4585         (cond ((eq gnus-auto-select-next 'quietly)
4586                ;; Select next newsgroup quietly.
4587                (gnus-summary-next-group nil))
4588               (t
4589                (gnus-summary-exit)))
4590         )))
4591
4592 (defun gnus-summary-catchup-all-and-exit (&optional quietly)
4593   "Mark all articles in this newsgroup as read, and then exit."
4594   (interactive)
4595   (gnus-summary-catchup-and-exit t quietly))
4596
4597 (defun gnus-summary-toggle-truncation (arg)
4598   "Toggle truncation of summary lines.
4599 With arg, turn line truncation on iff arg is positive."
4600   (interactive "P")
4601   (setq truncate-lines
4602         (if (null arg) (not truncate-lines)
4603           (> (prefix-numeric-value arg) 0)))
4604   (redraw-display))
4605
4606 (defun gnus-summary-reselect-current-group (show-all)
4607   "Once exit and then reselect the current newsgroup.
4608 Prefix argument SHOW-ALL means to select all articles."
4609   (interactive "P")
4610   (let ((current-subject (gnus-summary-article-number)))
4611     (gnus-summary-exit t)
4612     ;; We have to adjust the point of Group mode buffer because the
4613     ;; current point was moved to the next unread newsgroup by
4614     ;; exiting.
4615     (gnus-summary-jump-to-group gnus-newsgroup-name)
4616     (gnus-group-read-group show-all t)
4617     (gnus-summary-goto-subject current-subject)
4618     ))
4619
4620 (defun gnus-summary-rescan-group (all)
4621   "Exit the newsgroup, ask for new articles, and select the newsgroup."
4622   (interactive "P")
4623   (gnus-summary-exit t)
4624   (gnus-summary-jump-to-group gnus-newsgroup-name)
4625   (save-excursion
4626     (set-buffer gnus-group-buffer)
4627     (gnus-group-get-new-news-this-group 1))
4628   (gnus-summary-jump-to-group gnus-newsgroup-name)
4629   (gnus-group-read-group all))
4630
4631 (defun gnus-summary-exit (&optional temporary)
4632   "Exit reading current newsgroup, and then return to group selection mode.
4633 gnus-exit-group-hook is called with no arguments if that value is non-nil."
4634   (interactive)
4635   (let ((group gnus-newsgroup-name)
4636         (mode major-mode)
4637         (buf (current-buffer)))
4638     (let ((updated nil)
4639           (headers gnus-newsgroup-headers)
4640           (unreads gnus-newsgroup-unreads)
4641           (unselected (setq gnus-newsgroup-unselected
4642                             (sort gnus-newsgroup-unselected '<)))
4643           (ticked gnus-newsgroup-marked))
4644       ;; Important internal variables are saved, so we can reenter
4645       ;; the Summary buffer even if the hook changes them.
4646       (run-hooks 'gnus-exit-group-hook)
4647       (gnus-update-read-articles group unreads unselected ticked
4648                                  t gnus-newsgroup-replied
4649                                  gnus-newsgroup-expirable
4650                                  gnus-newsgroup-killed
4651                                  gnus-newsgroup-interesting
4652                                  gnus-newsgroup-bookmarks)
4653       ;; T means ignore unsubscribed newsgroups.
4654       (if gnus-use-cross-reference
4655           (gnus-mark-xrefs-as-read group headers unreads))
4656       ;; Save the kill buffer (if it exists)
4657       (gnus-kill-save-kill-buffer)
4658       ;; Do not switch windows but change the buffer to work.
4659       (set-buffer gnus-group-buffer)
4660       (gnus-group-update-group group))
4661     ;; Make sure where I was, and go to next newsgroup.
4662     (gnus-group-jump-to-group group)
4663     (gnus-group-next-unread-group 1)
4664     (if temporary
4665         ;; If exiting temporary, caller should adjust Group mode
4666         ;; buffer point by itself.
4667         nil                             ;Nothing to do.
4668       ;; Return to Group mode buffer.
4669       (if (and (get-buffer buf) 
4670                (eq mode 'gnus-summary-mode))
4671           (kill-buffer buf))
4672       (if (get-buffer gnus-article-buffer)
4673           (bury-buffer gnus-article-buffer))
4674       (setq gnus-current-select-method gnus-select-method)
4675       (gnus-configure-windows 'newsgroups t)
4676       (pop-to-buffer gnus-group-buffer))))
4677
4678 (defun gnus-summary-quit ()
4679   "Quit reading current newsgroup without updating read article info."
4680   (interactive)
4681   (if (y-or-n-p "Do you really wanna quit reading this group? ")
4682       (progn
4683         (message "")                    ;Erase "Yes or No" question.
4684         ;; Return to Group selection mode.
4685         (if (get-buffer gnus-summary-buffer)
4686             (bury-buffer gnus-summary-buffer))
4687         (if (get-buffer gnus-article-buffer)
4688             (bury-buffer gnus-article-buffer))
4689         (gnus-configure-windows 'newsgroups)
4690         (pop-to-buffer gnus-group-buffer)
4691         (gnus-group-jump-to-group gnus-newsgroup-name) ;Make sure where I was.
4692         (gnus-group-next-group 1))))
4693
4694 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
4695 (defun gnus-summary-describe-group ()
4696   "Describe the current newsgroup."
4697   (interactive)
4698   (gnus-group-describe-group gnus-newsgroup-name))
4699
4700 (defun gnus-summary-describe-briefly ()
4701   "Describe Summary mode commands briefly."
4702   (interactive)
4703   (message
4704     (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")))
4705
4706 ;; Walking around Group mode buffer from Summary mode.
4707
4708 (defun gnus-summary-next-group (no-article &optional group)
4709   "Exit current newsgroup and then select next unread newsgroup.
4710 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
4711   (interactive "P")
4712   ;; Make sure Group mode buffer point is on current newsgroup.
4713   (gnus-summary-jump-to-group gnus-newsgroup-name)
4714   (let ((group (or group (gnus-summary-search-group)))
4715         (buf gnus-summary-buffer))
4716     (if (null group)
4717         (progn
4718           (message "Exiting %s..." gnus-newsgroup-name)  
4719           (gnus-summary-exit)
4720           (message ""))
4721       (message "Selecting %s..." group)
4722       (gnus-summary-exit t)             ;Exit Summary mode temporary.
4723       ;; We are now in Group mode buffer.
4724       ;; Make sure Group mode buffer point is on GROUP.
4725       (gnus-summary-jump-to-group group)
4726       (gnus-summary-read-group group nil no-article buf)
4727       (or (eq (current-buffer)
4728               (get-buffer gnus-summary-buffer))
4729           (eq gnus-auto-select-next t)
4730           ;; Expected newsgroup has nothing to read since the articles
4731           ;; are marked as read by cross-referencing. So, try next
4732           ;; newsgroup. (Make sure we are in Group mode buffer now.)
4733           (and (eq (current-buffer)
4734                    (get-buffer gnus-group-buffer))
4735                (gnus-group-group-name)
4736                (gnus-summary-read-group
4737                 (gnus-group-group-name) nil no-article buf))))))
4738
4739 (defun gnus-summary-prev-group (no-article)
4740   "Exit current newsgroup and then select previous unread newsgroup.
4741 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
4742   (interactive "P")
4743   ;; Make sure Group mode buffer point is on current newsgroup.
4744   (gnus-summary-jump-to-group gnus-newsgroup-name)
4745   (let ((group (gnus-summary-search-group t)))
4746     (if (null group)
4747         (progn
4748           (message "Exiting %s..." gnus-newsgroup-name)  
4749           (gnus-summary-exit)
4750           (message ""))
4751       (message "Selecting %s..." group)
4752       (gnus-summary-exit t)             ;Exit Summary mode temporary.
4753       ;; We are now in Group mode buffer.
4754       ;; We have to adjust point of Group mode buffer because current
4755       ;; point is moved to next unread newsgroup by exiting.
4756       (gnus-summary-jump-to-group group)
4757       (gnus-summary-read-group group nil no-article)
4758       (or (eq (current-buffer)
4759               (get-buffer gnus-summary-buffer))
4760           (eq gnus-auto-select-next t)
4761           ;; Expected newsgroup has nothing to read since the articles
4762           ;; are marked as read by cross-referencing. So, try next
4763           ;; newsgroup. (Make sure we are in Group mode buffer now.)
4764           (and (eq (current-buffer)
4765                    (get-buffer gnus-group-buffer))
4766                (gnus-summary-search-group t)
4767                (gnus-summary-read-group
4768                 (gnus-summary-search-group t) nil no-article))
4769           )
4770       )))
4771
4772 ;; Walking around summary lines.
4773
4774 (defun gnus-summary-next-subject (n &optional unread)
4775   "Go to next N'th summary line.
4776 If N is negative, go to the previous N'th subject line.
4777 If UNREAD is non-nil, only unread articles are selected.
4778 The difference between N and the actual number of steps taken is
4779 returned."
4780   (interactive "p")
4781   (let ((backward (< n 0))
4782         (n (abs n)))
4783   (while (and (> n 0)
4784               (gnus-summary-search-forward unread nil backward))
4785     (setq n (1- n)))
4786   (gnus-summary-recenter)
4787   (if (/= 0 n) (message "No more%s articles" (if unread " unread" "")))
4788 ;  (gnus-summary-position-cursor)
4789  n))
4790
4791 (defun gnus-summary-next-unread-subject (n)
4792   "Go to next N'th unread summary line."
4793   (interactive "p")
4794   (gnus-summary-next-subject n t))
4795
4796 (defun gnus-summary-prev-subject (n &optional unread)
4797   "Go to previous N'th summary line.
4798 If optional argument UNREAD is non-nil, only unread article is selected."
4799   (interactive "p")
4800   (gnus-summary-next-subject (- n) unread))
4801
4802 (defun gnus-summary-prev-unread-subject (n)
4803   "Go to previous N'th unread summary line."
4804   (interactive "p")
4805   (gnus-summary-next-subject (- n) t))
4806
4807 (defun gnus-summary-goto-subject (article)
4808   "Go the subject line of ARTICLE."
4809   (interactive
4810    (list
4811     (string-to-int
4812      (completing-read "Article number: "
4813                       (mapcar
4814                        (lambda (headers)
4815                          (list
4816                           (int-to-string (header-number headers))))
4817                        gnus-newsgroup-headers)
4818                       nil 'require-match))))
4819     (if (or (eq article (gnus-summary-article-number))
4820             (let ((org (point)))
4821               (goto-char 1)
4822               (if (re-search-forward (format "[^Z] %d [0-9]+[\n\r]" article)
4823                                      nil t)
4824                   (goto-char (match-beginning 0))
4825                 (goto-char org)
4826                 nil)))
4827         (progn
4828           (gnus-summary-position-cursor)
4829           article)))
4830
4831 ;; Walking around summary lines with displaying articles.
4832
4833 (defun gnus-summary-expand-window ()
4834   "Expand Summary window to show headers full window."
4835   (interactive)
4836   (gnus-configure-windows 'summary)
4837   (pop-to-buffer gnus-summary-buffer))
4838
4839 (defun gnus-summary-display-article (article &optional all-header)
4840   "Display ARTICLE in Article buffer."
4841   (setq gnus-summary-buffer (current-buffer))
4842   (if (null article)
4843       nil
4844     (gnus-configure-windows 'article)
4845     (pop-to-buffer gnus-summary-buffer)
4846     (gnus-article-prepare article all-header)
4847     (if (= (gnus-summary-article-mark) ?Z) 
4848         (progn
4849           (forward-line 1)
4850           (gnus-summary-position-cursor)))
4851     (gnus-summary-recenter)
4852     (gnus-set-mode-line 'summary)
4853     (run-hooks 'gnus-select-article-hook)
4854     ;; Successfully display article.
4855     t))
4856
4857 (defun gnus-summary-select-article (&optional all-headers force)
4858   "Select the current article.
4859 Optional first argument ALL-HEADERS is non-nil, show all header fields.
4860 Optional second argument FORCE is nil, the article is only selected
4861 again when current header does not match with ALL-HEADERS option."
4862   (let ((article (gnus-summary-article-number))
4863         (all-headers (not (not all-headers)))) ;Must be T or NIL.
4864     (if (or (null gnus-current-article)
4865             (null gnus-article-current)
4866             (/= article (cdr gnus-article-current))
4867             (not (equal (car gnus-article-current) gnus-newsgroup-name))
4868             force)
4869         ;; The requested article is different from the current article.
4870         (gnus-summary-display-article article all-headers)
4871       (if all-headers
4872           (gnus-article-show-all-headers))
4873       (gnus-configure-windows 'article)
4874       (pop-to-buffer gnus-summary-buffer))))
4875
4876 (defun gnus-summary-set-current-mark (&optional current-mark)
4877   "Obsolete function."
4878   nil)
4879
4880 (defun gnus-summary-next-article (unread &optional subject)
4881   "Select article after current one.
4882 If argument UNREAD is non-nil, only unread article is selected."
4883   (interactive "P")
4884   (let ((header nil))
4885     (cond ((gnus-summary-display-article
4886             (gnus-summary-search-forward unread subject)))
4887           ((and subject
4888                 gnus-auto-select-same
4889                 (gnus-set-difference gnus-newsgroup-unreads
4890                                      (append gnus-newsgroup-marked
4891                                              gnus-newsgroup-interesting))
4892                 (memq this-command
4893                       '(gnus-summary-next-unread-article
4894                         gnus-summary-next-page
4895                         gnus-summary-kill-same-subject-and-select
4896                         ;;gnus-summary-next-article
4897                         ;;gnus-summary-next-same-subject
4898                         ;;gnus-summary-next-unread-same-subject
4899                         )))
4900            ;; Wrap article pointer if there are unread articles.
4901            ;; Hook function, such as gnus-summary-rmail-digest, may
4902            ;; change current buffer, so need check.
4903            (let ((buffer (current-buffer))
4904                  (last-point (point)))
4905              ;; No more articles with same subject, so jump to the first
4906              ;; unread article.
4907              (gnus-summary-first-unread-article)
4908              ;;(and (eq buffer (current-buffer))
4909              ;; (= (point) last-point)
4910              ;; ;; Ignore given SUBJECT, and try again.
4911              ;; (gnus-summary-next-article unread nil))
4912              (and (eq buffer (current-buffer))
4913                   (< (point) last-point)
4914                   (message "Wrapped"))
4915              ))
4916           ((and gnus-auto-extend-newsgroup
4917                 (not unread)            ;Not unread only
4918                 (not subject)           ;Only if subject is not specified.
4919                 (setq header (gnus-more-header-forward)))
4920            ;; Extend to next article if possible.
4921            ;; Basic ideas by himacdonald@watdragon.waterloo.edu
4922            (gnus-extend-newsgroup header nil)
4923            ;; Threads feature must be turned off.
4924            (let ((buffer-read-only nil))
4925              (goto-char (point-max))
4926              (gnus-summary-prepare-threads (list header) 0))
4927            (gnus-summary-goto-article gnus-newsgroup-end))
4928           (t
4929            ;; Select next newsgroup automatically if requested.
4930            (let ((cmd (aref (this-command-keys) 0))
4931                  (group (gnus-summary-search-group nil gnus-keep-same-level))
4932                  (auto-select
4933                   (and gnus-auto-select-next
4934                        ;;(null (gnus-set-difference gnus-newsgroup-unreads
4935                        ;;                               gnus-newsgroup-marked))
4936                        (memq this-command
4937                              '(gnus-summary-next-unread-article
4938                                gnus-summary-next-article
4939                                gnus-summary-next-page
4940                                gnus-summary-next-same-subject
4941                                gnus-summary-next-unread-same-subject
4942                                gnus-summary-kill-same-subject
4943                                gnus-summary-kill-same-subject-and-select
4944                                ))
4945                        ;; Ignore characters typed ahead.
4946                        (not (input-pending-p))
4947                        )))
4948              ;; Keep just the event type of CMD.
4949              (if (listp cmd)
4950                  (setq cmd (car cmd)))
4951              (message "No more%s articles%s"
4952                       (if unread " unread" "")
4953                       (if (and auto-select
4954                                (not (eq gnus-auto-select-next 'quietly)))
4955                           (if group
4956                               (format " (Type %s for %s [%s])"
4957                                       (single-key-description cmd)
4958                                       group
4959                                       (car (gnus-gethash 
4960                                             group gnus-newsrc-hashtb)))
4961                             (format " (Type %s to exit %s)"
4962                                     (single-key-description cmd)
4963                                     gnus-newsgroup-name))
4964                         ""))
4965              ;; Select next unread newsgroup automagically.
4966              (cond ((and auto-select
4967                          (eq gnus-auto-select-next 'quietly))
4968                     ;; Select quietly.
4969                     (gnus-summary-next-group nil group))
4970                    (auto-select
4971                     ;; Confirm auto selection.
4972                     (let* ((event (read-event))
4973                            (type
4974                             (if (listp event)
4975                                 (car event)
4976                               event)))
4977                       (if (and (eq event type) (eq event cmd))
4978                           (gnus-summary-next-group nil group)
4979                         (setq unread-command-events (list event)))))
4980                    )
4981              ))
4982           )))
4983
4984 (defun gnus-summary-next-unread-article ()
4985   "Select unread article after current one."
4986   (interactive)
4987   (gnus-summary-next-article t (and gnus-auto-select-same
4988                                     (gnus-summary-subject-string)))
4989   (gnus-summary-position-cursor))
4990
4991 (defun gnus-summary-prev-article (unread &optional subject)
4992   "Select article before current one.
4993 If argument UNREAD is non-nil, only unread article is selected."
4994   (interactive "P")
4995   (let ((header nil))
4996     (cond ((gnus-summary-display-article
4997             (gnus-summary-search-backward unread subject)))
4998           ((and subject
4999                 gnus-auto-select-same
5000                 (gnus-set-difference gnus-newsgroup-unreads
5001                                      (append gnus-newsgroup-marked
5002                                              gnus-newsgroup-interesting))
5003                 (memq this-command
5004                       '(gnus-summary-prev-unread-article
5005                         ;;gnus-summary-prev-page
5006                         ;;gnus-summary-prev-article
5007                         ;;gnus-summary-prev-same-subject
5008                         ;;gnus-summary-prev-unread-same-subject
5009                         )))
5010            ;; Ignore given SUBJECT, and try again.
5011            (gnus-summary-prev-article unread nil))
5012           (unread
5013            (message "No more unread articles"))
5014           ((and gnus-auto-extend-newsgroup
5015                 (not subject)           ;Only if subject is not specified.
5016                 (setq header (gnus-more-header-backward)))
5017            ;; Extend to previous article if possible.
5018            ;; Basic ideas by himacdonald@watdragon.waterloo.edu
5019            (gnus-extend-newsgroup header t)
5020            (let ((buffer-read-only nil))
5021              (goto-char (point-min))
5022              (gnus-summary-prepare-threads (list header) 0))
5023            (gnus-summary-goto-article gnus-newsgroup-begin)
5024            (gnus-summary-position-cursor))
5025           (t
5026            (message "No more articles"))
5027           )))
5028
5029 (defun gnus-summary-prev-unread-article ()
5030   "Select unred article before current one."
5031   (interactive)
5032   (gnus-summary-prev-article t (and gnus-auto-select-same
5033                                     (gnus-summary-subject-string))))
5034
5035 (defun gnus-summary-next-page (lines)
5036   "Show next page of selected article.
5037 If end of article, select next article.
5038 Argument LINES specifies lines to be scrolled up."
5039   (interactive "P")
5040   (setq gnus-summary-buffer (current-buffer))
5041   (let ((article (gnus-summary-article-number))
5042         (endp nil))
5043     (if (or (null gnus-current-article)
5044             (null gnus-article-current)
5045             (/= article (cdr gnus-article-current))
5046             (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5047         ;; Selected subject is different from current article's.
5048           (gnus-summary-display-article article)
5049       (gnus-configure-windows 'article)
5050       (pop-to-buffer gnus-summary-buffer)
5051       (gnus-eval-in-buffer-window gnus-article-buffer
5052         (setq endp (gnus-article-next-page lines)))
5053       (cond ((and endp lines)
5054              (message "End of message"))
5055             ((and endp (null lines))
5056              (gnus-summary-next-unread-article)))
5057       (gnus-summary-position-cursor))))
5058
5059 (defun gnus-summary-prev-page (lines)
5060   "Show previous page of selected article.
5061 Argument LINES specifies lines to be scrolled down."
5062   (interactive "P")
5063   (let ((article (gnus-summary-article-number)))
5064     (if (or (null gnus-current-article)
5065             (null gnus-article-current)
5066             (/= article (cdr gnus-article-current))
5067             (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5068         ;; Selected subject is different from current article's.
5069         (gnus-summary-display-article article)
5070       (gnus-configure-windows 'article)
5071       (pop-to-buffer gnus-summary-buffer)
5072       (gnus-eval-in-buffer-window gnus-article-buffer
5073         (gnus-article-prev-page lines))
5074       (gnus-summary-position-cursor))))
5075
5076 (defun gnus-summary-scroll-up (lines)
5077   "Scroll up (or down) one line current article.
5078 Argument LINES specifies lines to be scrolled up (or down if negative)."
5079   (interactive "p")
5080   (gnus-summary-select-article)
5081   (gnus-eval-in-buffer-window gnus-article-buffer
5082     (cond ((> lines 0)
5083            (if (gnus-article-next-page lines)
5084                (message "End of message")))
5085           ((< lines 0)
5086            (gnus-article-prev-page (- 0 lines))))
5087     ))
5088
5089 (defun gnus-summary-next-same-subject ()
5090   "Select next article which has the same subject as current one."
5091   (interactive)
5092   (gnus-summary-next-article nil (gnus-summary-subject-string)))
5093
5094 (defun gnus-summary-prev-same-subject ()
5095   "Select previous article which has the same subject as current one."
5096   (interactive)
5097   (gnus-summary-prev-article nil (gnus-summary-subject-string)))
5098
5099 (defun gnus-summary-next-unread-same-subject ()
5100   "Select next unread article which has the same subject as current one."
5101   (interactive)
5102   (gnus-summary-next-article t (gnus-summary-subject-string)))
5103
5104 (defun gnus-summary-prev-unread-same-subject ()
5105   "Select previous unread article which has the same subject as current one."
5106   (interactive)
5107   (gnus-summary-prev-article t (gnus-summary-subject-string)))
5108
5109 (defun gnus-summary-first-unread-article ()
5110   "Select the first unread article. 
5111 Return nil if there are no unread articles."
5112   (interactive)
5113   (let ((begin (point)))
5114     (goto-char 1)
5115     (if (re-search-forward "  [-0-9]+ [0-9]+[\n\r]" nil t)
5116         (progn
5117           (forward-char -1)
5118           ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
5119           (gnus-summary-position-cursor)
5120           (gnus-summary-display-article (gnus-summary-article-number)))
5121       ;; If there is no unread articles, stay where you are.
5122       (goto-char begin)
5123       (message "No more unread articles")
5124       nil)))
5125
5126 (defun gnus-summary-goto-article (article &optional all-headers)
5127   "Fetch ARTICLE and display it if it exists.
5128 If ALL-HEADERS is non-nil, no header lines are hidden."
5129   (interactive
5130    (list
5131     (string-to-int
5132      (completing-read "Article number: "
5133                       (mapcar
5134                        (lambda (headers)
5135                          (list
5136                           (int-to-string (header-number headers))))
5137                        gnus-newsgroup-headers)
5138                       nil 'require-match))))
5139   (if (gnus-summary-goto-subject article)
5140       (gnus-summary-display-article article all-headers)))
5141
5142 (defun gnus-summary-goto-last-article ()
5143   "Go to last subject line."
5144   (interactive)
5145   (if gnus-last-article
5146       (gnus-summary-goto-article gnus-last-article)))
5147
5148
5149 ;; Summary article oriented commands
5150
5151 (defun gnus-summary-refer-parent-article ()
5152   "Refer parent article of current article."
5153   (interactive)
5154   (let ((ref (header-references gnus-current-headers))
5155         parent)
5156     (if (or (not ref) (equal ref ""))
5157         (error "No references in this article"))
5158     (and (string-match "<[^<>]*>[ \t]*$" ref)
5159          (setq parent 
5160                (substring ref (match-beginning 0) (match-end 0))))
5161     (if (stringp parent)
5162         (gnus-summary-refer-article parent)
5163       (error "Possibly malformed references"))))
5164
5165 (defun gnus-summary-refer-article (message-id)
5166   "Refer article specified by MESSAGE-ID.
5167 NOTE: This command only works with newsgroup that use NNTP."
5168   (interactive "sMessage-ID: ")
5169   ;; Make sure that this command depends on the fact that article
5170   ;; related information is not updated when an article is retrieved
5171   ;; by Message-ID.
5172   (gnus-summary-select-article t)       ;Request all headers.
5173   (if (and (stringp message-id)
5174            (> (length message-id) 0))
5175       (let ((current (header-id gnus-current-headers)))
5176         (gnus-eval-in-buffer-window 
5177          gnus-article-buffer
5178          ;; Construct the correct Message-ID if necessary.
5179          ;; Suggested by tale@pawl.rpi.edu.
5180          (or (string-match "^<" message-id)
5181              (setq message-id (concat "<" message-id)))
5182          (or (string-match ">$" message-id)
5183              (setq message-id (concat message-id ">"))))))
5184   (if (and (stringp message-id)
5185            (gnus-article-prepare message-id nil (gnus-read-header message-id)))
5186       (progn
5187         (gnus-summary-insert-line 
5188          nil gnus-current-headers 0 nil ?D nil nil t)
5189         (forward-line -1)
5190         (gnus-summary-position-cursor)
5191         message-id)
5192     (error "No such references")))
5193
5194 (defun gnus-summary-next-digest (nth)
5195   "Move to head of NTH next digested message."
5196   (interactive "p")
5197   (gnus-summary-select-article)
5198   (gnus-eval-in-buffer-window gnus-article-buffer
5199     (gnus-article-next-digest (or nth 1))
5200     ))
5201
5202 (defun gnus-summary-prev-digest (nth)
5203   "Move to head of NTH previous digested message."
5204   (interactive "p")
5205   (gnus-summary-select-article)
5206   (gnus-eval-in-buffer-window gnus-article-buffer
5207     (gnus-article-prev-digest (or nth 1))
5208     ))
5209
5210 (defun gnus-summary-rmail-digest ()
5211   "Run RMAIL on current digest article.
5212 gnus-select-digest-hook will be called with no arguments, if that
5213 value is non-nil. It is possible to modify the article so that Rmail
5214 can work with it.
5215 gnus-rmail-digest-hook will be called with no arguments, if that value
5216 is non-nil. The hook is intended to customize Rmail mode."
5217   (interactive)
5218   (gnus-summary-select-article)
5219   (require 'rmail)
5220   (let ((artbuf gnus-article-buffer)
5221         (digbuf (get-buffer-create gnus-digest-buffer))
5222         (mail-header-separator ""))
5223     (set-buffer digbuf)
5224     (gnus-add-current-to-buffer-list)
5225     (buffer-disable-undo (current-buffer))
5226     (setq buffer-read-only nil)
5227     (erase-buffer)
5228     (insert-buffer-substring artbuf)
5229     (run-hooks 'gnus-select-digest-hook)
5230     (gnus-convert-article-to-rmail)
5231     (goto-char (point-min))
5232     ;; Rmail initializations.
5233     (rmail-insert-rmail-file-header)
5234     (rmail-mode)
5235     (rmail-set-message-counters)
5236     (rmail-show-message)
5237     (condition-case ()
5238         (progn
5239           (undigestify-rmail-message)
5240           (rmail-expunge)               ;Delete original message.
5241           ;; File name is meaningless but `save-buffer' requires it.
5242           (setq buffer-file-name "Gnus Digest")
5243           (setq mode-line-buffer-identification
5244                 (concat "Digest: "
5245                         (header-subject gnus-current-headers)))
5246           ;; There is no need to write this buffer to a file.
5247           (make-local-variable 'write-file-hooks)
5248           (setq write-file-hooks
5249                 (list (lambda ()
5250                         (set-buffer-modified-p nil)
5251                         (message "(No changes need to be saved)")
5252                         'no-need-to-write-this-buffer)))
5253           ;; Default file name saving digest messages.
5254           (setq rmail-default-rmail-file
5255                 (funcall gnus-rmail-save-name gnus-newsgroup-name
5256                          gnus-current-headers gnus-newsgroup-last-rmail))
5257           (setq rmail-default-file
5258                 (funcall gnus-mail-save-name gnus-newsgroup-name
5259                          gnus-current-headers gnus-newsgroup-last-mail))
5260           ;; Prevent generating new buffer named ***<N> each time.
5261           (setq rmail-summary-buffer
5262                 (get-buffer-create gnus-digest-summary-buffer))
5263           (run-hooks 'gnus-rmail-digest-hook)
5264           ;; Take all windows safely.
5265           (gnus-configure-windows '(1 0 0))
5266           (pop-to-buffer gnus-group-buffer)
5267           ;; Use Summary Article windows for Digest summary and
5268           ;; Digest buffers.
5269           (if gnus-digest-show-summary
5270               (let ((gnus-summary-buffer gnus-digest-summary-buffer)
5271                     (gnus-article-buffer gnus-digest-buffer))
5272                 (gnus-configure-windows 'article)
5273                 (pop-to-buffer gnus-digest-buffer)
5274                 (rmail-summary)
5275                 (pop-to-buffer gnus-digest-summary-buffer)
5276                 (message (substitute-command-keys
5277                           "Type \\[rmail-summary-quit] to return to Gnus")))
5278             (let ((gnus-summary-buffer gnus-digest-buffer))
5279               (gnus-configure-windows 'summary)
5280               (pop-to-buffer gnus-digest-buffer)
5281               (message (substitute-command-keys
5282                         "Type \\[rmail-quit] to return to Gnus")))
5283             )
5284           ;; Move the buffers to the end of buffer list.
5285           (bury-buffer gnus-article-buffer)
5286           (bury-buffer gnus-group-buffer)
5287           (bury-buffer gnus-digest-summary-buffer)
5288           (bury-buffer gnus-digest-buffer))
5289       (error (set-buffer-modified-p nil)
5290              (kill-buffer digbuf)
5291              ;; This command should not signal an error because the
5292              ;; command is called from hooks.
5293              (ding) (message "Article is not a digest")))
5294     ))
5295
5296 (defun gnus-summary-isearch-article ()
5297   "Do incremental search forward on current article."
5298   (interactive)
5299   (gnus-summary-select-article)
5300   (gnus-eval-in-buffer-window gnus-article-buffer
5301                               (isearch-forward)))
5302
5303 (defun gnus-summary-search-article-forward (regexp)
5304   "Search for an article containing REGEXP forward.
5305 gnus-select-article-hook is not called during the search."
5306   (interactive
5307    (list (read-string
5308           (concat "Search forward (regexp): "
5309                   (if gnus-last-search-regexp
5310                       (concat "(default " gnus-last-search-regexp ") "))))))
5311   (if (string-equal regexp "")
5312       (setq regexp (or gnus-last-search-regexp ""))
5313     (setq gnus-last-search-regexp regexp))
5314   (if (gnus-summary-search-article regexp nil)
5315       (gnus-eval-in-buffer-window gnus-article-buffer
5316         (recenter 0)
5317         ;;(sit-for 1)
5318         )
5319     (error "Search failed: \"%s\"" regexp)
5320     ))
5321
5322 (defun gnus-summary-search-article-backward (regexp)
5323   "Search for an article containing REGEXP backward.
5324 gnus-select-article-hook is not called during the search."
5325   (interactive
5326    (list (read-string
5327           (concat "Search backward (regexp): "
5328                   (if gnus-last-search-regexp
5329                       (concat "(default " gnus-last-search-regexp ") "))))))
5330   (if (string-equal regexp "")
5331       (setq regexp (or gnus-last-search-regexp ""))
5332     (setq gnus-last-search-regexp regexp))
5333   (if (gnus-summary-search-article regexp t)
5334       (gnus-eval-in-buffer-window gnus-article-buffer
5335         (recenter 0)
5336         ;;(sit-for 1)
5337         )
5338     (error "Search failed: \"%s\"" regexp)
5339     ))
5340
5341 (defun gnus-summary-search-article (regexp &optional backward)
5342   "Search for an article containing REGEXP.
5343 Optional argument BACKWARD means do search for backward.
5344 gnus-select-article-hook is not called during the search."
5345   (let ((gnus-select-article-hook nil)  ;Disable hook.
5346         (gnus-mark-article-hook nil)    ;Inhibit marking as read.
5347         (re-search
5348          (if backward
5349              (function re-search-backward) (function re-search-forward)))
5350         (found nil)
5351         (last nil))
5352     ;; Hidden thread subtrees must be searched for ,too.
5353     (gnus-summary-show-all-threads)
5354     ;; First of all, search current article.
5355     ;; We don't want to read article again from NNTP server nor reset
5356     ;; current point.
5357     (gnus-summary-select-article)
5358     (message "Searching article: %d..." gnus-current-article)
5359     (setq last gnus-current-article)
5360     (gnus-eval-in-buffer-window gnus-article-buffer
5361       (save-restriction
5362         (widen)
5363         ;; Begin search from current point.
5364         (setq found (funcall re-search regexp nil t))))
5365     ;; Then search next articles.
5366     (while (and (not found)
5367                 (gnus-summary-display-article 
5368                  (gnus-summary-search-subject backward nil nil)))
5369       (message "Searching article: %d..." gnus-current-article)
5370       (gnus-eval-in-buffer-window gnus-article-buffer
5371         (save-restriction
5372           (widen)
5373           (goto-char (if backward (point-max) (point-min)))
5374           (setq found (funcall re-search regexp nil t)))
5375         ))
5376     (message "")
5377     ;; Adjust article pointer.
5378     (or (eq last gnus-current-article)
5379         (setq gnus-last-article last))
5380     ;; Return T if found such article.
5381     found
5382     ))
5383
5384 (defun gnus-summary-execute-command (field regexp command &optional backward)
5385   "If FIELD of article header matches REGEXP, execute a COMMAND string.
5386 If FIELD is an empty string (or nil), entire article body is searched for.
5387 If optional (prefix) argument BACKWARD is non-nil, do backward instead."
5388   (interactive
5389    (list (let ((completion-ignore-case t))
5390            (completing-read "Field name: "
5391                             '(("Number")("Subject")("From")
5392                               ("Lines")("Date")("Id")
5393                               ("Xref")("References"))
5394                             nil 'require-match))
5395          (read-string "Regexp: ")
5396          (read-key-sequence "Command: ")
5397          current-prefix-arg))
5398   ;; Hidden thread subtrees must be searched for ,too.
5399   (gnus-summary-show-all-threads)
5400   ;; We don't want to change current point nor window configuration.
5401   (save-excursion
5402     (save-window-excursion
5403       (message "Executing %s..." (key-description command))
5404       ;; We'd like to execute COMMAND interactively so as to give arguments.
5405       (gnus-execute field regexp
5406                     (` (lambda ()
5407                          (call-interactively '(, (key-binding command)))))
5408                     backward)
5409       (message "Executing %s... done" (key-description command)))))
5410
5411 (defun gnus-summary-beginning-of-article ()
5412   "Scroll the article back to the beginning."
5413   (interactive)
5414   (gnus-summary-select-article)
5415   (gnus-eval-in-buffer-window gnus-article-buffer
5416     (widen)
5417     (goto-char (point-min))
5418     (if gnus-break-pages
5419         (gnus-narrow-to-page))
5420     ))
5421
5422 (defun gnus-summary-end-of-article ()
5423   "Scroll to the end of the article."
5424   (interactive)
5425   (gnus-summary-select-article)
5426   (gnus-eval-in-buffer-window gnus-article-buffer
5427     (widen)
5428     (goto-char (point-max))
5429     (if gnus-break-pages
5430         (gnus-narrow-to-page))
5431     ))
5432
5433 (defun gnus-summary-show-article ()
5434   "Force re-fetching of the current article."
5435   (interactive)
5436   (gnus-summary-select-article gnus-have-all-headers t))
5437
5438 (defun gnus-summary-toggle-header (arg)
5439   "Show the headers if they are hidden, or hide them if they are shown.
5440 If ARG is a positive number, show the entire header.
5441 If ARG is a negative number, hide the unwanted header lines."
5442   (interactive "P")
5443   (save-excursion
5444     (set-buffer gnus-article-buffer)
5445     (let ((buffer-read-only nil))
5446       (if (numberp arg) 
5447           (if (> arg 0) (remove-text-properties 1 (point-max) '(invisible t))
5448             (if (< arg 0) (run-hooks 'gnus-article-display-hook)))
5449         (if (text-property-any 1 (point-max) 'invisible t)
5450             (remove-text-properties 1 (point-max) '(invisible t))
5451           (run-hooks 'gnus-article-display-hook))))))
5452
5453 (defun gnus-summary-show-all-headers ()
5454   "Make all header lines visible."
5455   (interactive)
5456   (gnus-article-show-all-headers))
5457
5458 (defun gnus-summary-toggle-mime (arg)
5459   "Toggle MIME processing.
5460 If ARG is a positive number, turn MIME processing on."
5461   (interactive "P")
5462   (setq gnus-show-mime
5463         (if (null arg) (not gnus-show-mime)
5464           (> (prefix-numeric-value arg) 0)))
5465   (gnus-summary-select-article t 'force))
5466
5467 (defun gnus-summary-caesar-message (rotnum)
5468   "Caesar rotates all letters of current message by 13/47 places.
5469 With prefix arg, specifies the number of places to rotate each letter forward.
5470 Caesar rotates Japanese letters by 47 places in any case."
5471   (interactive "P")
5472   (gnus-summary-select-article)
5473   (gnus-overload-functions)
5474   (gnus-eval-in-buffer-window gnus-article-buffer
5475     (save-restriction
5476       (widen)
5477       ;; We don't want to jump to the beginning of the message.
5478       ;; `save-excursion' does not do its job.
5479       (move-to-window-line 0)
5480       (let ((last (point)))
5481         (news-caesar-buffer-body rotnum)
5482         (goto-char last)
5483         (recenter 0)
5484         ))
5485     ))
5486
5487 (defun gnus-summary-stop-page-breaking ()
5488   "Stop page breaking in the current article."
5489   (interactive)
5490   (gnus-summary-select-article)
5491   (gnus-eval-in-buffer-window gnus-article-buffer (widen)))
5492
5493 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
5494
5495 (defun gnus-summary-move-article (n &optional to-newsgroup select-method)
5496   "Move the current article to a different newsgroup.
5497 If N is a positive number, move the N next articles.
5498 If N is a negative number, move the N previous articles.
5499 If N is nil and any articles have been marked with the process mark,
5500 move those articles instead.
5501 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to. 
5502 If SELECT-METHOD is symbol, do not move to a specific newsgroup, but
5503 re-spool using this method.
5504 For this function to work, both the current newsgroup and the
5505 newsgroup that you want to move to have to support the `request-move'
5506 and `request-accept' functions. (Ie. mail newsgroups at present.)"
5507   (interactive "P")
5508   (or (gnus-check-backend-function 'request-move-article gnus-newsgroup-name)
5509       (error "The current newsgroup does not support article moving"))
5510   (let (articles art-group)
5511     (if (and n (numberp n))
5512         (let ((backward (< n 0))
5513               (n (abs n)))
5514           (save-excursion
5515             (while (and (> n 0)
5516                         (setq articles (cons (gnus-summary-article-number) 
5517                                              articles))
5518                         (gnus-summary-search-forward nil nil backward))
5519               (setq n (1- n))))
5520           (setq articles (sort articles (function <))))
5521       (setq articles (or (setq gnus-newsgroup-processable
5522                                (sort gnus-newsgroup-processable (function <)))
5523                          (list (gnus-summary-article-number)))))
5524     (if (and (not to-newsgroup) (not select-method))
5525         (setq to-newsgroup
5526               (completing-read 
5527                (format "Where do you want to move %s? "
5528                        (if (> (length articles) 1)
5529                            (format "these %d articles" (length articles))
5530                          "this article"))
5531                gnus-active-hashtb nil t)))
5532     (or (gnus-check-backend-function 'request-accept-article 
5533                                      (or select-method to-newsgroup))
5534         (error "%s does not support article moving" to-newsgroup))
5535     (message "Moving to %s: %s..." (or select-method to-newsgroup) articles)
5536     (while articles
5537       (if (setq art-group
5538                 (gnus-request-move-article 
5539                  (car articles)
5540                  gnus-newsgroup-name (nth 1 gnus-current-select-method)
5541                  (list 'gnus-request-accept-article 
5542                        (or select-method to-newsgroup))))
5543           (let* ((buffer-read-only nil)
5544                  (entry (or
5545                          (gnus-gethash (car art-group) gnus-newsrc-hashtb)
5546                          (gnus-gethash (concat gnus-foreign-group-prefix
5547                                                (car art-group) )
5548                                        gnus-newsrc-hashtb)))
5549                  (info (nth 2 entry))
5550                  (article (car articles))
5551                  (marked (nth 3 info)))
5552             (gnus-summary-goto-subject article)
5553             (delete-region (progn (beginning-of-line) (point))
5554                            (progn (forward-line 1) (point)))
5555             (if (not (memq article gnus-newsgroup-unreads))
5556                 (setcar (cdr (cdr info))
5557                         (gnus-add-to-range (nth 2 info) 
5558                                            (list (cdr art-group)))))
5559             ;; !!! Here one should copy all the marks over to the new
5560             ;; newsgroup, but I couldn't be bothered. nth on that!
5561             )
5562         (message "Couldn't move article %s" (car articles)))
5563       (setq articles (cdr articles)))))
5564
5565 (defun gnus-summary-respool-article (n &optional respool-method)
5566   "Respool the current article.
5567 The article will be squeezed through the mail spooling process again,
5568 which means that it will be put in some mail newsgroup or other
5569 depending on `nnmail-split-methods'.
5570 If N is a positive number, respool the N next articles.
5571 If N is a negative number, respool the N previous articles.
5572 If N is nil and any articles have been marked with the process mark,
5573 respool those articles instead.
5574 For this function to work, both the current newsgroup and the
5575 newsgroup that you want to move to have to support the `request-move'
5576 and `request-accept' functions. (Ie. mail newsgroups at present.)"
5577   (interactive "P")
5578   (or respool-method
5579       (setq respool-method
5580             (completing-read
5581              "What method do you want to use when respooling? "
5582              (gnus-methods-using 'respool) nil t)))
5583   (gnus-summary-move-article n nil respool-method))
5584              
5585
5586 ;; Summary marking commands.
5587
5588 (defun gnus-summary-kill-same-subject-and-select (unmark)
5589   "Mark articles which has the same subject as read, and then select the next.
5590 If UNMARK is positive, remove any kind of mark.
5591 If UNMARK is negative, tick articles."
5592   (interactive "P")
5593   (if unmark
5594       (setq unmark (prefix-numeric-value unmark)))
5595   (let ((count
5596          (gnus-summary-mark-same-subject
5597           (gnus-summary-subject-string) unmark)))
5598     ;; Select next unread article. If auto-select-same mode, should
5599     ;; select the first unread article.
5600     (gnus-summary-next-article t (and gnus-auto-select-same
5601                                       (gnus-summary-subject-string)))
5602     (message "%d articles are marked as %s"
5603              count (if unmark "unread" "read"))
5604     ))
5605
5606 (defun gnus-summary-kill-same-subject (unmark)
5607   "Mark articles which has the same subject as read. 
5608 If UNMARK is positive, remove any kind of mark.
5609 If UNMARK is negative, tick articles."
5610   (interactive "P")
5611   (if unmark
5612       (setq unmark (prefix-numeric-value unmark)))
5613   (let ((count
5614          (gnus-summary-mark-same-subject
5615           (gnus-summary-subject-string) unmark)))
5616     ;; If marked as read, go to next unread subject.
5617     (if (null unmark)
5618         ;; Go to next unread subject.
5619         (gnus-summary-next-subject 1 t))
5620     (message "%d articles are marked as %s"
5621              count (if unmark "unread" "read"))
5622     ))
5623
5624 (defun gnus-summary-mark-same-subject (subject &optional unmark)
5625   "Mark articles with same SUBJECT as read, and return marked number.
5626 If optional argument UNMARK is positive, remove any kinds of marks.
5627 If optional argument UNMARK is negative, mark articles as unread instead."
5628   (let ((count 1))
5629     (save-excursion
5630       (cond ((null unmark)
5631              (gnus-summary-mark-as-read nil "K"))
5632             ((> unmark 0)
5633              (gnus-summary-tick-article nil t))
5634             (t
5635              (gnus-summary-tick-article)))
5636       (while (and subject
5637                   (gnus-summary-search-forward nil subject))
5638         (cond ((null unmark)
5639                (gnus-summary-mark-as-read nil "K"))
5640               ((> unmark 0)
5641                (gnus-summary-tick-article nil t))
5642               (t
5643                (gnus-summary-tick-article)))
5644         (setq count (1+ count))
5645         ))
5646     ;; Hide killed thread subtrees.  Does not work properly always.
5647     ;;(and (null unmark)
5648     ;;     gnus-thread-hide-killed
5649     ;;     (gnus-summary-hide-thread))
5650     ;; Return number of articles marked as read.
5651     count
5652     ))
5653
5654 (defun gnus-summary-mark-as-processable (n &optional unmark)
5655   "Set the process mark on the next N articles.
5656 If N is negative, mark backward instead.  If UNMARK is non-nil, remove
5657 the process mark instead.  The difference between N and the actual
5658 number of articles marked is returned."
5659   (interactive "p")
5660   (let ((backward (< n 0))
5661         (n (abs n)))
5662   (while (and (> n 0)
5663               (if unmark
5664                   (gnus-summary-remove-process-mark 
5665                    (gnus-summary-article-number))
5666                 (gnus-summary-set-process-mark 
5667                  (gnus-summary-article-number)))
5668               (= 0 (gnus-summary-next-subject (if backward -1 1))))
5669     (setq n (1- n)))
5670   (if (/= 0 n) (message "No more articles"))
5671   n))
5672
5673 (defun gnus-summary-unmark-as-processable (n)
5674   "Remove the process mark from the next N articles.
5675 If N is negative, mark backward instead.  The difference between N and
5676 the actual number of articles marked is returned."
5677   (interactive "p")
5678   (gnus-summary-mark-as-processable n t))
5679
5680 (defun gnus-summary-unmark-all-processable ()
5681   "Remove the process mark from all articles."
5682   (interactive)
5683   (save-excursion
5684     (while gnus-newsgroup-processable
5685       (gnus-summary-remove-process-mark (car gnus-newsgroup-processable))))
5686   (gnus-summary-position-cursor))
5687
5688 (defun gnus-summary-mark-as-expirable (n &optional unmark)
5689   "Mark N articles forward as expirable.
5690 If N is negative, mark backward instead.  If UNMARK is non-nil, remove
5691 the expirably mark instead.  The difference between N and the actual
5692 number of articles marked is returned."
5693   (interactive "p")
5694   (let ((backward (< n 0))
5695         (n (abs n)))
5696   (while (and (> n 0)
5697               (if unmark
5698                   (gnus-summary-remove-expirable-mark 
5699                    (gnus-summary-article-number))
5700                 (gnus-summary-set-expirable-mark 
5701                  (gnus-summary-article-number)))
5702               (= 0 (gnus-summary-next-subject (if backward -1 1))))
5703     (setq n (1- n)))
5704   (if (/= 0 n) (message "No more articles"))
5705   n))
5706
5707 (defun gnus-summary-unmark-as-expirable (n)
5708   "Mark N articles forward as expirable.
5709 If N is negative, mark backward instead.  The difference between N and
5710 the actual number of articles marked is returned."
5711   (interactive "p")
5712   (gnus-summary-mark-as-expirable n t))
5713
5714 (defun gnus-summary-set-expirable-mark (article)
5715   "Mark the current article as expirable and update the Summary line."
5716   (setq gnus-newsgroup-expirable (cons article gnus-newsgroup-expirable))
5717   (let ((buffer-read-only nil))
5718     (if (gnus-summary-goto-subject article)
5719         (progn
5720           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5721           (beginning-of-line)
5722           (forward-char 2)
5723           (delete-char 1)
5724           (insert "X")
5725           t))))
5726
5727 (defun gnus-summary-remove-expirable-mark (article)
5728   "Remove the expirable mark from ARTICLE as expirable and update the Summary line."
5729   (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable))
5730   (let ((buffer-read-only nil))
5731     (if (gnus-summary-goto-subject article)
5732         (progn
5733           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5734           (beginning-of-line)
5735           (forward-char 2)
5736           (delete-char 1)
5737           (insert 
5738            (if (memq article gnus-newsgroup-processable) ?# ? ))
5739           t))))
5740
5741 (defun gnus-summary-expire-articles ()
5742   "Expire all articles that are marked as expirable in the current group."
5743   (interactive)
5744   (if (and gnus-newsgroup-expirable
5745            (gnus-check-backend-function 
5746             'gnus-request-expire-articles gnus-newsgroup-name))
5747       (setq gnus-newsgroup-expirable 
5748             (gnus-request-expire-articles gnus-newsgroup-expirable
5749                                           gnus-newsgroup-name))))
5750
5751 (defun gnus-summary-mark-article-as-replied (article)
5752   "Mark ARTICLE replied and update the Summary line."
5753   (setq gnus-newsgroup-replied (cons article gnus-newsgroup-replied))
5754   (let ((buffer-read-only nil))
5755     (if (gnus-summary-goto-subject article)
5756         (progn
5757           (beginning-of-line)
5758           (forward-char 1)
5759           (delete-char 1)
5760           (insert "R")
5761           t))))
5762
5763 (defun gnus-summary-set-bookmark (article)
5764   "Set a bookmark in current article."
5765   (interactive (list (gnus-summary-article-number)))
5766   (if (or (not (get-buffer gnus-article-buffer))
5767           (not gnus-current-article)
5768           (not gnus-article-current)
5769           (not (equal gnus-newsgroup-name (car gnus-article-current))))
5770       (error "No current article selected"))
5771   ;; Remove old bookmark, if one exists.
5772   (let ((old (assq article gnus-newsgroup-bookmarks)))
5773     (if old (setq gnus-newsgroup-bookmarks 
5774                   (delq old gnus-newsgroup-bookmarks))))
5775   ;; Set the new bookmark, which is on the form 
5776   ;; (article-number . line-number-in-body).
5777   (setq gnus-newsgroup-bookmarks 
5778         (cons 
5779          (cons article 
5780                (save-excursion
5781                  (set-buffer gnus-article-buffer)
5782                  (count-lines
5783                   (min (point)
5784                        (save-excursion
5785                          (goto-char 1)
5786                          (search-forward "\n\n" nil t)
5787                          (point)))
5788                   (point))))
5789          gnus-newsgroup-bookmarks))
5790   (message "A bookmark has been added to the current article."))
5791
5792 (defun gnus-summary-remove-bookmark (article)
5793   "Remove the bookmark from the current article."
5794   (interactive (list (gnus-summary-article-number)))
5795   ;; Remove old bookmark, if one exists.
5796   (let ((old (assq article gnus-newsgroup-bookmarks)))
5797     (if old 
5798         (progn
5799           (setq gnus-newsgroup-bookmarks 
5800                 (delq old gnus-newsgroup-bookmarks))
5801           (message "Removed bookmark."))
5802       (message "No bookmark in current article."))))
5803
5804 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
5805 (defun gnus-summary-mark-as-interesting (n)
5806   "Mark N articles forward as interesting.
5807 If N is negative, mark backward instead.  The difference between N and
5808 the actual number of articles marked is returned."
5809   (interactive "p")
5810   (gnus-summary-mark-forward n "I"))
5811
5812 (defun gnus-summary-set-process-mark (article)
5813   "Set the process mark on ARTICLE and update the Summary line."
5814   (setq gnus-newsgroup-processable (cons article gnus-newsgroup-processable))
5815   (let ((buffer-read-only nil))
5816     (if (gnus-summary-goto-subject article)
5817         (progn
5818           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5819           (beginning-of-line)
5820           (forward-char 2)
5821           (delete-char 1)
5822           (insert "#")
5823           t))))
5824
5825 (defun gnus-summary-remove-process-mark (article)
5826   "Remove the process mark from ARTICLE and update the Summary line."
5827   (setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable))
5828   (let ((buffer-read-only nil))
5829     (if (gnus-summary-goto-subject article)
5830         (progn
5831           (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5832           (beginning-of-line)
5833           (forward-char 2)
5834           (delete-char 1)
5835           (insert 
5836            (if (memq article gnus-newsgroup-expirable) ?X ? ))
5837           t))))
5838
5839 (defun gnus-summary-mark-forward (n &optional unread)
5840   "Mark N articles as read forwards.
5841 If N is negative, mark backwards instead.
5842 If UNREAD is non-nil, mark articles as unread. In that case, UNREAD
5843 must either be \" \", \"-\" or \"I\".
5844 The difference between N and the actual number of articles marked is
5845 returned."
5846   (interactive "p")
5847   (let ((backward (< n 0))
5848         (n (abs n)))
5849   (while (and (> n 0)
5850               (gnus-summary-mark-article nil unread)
5851               (= 0 (gnus-summary-next-subject (if backward -1 1) 
5852                                               (not unread))))
5853     (setq n (1- n)))
5854   (if (/= 0 n) (message "No more %sarticles" (if unread "" "unread ")))
5855   (gnus-set-mode-line 'summary)
5856   n))
5857
5858 (defun gnus-summary-mark-article (&optional article mark)
5859   "Mark ARTICLE with MARK.
5860 MARK can be any string (but it should just be one character long). 
5861 Four MARK strings are reserved: \" \" (unread), 
5862 \"-\" (ticked), \"I\" (interesting), \"D\" (read).
5863 If MARK is nil, then the default string \"D\" is used.
5864 If ARTICLE is nil, then the article on the current line will be
5865 marked." 
5866   (let* ((buffer-read-only nil)
5867          (mark (or mark "D"))
5868          (article (or article (gnus-summary-article-number))))
5869     (if (numberp mark) (setq mark (format "%c" mark)))
5870     (prog1
5871         (if (gnus-summary-goto-subject article)
5872             (progn
5873               (gnus-summary-show-thread)
5874               (beginning-of-line)
5875               (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5876               ;; Fix the invisible mark.
5877               (re-search-forward ". [-0-9]+ [0-9]+[\n\r]")
5878               (goto-char (match-beginning 0))
5879               (delete-char 1)
5880               (insert mark)
5881               (set-text-properties (1- (point)) (point) '(invisible t))
5882               ;; Fix the visible mark.
5883               (beginning-of-line)
5884               (delete-char 1)
5885               (insert mark)
5886               t))
5887       ;; Bug by Brian Edmonds <bedmonds@prodigy.bc.ca>
5888       (if (or (string= mark " ") (string= mark "-") (string= mark "I"))
5889           (gnus-mark-article-as-unread article mark)
5890         (gnus-mark-article-as-read article)))))
5891
5892 (defun gnus-mark-article-as-read (article)
5893   "Remember that ARTICLE is marked as read."
5894   ;; Make the article expirable.
5895   (if gnus-newsgroup-auto-expire
5896     (gnus-summary-set-expirable-mark article))
5897   ;; Remove from unread and marked list.
5898   (setq gnus-newsgroup-unreads
5899         (delq article gnus-newsgroup-unreads))
5900   (setq gnus-newsgroup-marked
5901         (delq article gnus-newsgroup-marked))
5902   (setq gnus-newsgroup-interesting
5903         (delq article gnus-newsgroup-interesting)))
5904
5905 (defun gnus-mark-article-as-unread (article &optional mark)
5906   "Remember that ARTICLE is marked as unread.
5907 MARK is the mark type: \" \", \"-\" or \"I\"."
5908   ;; Add to unread list.
5909   (or (memq article gnus-newsgroup-unreads)
5910       (setq gnus-newsgroup-unreads
5911             (cons article gnus-newsgroup-unreads)))
5912   ;; Update the expired list.
5913   (gnus-summary-remove-expirable-mark article)
5914   ;; If CLEAR-MARK is non-nil, the article must be removed from marked
5915   ;; list.  Otherwise, it must be added to the list.
5916   (setq gnus-newsgroup-marked
5917         (delq article gnus-newsgroup-marked))
5918   (setq gnus-newsgroup-interesting
5919         (delq article gnus-newsgroup-interesting))
5920   (if (equal mark "-")
5921       (setq gnus-newsgroup-marked 
5922             (cons article gnus-newsgroup-marked)))
5923   (if (equal mark "I")
5924       (setq gnus-newsgroup-interesting 
5925             (cons article gnus-newsgroup-interesting))))
5926
5927 (defalias 'gnus-summary-mark-as-unread-forward 
5928   'gnus-summary-tick-article-forward)
5929 (make-obsolete 'gnus-summary-mark-as-unread-forward 
5930                'gnus-summary-tick-article--forward)
5931 (defun gnus-summary-tick-article-forward (n)
5932   "Tick N articles forwards.
5933 If N is negative, tick backwards instead.
5934 The difference between N and the number of articles ticked is returned."
5935   (interactive "p")
5936   (gnus-summary-mark-forward n "-"))
5937
5938 (defalias 'gnus-summary-mark-as-unread-backward 'gnus-summary-tick-article-backward)
5939 (make-obsolete 'gnus-summary-mark-as-unread-backward 'gnus-summary-tick-article-backward)
5940 (defun gnus-summary-tick-article-backward (n)
5941   "Tick N articles backwards.
5942 The difference between N and the number of articles ticked is returned."
5943   (interactive "p")
5944   (gnus-summary-mark-forward (- n) "-"))
5945
5946 (defalias 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
5947 (make-obsolete 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
5948 (defun gnus-summary-tick-article (&optional article clear-mark)
5949   "Mark current article as unread.
5950 Optional 1st argument ARTICLE specifies article number to be marked as unread.
5951 Optional 2nd argument CLEAR-MARK remove any kinds of mark."
5952   (gnus-summary-mark-article article (if clear-mark " " "-")))
5953
5954 (defun gnus-summary-mark-as-read-forward (n)
5955   "Mark N articles as read forwards.
5956 If N is negative, mark backwards instead.
5957 The difference between N and the actual number of articles marked is
5958 returned."
5959   (interactive "p")
5960   (gnus-summary-mark-forward n))
5961
5962 (defun gnus-summary-mark-as-read-backward (n)
5963   "Mark the N articles as read backwards.
5964 The difference between N and the actual number of articles marked is
5965 returned."
5966   (interactive "p")
5967   (gnus-summary-mark-forward (- n)))
5968
5969 (defun gnus-summary-mark-as-read (&optional article mark)
5970   "Mark current article as read.
5971 ARTICLE specifies the article to be marked as read.
5972 MARK specifies a string to be inserted at the beginning of the line.
5973 Any kind of string (length 1) except for a space and `-' is ok."
5974   (gnus-summary-mark-article article mark))
5975
5976 (defun gnus-summary-clear-mark-forward (n)
5977   "Clear marks from N articles forward.
5978 If N is negative, clear backward instead.
5979 The difference between N and the number of marks cleared is returned."
5980   (interactive "p")
5981   (gnus-summary-mark-forward n " "))
5982
5983 (defun gnus-summary-clear-mark-backward (n)
5984   "Clear marks from N articles backward.
5985 The difference between N and the number of marks cleared is returned."
5986   (interactive "p")
5987   (gnus-summary-mark-forward (- n) " "))
5988
5989 (defun gnus-summary-delete-marked-as-read ()
5990   "Delete lines that are marked as read."
5991   (interactive)
5992   (if gnus-newsgroup-unreads
5993       (let ((buffer-read-only nil))
5994         (save-excursion
5995           (goto-char (point-min))
5996           ;; Fix by Jim Sisolak <sisolak@trans4.neep.wisc.edu>.
5997           (delete-matching-lines "^[DK]"))
5998         ;; Adjust point.
5999         (if (eobp)
6000             (gnus-summary-prev-subject 1)
6001           (gnus-summary-position-cursor)))
6002     ;; It is not so good idea to make the buffer empty.
6003     (message "All articles are marked as read")))
6004
6005 (defun gnus-summary-delete-marked-with (marks)
6006   "Delete lines that are marked with MARKS (e.g. \"DK\")."
6007   (interactive "sMarks: ")
6008   (let ((buffer-read-only nil))
6009     (save-excursion
6010       (goto-char (point-min))
6011       (delete-matching-lines (concat "^[" marks "]")))
6012     ;; Adjust point.
6013     (or (zerop (buffer-size))
6014         (if (eobp)
6015             (gnus-summary-prev-subject 1)
6016           (gnus-summary-position-cursor)))))
6017
6018 (defun gnus-summary-show-all-interesting ()
6019   "Display all the hidden articles that are marked as interesting."
6020   (interactive)
6021   (let ((int gnus-newsgroup-interesting-subjects)
6022         (buffer-read-only nil))
6023     (if (not int)
6024         (error "No interesting articles hidden."))
6025     (goto-char (point-min))
6026     (save-excursion
6027       (while int
6028         (insert (cdr (car int)))
6029         (setq int (cdr int))))
6030     (gnus-summary-position-cursor)
6031     (setq gnus-newsgroup-interesting-subjects nil)))
6032
6033 (defun gnus-summary-catchup (all &optional quietly)
6034   "Mark all articles not marked as unread in this newsgroup as read.
6035 If prefix argument ALL is non-nil, all articles are marked as read."
6036   (interactive "P")
6037   (if (or quietly
6038           (not gnus-interactive-catchup) ;Without confirmation?
6039           gnus-expert-user
6040           (y-or-n-p
6041            (if all
6042                "Do you really want to mark everything as read? "
6043              "Delete all articles not marked as unread? ")))
6044       (let ((unmarked
6045              (gnus-set-difference gnus-newsgroup-unreads
6046                                   (if (not all) gnus-newsgroup-marked))))
6047         (message "")                    ;Erase "Yes or No" question.
6048         ;; Hidden thread subtrees must be searched for, too.
6049         (gnus-summary-show-all-threads)
6050         (while unmarked
6051           (gnus-summary-mark-as-read (car unmarked) "C")
6052           (setq unmarked (cdr unmarked))
6053           ))
6054     ))
6055
6056 (defun gnus-summary-catchup-to-here ()
6057   "Mark all unticked articles before the current one as read."
6058   (interactive)
6059   (beginning-of-line)
6060   (let ((current (gnus-summary-article-number)))
6061     (goto-char (point-min))
6062     (while (not (= (gnus-summary-article-number) current))
6063       (beginning-of-line)
6064       (if (/= ?- (following-char))
6065           (gnus-summary-mark-as-read))
6066       (gnus-summary-next-subject 1))))
6067
6068 (defun gnus-summary-catchup-all (&optional quietly)
6069   "Mark all articles in this newsgroup as read."
6070   (interactive)
6071   (gnus-summary-catchup t quietly))
6072
6073 ;; Thread-based commands.
6074
6075 (defun gnus-summary-toggle-threads (arg)
6076   "Toggle showing conversation threads.
6077 If ARG is positive number, turn showing conversation threads on."
6078   (interactive "P")
6079   (let ((current (gnus-summary-article-number)))
6080     (setq gnus-show-threads
6081           (if (null arg) (not gnus-show-threads)
6082             (> (prefix-numeric-value arg) 0)))
6083     (gnus-summary-prepare)
6084     (gnus-summary-goto-subject current)))
6085
6086 (defun gnus-summary-show-all-threads ()
6087   "Show all threads."
6088   (interactive)
6089   (if gnus-show-threads
6090       (save-excursion
6091         (let ((buffer-read-only nil))
6092           (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)))))
6093
6094 (defun gnus-summary-show-thread ()
6095   "Show thread subtrees."
6096   (interactive)
6097   (if gnus-show-threads
6098       (save-excursion
6099         (let ((buffer-read-only nil))
6100           (subst-char-in-region 
6101            (progn (beginning-of-line) (point))
6102            (progn (end-of-line) (point)) ?\^M ?\n t)))))
6103
6104 (defun gnus-summary-hide-all-threads ()
6105   "Hide all thread subtrees."
6106   (interactive)
6107   (if gnus-show-threads
6108       (save-excursion
6109         (goto-char (point-min))
6110         (gnus-summary-hide-thread)
6111         (while (gnus-summary-search-forward)
6112           (gnus-summary-hide-thread)))))
6113
6114 (defun gnus-summary-hide-thread ()
6115   "Hide thread subtrees."
6116   (interactive)
6117   (if gnus-show-threads
6118       (save-excursion
6119         (let ((buffer-read-only nil)
6120               (start (point))
6121               (level (gnus-summary-thread-level))
6122               (end (point)))
6123           ;; Go forward until either the buffer ends or the subthread
6124           ;; ends. 
6125           (while (and (= 0 (forward-line 1))
6126                       (> (gnus-summary-thread-level) level))
6127             (setq end (point)))
6128           (subst-char-in-region start end ?\n ?\^M t)))))
6129
6130 (defun gnus-summary-go-to-next-thread (&optional previous)
6131   "Go to the same level (or less) next thread.
6132 If PREVIOUS is non-nil, go to previous thread instead."
6133   (let ((level (gnus-summary-thread-level))
6134         (start (point))
6135         beg end)
6136     (if previous 
6137         (progn
6138           (re-search-backward " 0[\n\r]" nil t)
6139           (setq end (point))
6140           (if (not 
6141                (and (re-search-backward " 0[\n\r]" nil t)
6142                     (re-search-forward (format " %s[\n\r]" level) end t)))
6143               (goto-char start)))
6144       (if (not (and (re-search-forward " 0[\n\r]" nil t)
6145                     (setq beg (point))
6146                     (re-search-forward " 0[\n\r]" nil t)
6147                     (setq end (point))
6148                     (goto-char beg)
6149                     (re-search-forward (format " %s[\n\r]" level) nil t)))
6150           (goto-char start)))
6151     (/= (point) start)))
6152
6153 (defun gnus-summary-next-thread (n)
6154   "Go to the same level next N'th thread.
6155 If N is negative, search backward instead.
6156 Returns the difference between N and the number of skips actually
6157 done."
6158   (interactive "p")
6159   (let ((backward (< n 0))
6160         (n (abs n)))
6161   (while (and (> n 0)
6162               (gnus-summary-go-to-next-thread backward))
6163     (setq n (1- n)))
6164   (gnus-summary-position-cursor)
6165   (if (/= 0 n) (message "No more threads" ))
6166   n))
6167
6168 (defun gnus-summary-prev-thread (n)
6169   "Go to the same level previous N'th thread.
6170 Returns the difference between N and the number of skips actually
6171 done."
6172   (interactive "p")
6173   (gnus-summary-next-thread (- n)))
6174
6175 (defun gnus-summary-go-down-thread (&optional up same)
6176   "Go down one level in the current thread.
6177 If UP is non-nil, go up instead.
6178 If SAME is non-nil, also move to articles of the same level."
6179   (let ((level (gnus-summary-thread-level))
6180         (start (point))
6181         (level-diff (if up -1 1))
6182         l)
6183     (if (not (and (= 0 (forward-line level-diff))
6184                   (or (= (+ level level-diff) 
6185                          (setq l (gnus-summary-thread-level)))
6186                       (and same (= level l)))))
6187         (goto-char start))
6188     (/= start (point))))
6189
6190 (defun gnus-summary-down-thread (n)
6191   "Go down thread N steps.
6192 If N is negative, go up instead.
6193 Returns the difference between N and how many steps down that were
6194 taken."
6195   (interactive "p")
6196   (let ((up (< n 0))
6197         (n (abs n)))
6198   (while (and (> n 0)
6199               (gnus-summary-go-down-thread up))
6200     (setq n (1- n)))
6201   (gnus-summary-position-cursor)
6202   (if (/= 0 n) (message "Can't go further" ))
6203   n))
6204
6205 (defun gnus-summary-up-thread (n)
6206   "Go up thread N steps.
6207 If N is negative, go up instead.
6208 Returns the difference between N and how many steps down that were
6209 taken."
6210   (interactive "p")
6211   (gnus-summary-down-thread (- n)))
6212
6213 (defun gnus-summary-kill-thread (unmark)
6214   "Mark articles under current thread as read.
6215 If the prefix argument is positive, remove any kinds of marks.
6216 If the prefix argument is negative, tick articles instead."
6217   (interactive "P")
6218   (if unmark
6219       (setq unmark (prefix-numeric-value unmark)))
6220   (let ((killing t)
6221         (level (gnus-summary-thread-level)))
6222     (save-excursion
6223       (while killing
6224         ;; Mark the article...
6225         (cond ((null unmark) (gnus-summary-mark-as-read nil "K"))
6226               ((> unmark 0) (gnus-summary-tick-article nil t))
6227               (t (gnus-summary-tick-article)))
6228         ;; ...and go forward until either the buffer ends or the subtree
6229         ;; ends. 
6230         (if (not (and (= 0 (forward-line 1))
6231                       (> (gnus-summary-thread-level) level)))
6232             (setq killing nil))))
6233     ;; Hide killed subtrees.
6234     (and (null unmark)
6235          gnus-thread-hide-killed
6236          (gnus-summary-hide-thread))
6237     ;; If marked as read, go to next unread subject.
6238     (if (null unmark)
6239         ;; Go to next unread subject.
6240         (gnus-summary-next-subject 1 t)))
6241   (gnus-set-mode-line 'summary))
6242
6243 ;; Summary sorting commands
6244
6245 (defun gnus-summary-sort-by-number (reverse)
6246   "Sort Summary buffer by article number.
6247 Argument REVERSE means reverse order."
6248   (interactive "P")
6249   (gnus-summary-keysort-summary
6250    (function <)
6251    (lambda (a)
6252      (header-number a))
6253    reverse
6254    ))
6255
6256 (defun gnus-summary-sort-by-author (reverse)
6257   "Sort Summary buffer by author name alphabetically.
6258 If case-fold-search is non-nil, case of letters is ignored.
6259 Argument REVERSE means reverse order."
6260   (interactive "P")
6261   (gnus-summary-keysort-summary
6262    (function string-lessp)
6263    (lambda (a)
6264      (if case-fold-search
6265          (downcase (header-from a))
6266        (header-from a)))
6267    reverse
6268    ))
6269
6270 (defun gnus-summary-sort-by-subject (reverse)
6271   "Sort Summary buffer by subject alphabetically. `Re:'s are ignored.
6272 If case-fold-search is non-nil, case of letters is ignored.
6273 Argument REVERSE means reverse order."
6274   (interactive "P")
6275   (gnus-summary-keysort-summary
6276    (function string-lessp)
6277    (lambda (a)
6278      (if case-fold-search
6279          (downcase (gnus-simplify-subject (header-subject a) 're-only))
6280        (gnus-simplify-subject (header-subject a) 're-only)))
6281    reverse
6282    ))
6283
6284 (defun gnus-summary-sort-by-date (reverse)
6285   "Sort Summary buffer by date.
6286 Argument REVERSE means reverse order."
6287   (interactive "P")
6288   (gnus-summary-keysort-summary
6289    (function string-lessp)
6290    (lambda (a)
6291      (gnus-sortable-date (header-date a)))
6292    reverse
6293    ))
6294
6295 (defun gnus-summary-keysort-summary (predicate key &optional reverse)
6296   "Sort Summary buffer by PREDICATE using a value passed by KEY.
6297 Optional argument REVERSE means reverse order."
6298   (let ((current (gnus-summary-article-number)))
6299     (gnus-keysort-headers predicate key reverse)
6300     (gnus-summary-prepare)
6301     (gnus-summary-goto-subject current)
6302     ))
6303
6304 (defun gnus-summary-sort-summary (predicate &optional reverse)
6305   "Sort Summary buffer by PREDICATE.
6306 Optional argument REVERSE means reverse order."
6307   (let ((current (gnus-summary-article-number)))
6308     (gnus-sort-headers predicate reverse)
6309     (gnus-summary-prepare)
6310     (gnus-summary-goto-subject current)
6311     ))
6312
6313 ;; Basic ideas by flee@cs.psu.edu (Felix Lee)
6314
6315 (defun gnus-keysort-headers (predicate key &optional reverse)
6316   "Sort current headers by PREDICATE using a value passed by KEY safely.
6317 *Safely* means C-g quitting is disabled during sort.
6318 Optional argument REVERSE means reverse order."
6319   (let ((inhibit-quit t))
6320     (setq gnus-newsgroup-headers
6321           (if reverse
6322               (nreverse
6323                (gnus-keysort (nreverse gnus-newsgroup-headers) predicate key))
6324             (gnus-keysort gnus-newsgroup-headers predicate key)))
6325     ))
6326
6327 (defun gnus-keysort (list predicate key)
6328   "Sort LIST by PREDICATE using a value passed by KEY."
6329   (mapcar (function cdr)
6330           (sort (mapcar (lambda (a) (cons (funcall key a) a)) list)
6331                 (lambda (a b)
6332                   (funcall predicate (car a) (car b))))))
6333
6334 (defun gnus-sort-headers (predicate &optional reverse)
6335   "Sort current headers by PREDICATE safely.
6336 *Safely* means C-g quitting is disabled during sort.
6337 Optional argument REVERSE means reverse order."
6338   (let ((inhibit-quit t))
6339     (setq gnus-newsgroup-headers
6340           (if reverse
6341               (nreverse (sort (nreverse gnus-newsgroup-headers) predicate))
6342             (sort gnus-newsgroup-headers predicate)))
6343     ))
6344
6345 (defun gnus-string-lessp (a b)
6346   "Return T if first arg string is less than second in lexicographic order.
6347 If case-fold-search is non-nil, case of letters is ignored."
6348   (if case-fold-search
6349       (string-lessp (downcase a) (downcase b))
6350     (string-lessp a b)))
6351
6352 (defun gnus-date-lessp (date1 date2)
6353   "Return T if DATE1 is earlyer than DATE2."
6354   (string-lessp (gnus-sortable-date date1)
6355                 (gnus-sortable-date date2)))
6356
6357 (defun gnus-sortable-date (date)
6358   "Make sortable string by string-lessp from DATE.
6359 Timezone package is used."
6360   (let* ((date   (timezone-fix-time date nil nil)) ;[Y M D H M S]
6361          (year   (aref date 0))
6362          (month  (aref date 1))
6363          (day    (aref date 2)))
6364     (timezone-make-sortable-date year month day 
6365                                  (timezone-make-time-string
6366                                   (aref date 3) (aref date 4) (aref date 5)))
6367     ))
6368
6369
6370 ;; Summary saving commands.
6371
6372 (defun gnus-summary-save-article (n)
6373   "Save the current article using the default saver function.
6374 If N is a positive number, save the N next articles.
6375 If N is a negative number, save the N previous articles.
6376 If N is nil and any articles have been marked with the process mark,
6377 save those articles instead.
6378 The variable `gnus-default-article-saver' specifies the saver function."
6379   (interactive "P")
6380   (let (articles process)
6381     (if (and n (numberp n))
6382         (let ((backward (< n 0))
6383               (n (abs n)))
6384           (save-excursion
6385             (while (and (> n 0)
6386                         (setq articles (cons (gnus-summary-article-number) 
6387                                              articles))
6388                         (gnus-summary-search-forward nil nil backward))
6389               (setq n (1- n))))
6390           (setq articles (sort articles (function <))))
6391       (if gnus-newsgroup-processable
6392           (progn
6393             (setq articles (setq gnus-newsgroup-processable
6394                                  (nreverse gnus-newsgroup-processable)))
6395             (setq process t))
6396         (setq articles (list (gnus-summary-article-number)))))
6397     (while articles
6398       (gnus-summary-display-article (car articles) t)
6399       (if (not gnus-save-all-headers)
6400           (gnus-article-hide-headers t))
6401       (if gnus-default-article-saver
6402           (funcall gnus-default-article-saver)
6403         (error "No default saver is defined."))
6404       (if process
6405           (gnus-summary-remove-process-mark (car articles)))
6406       (setq articles (cdr articles)))
6407     (if process (setq gnus-newsgroup-processable 
6408                       (nreverse gnus-newsgroup-processable)))
6409     n))
6410
6411 (defun gnus-summary-pipe-output (arg)
6412   "Pipe the current article to a subprocess.
6413 If N is a positive number, pipe the N next articles.
6414 If N is a negative number, pipe the N previous articles.
6415 If N is nil and any articles have been marked with the process mark,
6416 pipe those articles instead."
6417   (interactive "P")
6418   (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
6419     (gnus-summary-save-article arg)))
6420
6421 (defun gnus-summary-save-article-rmail (arg)
6422   "Append the current article to an Rmail file.
6423 If N is a positive number, save the N next articles.
6424 If N is a negative number, save the N previous articles.
6425 If N is nil and any articles have been marked with the process mark,
6426 save those articles instead."
6427   (interactive "P")
6428   (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail))
6429     (gnus-summary-save-article arg)))
6430
6431 (defun gnus-summary-save-in-rmail (&optional filename)
6432   "Append this article to Rmail file.
6433 Optional argument FILENAME specifies file name.
6434 Directory to save to is default to `gnus-article-save-directory' which
6435 is initialized from the SAVEDIR environment variable."
6436   (interactive)
6437   (let ((default-name
6438           (funcall gnus-rmail-save-name gnus-newsgroup-name
6439                    gnus-current-headers gnus-newsgroup-last-rmail)))
6440     (or filename
6441         (setq filename
6442               (read-file-name
6443                (concat "Save article in rmail file: (default "
6444                        (file-name-nondirectory default-name) ") ")
6445                (file-name-directory default-name)
6446                default-name)))
6447     (gnus-make-directory (file-name-directory filename))
6448     (gnus-eval-in-buffer-window 
6449      gnus-article-buffer
6450      (save-excursion
6451        (save-restriction
6452          (widen)
6453          (gnus-output-to-rmail filename))))
6454     ;; Remember the directory name to save articles.
6455     (setq gnus-newsgroup-last-rmail filename)))
6456
6457 (defun gnus-summary-save-in-mail (&optional filename)
6458   "Append this article to Unix mail file.
6459 Optional argument FILENAME specifies file name.
6460 Directory to save to is default to `gnus-article-save-directory' which
6461 is initialized from the SAVEDIR environment variable."
6462   (let ((default-name
6463           (funcall gnus-mail-save-name gnus-newsgroup-name
6464                    gnus-current-headers gnus-newsgroup-last-mail)))
6465     (or filename
6466         (setq filename
6467               (read-file-name
6468                (concat "Save article in Unix mail file: (default "
6469                        (file-name-nondirectory default-name) ") ")
6470                (file-name-directory default-name)
6471                default-name)))
6472     (setq filename
6473           (expand-file-name filename
6474                             (and default-name
6475                                  (file-name-directory default-name))))
6476     (gnus-make-directory (file-name-directory filename))
6477     (gnus-eval-in-buffer-window 
6478      gnus-article-buffer
6479      (save-excursion
6480        (save-restriction
6481          (widen)
6482          (if (and (file-readable-p filename) (rmail-file-p filename))
6483              (gnus-output-to-rmail filename)
6484            (rmail-output filename 1 t t)))))
6485     ;; Remember the directory name to save articles.
6486     (setq gnus-newsgroup-last-mail filename)))
6487
6488 (defun gnus-summary-save-in-file (&optional filename)
6489   "Append this article to file.
6490 Optional argument FILENAME specifies file name.
6491 Directory to save to is default to `gnus-article-save-directory' which
6492 is initialized from the SAVEDIR environment variable."
6493   (let ((default-name
6494           (funcall gnus-file-save-name gnus-newsgroup-name
6495                    gnus-current-headers gnus-newsgroup-last-file)))
6496     (or filename
6497         (setq filename
6498               (read-file-name
6499                (concat "Save article in file: (default "
6500                        (file-name-nondirectory default-name) ") ")
6501                (file-name-directory default-name)
6502                default-name)))
6503     (gnus-make-directory (file-name-directory filename))
6504     (gnus-eval-in-buffer-window 
6505      gnus-article-buffer
6506      (save-excursion
6507        (save-restriction
6508          (widen)
6509          (gnus-output-to-file filename))))
6510     ;; Remember the directory name to save articles.
6511     (setq gnus-newsgroup-last-file filename)))
6512
6513 (defun gnus-summary-save-in-pipe (&optional command)
6514   "Pipe this article to subprocess."
6515   (let ((command (read-string "Shell command on article: "
6516                               gnus-last-shell-command)))
6517     (if (string-equal command "")
6518         (setq command gnus-last-shell-command))
6519     (gnus-eval-in-buffer-window 
6520      gnus-article-buffer
6521      (save-restriction
6522        (widen)
6523        (shell-command-on-region (point-min) (point-max) command nil)))
6524     (setq gnus-last-shell-command command)))
6525
6526 ;; Summary killfile commands
6527
6528 (defun gnus-summary-edit-global-kill ()
6529   "Edit a global KILL file."
6530   (interactive)
6531   (setq gnus-current-kill-article (gnus-summary-article-number))
6532   (gnus-kill-file-edit-file nil)        ;Nil stands for global KILL file.
6533   (message
6534    (substitute-command-keys
6535     "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
6536
6537 (defun gnus-summary-edit-local-kill ()
6538   "Edit a local KILL file applied to the current newsgroup."
6539   (interactive)
6540   (setq gnus-current-kill-article (gnus-summary-article-number))
6541   (gnus-kill-file-edit-file gnus-newsgroup-name)
6542   (message
6543    (substitute-command-keys
6544     "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
6545
6546
6547 \f
6548 ;;;
6549 ;;; Gnus Article Mode
6550 ;;;
6551
6552 (if gnus-article-mode-map
6553     nil
6554   (setq gnus-article-mode-map (make-keymap))
6555   (suppress-keymap gnus-article-mode-map)
6556   (define-key gnus-article-mode-map " " 'gnus-article-next-page)
6557   (define-key gnus-article-mode-map "\177" 'gnus-article-prev-page)
6558   (define-key gnus-article-mode-map "r" 'gnus-article-refer-article)
6559   (define-key gnus-article-mode-map "h" 'gnus-article-show-summary)
6560   (define-key gnus-article-mode-map "s" 'gnus-article-show-summary)
6561   (define-key gnus-article-mode-map "m" 'gnus-article-mail)
6562   (define-key gnus-article-mode-map "M" 'gnus-article-mail-with-original)
6563   (define-key gnus-article-mode-map "?" 'gnus-article-describe-briefly)
6564   (define-key gnus-article-mode-map "\C-c\C-i" 'gnus-info-find-node))
6565
6566 (defun gnus-article-mode ()
6567   "Major mode for browsing through an article.
6568 All normal editing commands are switched off.
6569 The following commands are available:
6570
6571 \\<gnus-article-mode-map>
6572 \\[gnus-article-next-page]\t Scroll the article one page forwards
6573 \\[gnus-article-prev-page]\t Scroll the article one page backwards
6574 \\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
6575 \\[gnus-article-show-summary]\t Display the Summary buffer
6576 \\[gnus-article-mail]\t Send a reply to the address near point
6577 \\[gnus-article-mail-with-original]\t Send a reply to the address near point; include the original article
6578 \\[gnus-article-describe-briefly]\t Describe the current mode briefly
6579 \\[gnus-info-find-node]\t Go to the Gnus info node
6580
6581 "
6582   (interactive)
6583   (kill-all-local-variables)
6584   (setq mode-line-modified "--- ")
6585   (setq major-mode 'gnus-article-mode)
6586   (setq mode-name "Article")
6587   (make-local-variable 'minor-mode-alist)
6588   (or (assq 'gnus-show-mime minor-mode-alist)
6589       (setq minor-mode-alist
6590             (cons (list 'gnus-show-mime " MIME") minor-mode-alist)))
6591   (use-local-map gnus-article-mode-map)
6592   (make-local-variable 'page-delimiter)
6593   (setq page-delimiter gnus-page-delimiter)
6594   (make-local-variable 'mail-header-separator)
6595   (setq mail-header-separator "")       ;For caesar function.
6596   (buffer-disable-undo (current-buffer))
6597   (setq buffer-read-only t)             ;Disable modification
6598   (run-hooks 'gnus-article-mode-hook))
6599
6600 (defun gnus-article-setup-buffer ()
6601   "Initialize Article mode buffer."
6602   (or (get-buffer gnus-article-buffer)
6603       (save-excursion
6604         (set-buffer (get-buffer-create gnus-article-buffer))
6605         (gnus-add-current-to-buffer-list)
6606         (gnus-article-mode))
6607       ))
6608
6609 (defun gnus-request-article-this-buffer (article &optional group)
6610   "Get an article and insert it into this buffer."
6611   ;; Using `gnus-request-article' directly will insert the article into
6612   ;; `nntp-server-buffer' - so we'll save some time by not having to
6613   ;; copy it from the server buffer into the article buffer.
6614
6615   ;; We only request an article by message-id when we do not have the
6616   ;; headers for it, so we'll have to get those.
6617   (if (stringp article) (gnus-read-header article))
6618   ;; If the article number is negative, that means that this article
6619   ;; doesn't belong in this newsgroup (possibly), so we find its
6620   ;; message-id and request it by id instead of number.
6621   (if (and (numberp article) (< article 0))
6622       (save-excursion
6623         (set-buffer gnus-summary-buffer)
6624         (setq article 
6625               (header-id 
6626                (gnus-gethash (int-to-string article)
6627                              gnus-newsgroup-headers-hashtb-by-number)))))
6628   ;; Get the article and into the article buffer.
6629   (gnus-request-article article group (current-buffer)))
6630
6631 (defun gnus-read-header (id)
6632   "Read the headers of article ID and enter them into the Gnus system."
6633   (or gnus-newsgroup-headers-hashtb-by-number
6634       (gnus-make-headers-hashtable-by-number))
6635   (let (header)
6636     (if (not (setq header 
6637                    (car (if (let ((nntp-xover-is-evil t))
6638                               (gnus-retrieve-headers (list id) 
6639                                                      gnus-newsgroup-name))
6640                             (gnus-get-newsgroup-headers)))))
6641         nil
6642       (if (stringp id)
6643           (header-set-number header gnus-reffed-article-number))
6644       (setq gnus-newsgroup-headers (cons header gnus-newsgroup-headers))
6645       (gnus-sethash (int-to-string (header-number header)) header
6646                     gnus-newsgroup-headers-hashtb-by-number)
6647       (if (stringp id)
6648           (setq gnus-reffed-article-number (1- gnus-reffed-article-number)))
6649       (setq gnus-current-headers header)
6650       header)))
6651
6652 (defun gnus-article-prepare (article &optional all-headers header)
6653   "Prepare ARTICLE in Article mode buffer.
6654 ARTICLE can be either a article number or Message-ID.
6655 If ARTICLE is an id, HEADER should be the article headers.
6656 If ALL-HEADERS is non-nil, no headers are hidden."
6657   (save-excursion
6658     ;; Make sure we start are in a Summary buffer.
6659     (if (eq major-mode 'gnus-summary-mode)
6660         (setq gnus-summary-buffer (current-buffer))
6661       (set-buffer gnus-summary-buffer))
6662     ;; Make sure the connection to the server is alive.
6663     (if (not (gnus-server-opened gnus-current-select-method))
6664         (progn
6665           (gnus-check-news-server gnus-current-select-method)
6666           (gnus-request-group gnus-newsgroup-name t)))
6667     (or gnus-newsgroup-headers-hashtb-by-number
6668         (gnus-make-headers-hashtable-by-number))
6669     (let* ((article (if header (header-number header) article))
6670            (summary-buffer (current-buffer))
6671            (internal-hook gnus-article-internal-prepare-hook)
6672            (bookmark (cdr (assq article gnus-newsgroup-bookmarks)))
6673            (group gnus-newsgroup-name))
6674       (save-excursion
6675         (set-buffer gnus-article-buffer)
6676         (let ((buffer-read-only nil))
6677           (erase-buffer)
6678           (prog1
6679               (if (gnus-request-article-this-buffer article group)
6680                   (progn 
6681                     ;; gnus-have-all-headers must be either T or NIL.
6682                     (setq gnus-have-all-headers
6683                           (not (not (or all-headers gnus-show-all-headers))))
6684                     (if (and (numberp article)
6685                              (not (eq article gnus-current-article)))
6686                         ;; Seems like a new article has been selected.
6687                         (progn
6688                           ;; `gnus-current-article' must be an article number.
6689                           (save-excursion
6690                             (set-buffer summary-buffer)
6691                             (setq gnus-last-article gnus-current-article)
6692                             (setq gnus-current-article article)
6693                             (setq gnus-current-headers
6694                                   (gnus-get-header-by-number 
6695                                    gnus-current-article))
6696                             (setq gnus-article-current 
6697                                   (cons gnus-newsgroup-name 
6698                                         (header-number gnus-current-headers)))
6699                             (run-hooks 'gnus-mark-article-hook)
6700                             ;; Set the global newsgroup variables here.
6701                             ;; Suggested by Jim Sisolak
6702                             ;; <sisolak@trans4.neep.wisc.edu>.
6703                             (gnus-set-global-variables))))
6704                     ;; Hooks for getting information from the article.
6705                     ;; This hook must be called before being narrowed.
6706                     (run-hooks 'internal-hook)
6707                     (run-hooks 'gnus-article-prepare-hook)
6708                     ;; Decode MIME message.
6709                     (if (and gnus-show-mime
6710                              (gnus-fetch-field "Mime-Version"))
6711                         (funcall gnus-show-mime-method))
6712                     ;; Perform the article display hooks.
6713                     (let ((buffer-read-only nil))
6714                       (run-hooks 'gnus-article-display-hook))
6715                     ;; Do page break.
6716                     (goto-char (point-min))
6717                     (if gnus-break-pages
6718                         (gnus-narrow-to-page))
6719                     (gnus-set-mode-line 'article)
6720                     t)
6721                 ;; There is no such article.
6722                 (if (numberp article)
6723                     (gnus-summary-mark-as-read article))
6724                 (ding) 
6725                 (message "No such article (may be canceled)")
6726                 nil)
6727             (goto-char 1)
6728             (if bookmark
6729                 (progn
6730                   (message "Moved to bookmark.")
6731                   (search-forward "\n\n" nil t)
6732                   (forward-line bookmark)))
6733             (set-window-start 
6734              (get-buffer-window gnus-article-buffer) (point))))))))
6735
6736 (defun gnus-set-global-variables ()
6737   ;; Set the global equivalents of the Summary buffer-local variables
6738   ;; to the latest values they had. These reflect the Summary buffer
6739   ;; that was in action when the last article was fetched.
6740   (let ((name gnus-newsgroup-name)
6741         (marked gnus-newsgroup-marked)
6742         (unread gnus-newsgroup-unreads)
6743         (headers gnus-current-headers))
6744     (save-excursion
6745       (set-buffer gnus-group-buffer)
6746       (setq gnus-newsgroup-name name)
6747       (setq gnus-newsgroup-marked marked)
6748       (setq gnus-newsgroup-unreads unread)
6749       (setq gnus-current-headers headers))))
6750
6751 (defun gnus-article-show-all-headers ()
6752   "Show all article headers in Article mode buffer."
6753   (save-excursion 
6754     (setq gnus-have-all-headers t)
6755     (gnus-article-setup-buffer)
6756     (set-buffer gnus-article-buffer)
6757     (let ((buffer-read-only nil))
6758       (remove-text-properties 1 (point-max) '(invisible t)))))
6759
6760 (defun gnus-article-hide-headers-if-wanted ()
6761   "Hide unwanted headers if `gnus-have-all-headers' is nil.
6762 Provided for backwards compatability."
6763   (or gnus-have-all-headers
6764       (gnus-article-hide-headers)))
6765
6766 (defun gnus-article-hide-headers (&optional delete)
6767   "Hide unwanted headers and possibly sort them as well."
6768   (save-excursion
6769     (save-restriction
6770       (let ((sorted gnus-sorted-header-list)
6771             (buffer-read-only nil)
6772             want want-list beg want-l)
6773         ;; First we narrow to just the headers.
6774         (widen)
6775         (goto-char 1)
6776         (narrow-to-region 
6777          1 (progn (search-forward "\n\n" nil t) (forward-line -1) (point)))
6778         ;; Then we use the two regular expressions
6779         ;; `gnus-ignored-headers' and `gnus-visible-headers' to
6780         ;; select which header lines is to remain visible in the
6781         ;; article buffer.
6782         (goto-char 1)
6783         (while (re-search-forward "^[^ \t]*:" nil t)
6784           (beginning-of-line)
6785           ;; We add the headers we want to keep to a list and delete
6786           ;; them from the buffer.
6787           (if (or (and (stringp gnus-visible-headers)
6788                        (looking-at gnus-visible-headers))
6789                   (and (not (stringp gnus-visible-headers))
6790                        (stringp gnus-ignored-headers)
6791                        (not (looking-at gnus-ignored-headers))))
6792               (progn
6793                 (setq beg (point))
6794                 (forward-line 1)
6795                 ;; Be sure to get multi-line headers...
6796                 (re-search-forward "^[^ \t]*:" nil t)
6797                 (beginning-of-line)
6798                 (setq want-list 
6799                       (cons (buffer-substring beg (point)) want-list))
6800                 (delete-region beg (point))
6801                 (goto-char beg))
6802             (forward-line 1)))
6803         ;; Next we perform the sorting by looking at
6804         ;; `gnus-sorted-header-list'. 
6805         (goto-char 1)
6806         (while (and sorted want-list)
6807           (setq want-l want-list)
6808           (while (and want-l
6809                       (not (string-match (car sorted) (car want-l))))
6810             (setq want-l (cdr want-l)))
6811           (if want-l 
6812               (progn
6813                 (insert (car want-l))
6814                 (setq want-list (delq (car want-l) want-list))))
6815           (setq sorted (cdr sorted)))
6816         ;; Any headers that were not matched by the sorted list we
6817         ;; just tack on the end of the visible header list.
6818         (while want-list
6819           (insert (car want-list))
6820           (setq want-list (cdr want-list)))
6821         ;; And finally we make the unwanted headers invisible.
6822         (if delete
6823             (delete-region (point) (point-max))
6824           (set-text-properties (point) (point-max) '(invisible t)))))))
6825
6826 (defun gnus-article-hide-signature ()
6827   "Hides the signature in an article.
6828 It does this by hiding everyting after "^-- *$", which is what all
6829 signatures should be preceded by. Note that this may mean that parts
6830 of an article may disappear if the article has such a line in the
6831 middle of the text."
6832   (save-excursion
6833     (goto-char (point-max))
6834     (if (re-search-backward "^-- *$" nil t)
6835         (progn
6836           (add-text-properties (point) (point-max) '(invisible t))))))
6837
6838 (defun gnus-article-hide-citation ()
6839   "Hide all cited text.
6840 This function uses the famous, extremely intelligent \"shoot in foot\"
6841 algorithm - which is simply deleting all lines that start with
6842 \">\". Your mileage may vary. If you come up with anything better,
6843 please do mail it to me."
6844   (save-excursion
6845     (goto-char 1)
6846     (search-forward "\n\n" nil t)
6847     (while (not (eobp))
6848       (if (looking-at ">")
6849           (add-text-properties 
6850            (point) (save-excursion (forward-line 1) (point))
6851            '(invisible t)))
6852       (forward-line 1))))
6853
6854 ;; Article savers.
6855
6856 (defun gnus-output-to-rmail (file-name)
6857   "Append the current article to an Rmail file named FILE-NAME."
6858   (require 'rmail)
6859   ;; Most of these codes are borrowed from rmailout.el.
6860   (setq file-name (expand-file-name file-name))
6861   (setq rmail-default-rmail-file file-name)
6862   (let ((artbuf (current-buffer))
6863         (tmpbuf (get-buffer-create " *Gnus-output*")))
6864     (save-excursion
6865       (or (get-file-buffer file-name)
6866           (file-exists-p file-name)
6867           (if (yes-or-no-p
6868                (concat "\"" file-name "\" does not exist, create it? "))
6869               (let ((file-buffer (create-file-buffer file-name)))
6870                 (save-excursion
6871                   (set-buffer file-buffer)
6872                   (rmail-insert-rmail-file-header)
6873                   (let ((require-final-newline nil))
6874                     (write-region (point-min) (point-max) file-name t 1)))
6875                 (kill-buffer file-buffer))
6876             (error "Output file does not exist")))
6877       (set-buffer tmpbuf)
6878       (buffer-disable-undo (current-buffer))
6879       (erase-buffer)
6880       (insert-buffer-substring artbuf)
6881       (gnus-convert-article-to-rmail)
6882       ;; Decide whether to append to a file or to an Emacs buffer.
6883       (let ((outbuf (get-file-buffer file-name)))
6884         (if (not outbuf)
6885             (append-to-file (point-min) (point-max) file-name)
6886           ;; File has been visited, in buffer OUTBUF.
6887           (set-buffer outbuf)
6888           (let ((buffer-read-only nil)
6889                 (msg (and (boundp 'rmail-current-message)
6890                           rmail-current-message)))
6891             ;; If MSG is non-nil, buffer is in RMAIL mode.
6892             (if msg
6893                 (progn (widen)
6894                        (narrow-to-region (point-max) (point-max))))
6895             (insert-buffer-substring tmpbuf)
6896             (if msg
6897                 (progn
6898                   (goto-char (point-min))
6899                   (widen)
6900                   (search-backward "\^_")
6901                   (narrow-to-region (point) (point-max))
6902                   (goto-char (1+ (point-min)))
6903                   (rmail-count-new-messages t)
6904                   (rmail-show-message msg))))))
6905       )
6906     (kill-buffer tmpbuf)
6907     ))
6908
6909 (defun gnus-output-to-file (file-name)
6910   "Append the current article to a file named FILE-NAME."
6911   (setq file-name (expand-file-name file-name))
6912   (let ((artbuf (current-buffer))
6913         (tmpbuf (get-buffer-create " *Gnus-output*")))
6914     (save-excursion
6915       (set-buffer tmpbuf)
6916       (buffer-disable-undo (current-buffer))
6917       (erase-buffer)
6918       (insert-buffer-substring artbuf)
6919       ;; Append newline at end of the buffer as separator, and then
6920       ;; save it to file.
6921       (goto-char (point-max))
6922       (insert "\n")
6923       (append-to-file (point-min) (point-max) file-name))
6924     (kill-buffer tmpbuf)
6925     ))
6926
6927 (defun gnus-convert-article-to-rmail ()
6928   "Convert article in current buffer to Rmail message format."
6929   (let ((buffer-read-only nil))
6930     ;; Convert article directly into Babyl format.
6931     ;; Suggested by Rob Austein <sra@lcs.mit.edu>
6932     (goto-char (point-min))
6933     (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
6934     (while (search-forward "\n\^_" nil t) ;single char
6935       (replace-match "\n^_"))           ;2 chars: "^" and "_"
6936     (goto-char (point-max))
6937     (insert "\^_")))
6938
6939 (defun gnus-narrow-to-page (&optional arg)
6940   "Make text outside current page invisible except for page delimiter.
6941 A numeric arg specifies to move forward or backward by that many pages,
6942 thus showing a page other than the one point was originally in."
6943   (interactive "P")
6944   (setq arg (if arg (prefix-numeric-value arg) 0))
6945   (save-excursion
6946     (forward-page -1)                   ;Beginning of current page.
6947     (widen)
6948     (if (> arg 0)
6949         (forward-page arg)
6950       (if (< arg 0)
6951           (forward-page (1- arg))))
6952     ;; Find the end of the page.
6953     (forward-page)
6954     ;; If we stopped due to end of buffer, stay there.
6955     ;; If we stopped after a page delimiter, put end of restriction
6956     ;; at the beginning of that line.
6957     ;; These are commented out.
6958     ;;    (if (save-excursion (beginning-of-line)
6959     ;;                  (looking-at page-delimiter))
6960     ;;  (beginning-of-line))
6961     (narrow-to-region (point)
6962                       (progn
6963                         ;; Find the top of the page.
6964                         (forward-page -1)
6965                         ;; If we found beginning of buffer, stay there.
6966                         ;; If extra text follows page delimiter on same line,
6967                         ;; include it.
6968                         ;; Otherwise, show text starting with following line.
6969                         (if (and (eolp) (not (bobp)))
6970                             (forward-line 1))
6971                         (point)))
6972     ))
6973
6974 (defun gnus-gmt-to-local ()
6975   "Rewrite Date: field described in GMT to local in current buffer.
6976 The variable gnus-local-timezone is used for local time zone.
6977 Intended to be used with gnus-article-prepare-hook."
6978   (save-excursion
6979     (save-restriction
6980       (widen)
6981       (goto-char (point-min))
6982       (narrow-to-region (point-min)
6983                         (progn (search-forward "\n\n" nil 'move) (point)))
6984       (goto-char (point-min))
6985       (if (re-search-forward "^Date:[ \t]\\(.*\\)$" nil t)
6986           (let ((buffer-read-only nil)
6987                 (date (buffer-substring (match-beginning 1) (match-end 1))))
6988             (delete-region (match-beginning 1) (match-end 1))
6989             (insert
6990              (timezone-make-date-arpa-standard date nil gnus-local-timezone))
6991             ))
6992       )))
6993
6994
6995 ;; Article mode commands
6996
6997 (defun gnus-article-next-page (lines)
6998   "Show next page of current article.
6999 If end of article, return non-nil. Otherwise return nil.
7000 Argument LINES specifies lines to be scrolled up."
7001   (interactive "P")
7002   (move-to-window-line -1)
7003   ;; Fixed by enami@ptgd.sony.co.jp (enami tsugutomo)
7004   (if (save-excursion
7005         (end-of-line)
7006         (and (pos-visible-in-window-p)  ;Not continuation line.
7007              (eobp)))
7008       ;; Nothing in this page.
7009       (if (or (not gnus-break-pages)
7010               (save-excursion
7011                 (save-restriction
7012                   (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer?
7013           t                             ;Nothing more.
7014         (gnus-narrow-to-page 1)         ;Go to next page.
7015         nil
7016         )
7017     ;; More in this page.
7018     (condition-case ()
7019         (scroll-up lines)
7020       (end-of-buffer
7021        ;; Long lines may cause an end-of-buffer error.
7022        (goto-char (point-max))))
7023     nil
7024     ))
7025
7026 (defun gnus-article-prev-page (lines)
7027   "Show previous page of current article.
7028 Argument LINES specifies lines to be scrolled down."
7029   (interactive "P")
7030   (move-to-window-line 0)
7031   (if (and gnus-break-pages
7032            (bobp)
7033            (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
7034       (progn
7035         (gnus-narrow-to-page -1) ;Go to previous page.
7036         (goto-char (point-max))
7037         (recenter -1))
7038     (scroll-down lines)))
7039
7040 (defun gnus-article-next-digest (nth)
7041   "Move to head of NTH next digested message.
7042 Set mark at end of digested message."
7043   ;; Stop page breaking in digest mode.
7044   (widen)
7045   (end-of-line)
7046   ;; Skip NTH - 1 digest.
7047   ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
7048   ;; Digest separator is customizable.
7049   ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
7050   (while (and (> nth 1)
7051               (re-search-forward gnus-digest-separator nil 'move))
7052     (setq nth (1- nth)))
7053   (if (re-search-forward gnus-digest-separator nil t)
7054       (let ((begin (point)))
7055         ;; Search for end of this message.
7056         (end-of-line)
7057         (if (re-search-forward gnus-digest-separator nil t)
7058             (progn
7059               (search-backward "\n\n")  ;This may be incorrect.
7060               (forward-line 1))
7061           (goto-char (point-max)))
7062         (push-mark)                     ;Set mark at end of digested message.
7063         (goto-char begin)
7064         (beginning-of-line)
7065         ;; Show From: and Subject: fields.
7066         (recenter 1))
7067     (message "End of message")
7068     ))
7069
7070 (defun gnus-article-prev-digest (nth)
7071   "Move to head of NTH previous digested message."
7072   ;; Stop page breaking in digest mode.
7073   (widen)
7074   (beginning-of-line)
7075   ;; Skip NTH - 1 digest.
7076   ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
7077   ;; Digest separator is customizable.
7078   ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
7079   (while (and (> nth 1)
7080               (re-search-backward gnus-digest-separator nil 'move))
7081     (setq nth (1- nth)))
7082   (if (re-search-backward gnus-digest-separator nil t)
7083       (let ((begin (point)))
7084         ;; Search for end of this message.
7085         (end-of-line)
7086         (if (re-search-forward gnus-digest-separator nil t)
7087             (progn
7088               (search-backward "\n\n")  ;This may be incorrect.
7089               (forward-line 1))
7090           (goto-char (point-max)))
7091         (push-mark)                     ;Set mark at end of digested message.
7092         (goto-char begin)
7093         ;; Show From: and Subject: fields.
7094         (recenter 1))
7095     (goto-char (point-min))
7096     (message "Top of message")
7097     ))
7098
7099 (defun gnus-article-refer-article ()
7100   "Read article specified by message-id around point."
7101   (interactive)
7102   (save-window-excursion
7103     (save-excursion
7104       (re-search-forward ">" nil t)     ;Move point to end of "<....>".
7105       (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
7106           (let ((message-id
7107                  (buffer-substring (match-beginning 1) (match-end 1))))
7108             (set-buffer gnus-summary-buffer)
7109             (gnus-summary-refer-article message-id))
7110         (error "No references around point"))
7111       )))
7112
7113 (defun gnus-article-mail (yank)
7114   "Send a reply to the address near point.
7115 If YANK is non-nil, include the original article."
7116   (interactive "P")
7117   (let ((address 
7118          (buffer-substring
7119           (save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
7120           (save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
7121     (and address
7122          (progn
7123            (switch-to-buffer gnus-summary-buffer)
7124            (funcall gnus-mail-reply-method yank address)))))
7125
7126 (defun gnus-article-mail-with-original ()
7127   "Send a reply to the address near point and include the original article."
7128   (interactive)
7129   (gnus-article-mail 'yank))
7130
7131 (defun gnus-article-show-summary ()
7132   "Reconfigure windows to show Summary buffer."
7133   (interactive)
7134   (gnus-configure-windows 'article)
7135   (pop-to-buffer gnus-summary-buffer)
7136   (gnus-summary-goto-subject gnus-current-article))
7137
7138 (defun gnus-article-describe-briefly ()
7139   "Describe Article mode commands briefly."
7140   (interactive)
7141   (message
7142    (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")))
7143
7144 ;; caesar-region written by phr@prep.ai.mit.edu  Nov 86
7145 ;; Modified by tower@prep Nov 86
7146 ;; Modified by umerin@flab.flab.Fujitsu.JUNET for ROT47.
7147
7148 (defun gnus-caesar-region (&optional n)
7149   "Caesar rotation of region by N, default 13, for decrypting netnews.
7150 ROT47 will be performed for Japanese text in any case."
7151   (interactive (if current-prefix-arg   ; Was there a prefix arg?
7152                    (list (prefix-numeric-value current-prefix-arg))
7153                  (list nil)))
7154   (cond ((not (numberp n)) (setq n 13))
7155         (t (setq n (mod n 26))))        ;canonicalize N
7156   (if (not (zerop n))           ; no action needed for a rot of 0
7157       (progn
7158         (if (or (not (boundp 'caesar-translate-table))
7159                 (not caesar-translate-table)
7160                 (/= (aref caesar-translate-table ?a) (+ ?a n)))
7161             (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
7162               (message "Building caesar-translate-table...")
7163               (setq caesar-translate-table (make-vector 256 0))
7164               (while (< i 256)
7165                 (aset caesar-translate-table i i)
7166                 (setq i (1+ i)))
7167               (setq lower (concat lower lower) upper (upcase lower) i 0)
7168               (while (< i 26)
7169                 (aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
7170                 (aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
7171                 (setq i (1+ i)))
7172               ;; ROT47 for Japanese text.
7173               ;; Thanks to ichikawa@flab.fujitsu.junet.
7174               (setq i 161)
7175               (let ((t1 (logior ?O 128))
7176                     (t2 (logior ?! 128))
7177                     (t3 (logior ?~ 128)))
7178                 (while (< i 256)
7179                   (aset caesar-translate-table i
7180                         (let ((v (aref caesar-translate-table i)))
7181                           (if (<= v t1) (if (< v t2) v (+ v 47))
7182                             (if (<= v t3) (- v 47) v))))
7183                   (setq i (1+ i))))
7184               (message "Building caesar-translate-table... done")))
7185         (let ((from (region-beginning))
7186               (to (region-end))
7187               (i 0) str len)
7188           (setq str (buffer-substring from to))
7189           (setq len (length str))
7190           (while (< i len)
7191             (aset str i (aref caesar-translate-table (aref str i)))
7192             (setq i (1+ i)))
7193           (goto-char from)
7194           (delete-region from to)
7195           (insert str)))))
7196
7197 \f
7198 ;;;
7199 ;;; Gnus KILL-File Mode
7200 ;;;
7201
7202 (if gnus-kill-file-mode-map
7203     nil
7204   (setq gnus-kill-file-mode-map (copy-keymap emacs-lisp-mode-map))
7205   (define-key gnus-kill-file-mode-map "\C-c\C-k\C-s" 'gnus-kill-file-kill-by-subject)
7206   (define-key gnus-kill-file-mode-map "\C-c\C-k\C-a" 'gnus-kill-file-kill-by-author)
7207   (define-key gnus-kill-file-mode-map "\C-c\C-a" 'gnus-kill-file-apply-buffer)
7208   (define-key gnus-kill-file-mode-map "\C-c\C-e" 'gnus-kill-file-apply-last-sexp)
7209   (define-key gnus-kill-file-mode-map "\C-c\C-c" 'gnus-kill-file-exit)
7210   (define-key gnus-kill-file-mode-map "\C-c\C-i" 'gnus-info-find-node))
7211
7212 (defun gnus-kill-file-mode ()
7213   "Major mode for editing KILL file.
7214
7215 In addition to Emacs-Lisp Mode, the following commands are available:
7216
7217 \\[gnus-kill-file-kill-by-subject]      Insert KILL command for current subject.
7218 \\[gnus-kill-file-kill-by-author]       Insert KILL command for current author.
7219 \\[gnus-kill-file-apply-buffer] Apply current buffer to selected newsgroup.
7220 \\[gnus-kill-file-apply-last-sexp]      Apply sexp before point to selected newsgroup.
7221 \\[gnus-kill-file-exit] Save file and exit editing KILL file.
7222 \\[gnus-info-find-node] Read Info about KILL file.
7223
7224   A KILL file contains Lisp expressions to be applied to a selected
7225 newsgroup.  The purpose is to mark articles as read on the basis of
7226 some set of regexps.  A global KILL file is applied to every newsgroup,
7227 and a local KILL file is applied to a specified newsgroup.  Since a
7228 global KILL file is applied to every newsgroup, for better performance
7229 use a local one.
7230
7231   A KILL file can contain any kind of Emacs Lisp expressions expected
7232 to be evaluated in the Summary buffer.  Writing Lisp programs for this
7233 purpose is not so easy because the internal working of Gnus must be
7234 well-known.  For this reason, Gnus provides a general function which
7235 does this easily for non-Lisp programmers.
7236
7237   The `gnus-kill' function executes commands available in Summary Mode
7238 by their key sequences. `gnus-kill' should be called with FIELD,
7239 REGEXP and optional COMMAND and ALL.  FIELD is a string representing
7240 the header field or an empty string.  If FIELD is an empty string, the
7241 entire article body is searched for.  REGEXP is a string which is
7242 compared with FIELD value. COMMAND is a string representing a valid
7243 key sequence in Summary mode or Lisp expression. COMMAND defaults to
7244 '(gnus-summary-mark-as-read nil \"X\").  Make sure that COMMAND is
7245 executed in the Summary buffer.  If the second optional argument ALL
7246 is non-nil, the COMMAND is applied to articles which are already
7247 marked as read or unread.  Articles which are marked are skipped over
7248 by default.
7249
7250   For example, if you want to mark articles of which subjects contain
7251 the string `AI' as read, a possible KILL file may look like:
7252
7253         (gnus-kill \"Subject\" \"AI\")
7254
7255   If you want to mark articles with `D' instead of `X', you can use
7256 the following expression:
7257
7258         (gnus-kill \"Subject\" \"AI\" \"d\")
7259
7260 In this example it is assumed that the command
7261 `gnus-summary-mark-as-read-forward' is assigned to `d' in Summary Mode.
7262
7263   It is possible to delete unnecessary headers which are marked with
7264 `X' in a KILL file as follows:
7265
7266         (gnus-expunge \"X\")
7267
7268   If the Summary buffer is empty after applying KILL files, Gnus will
7269 exit the selected newsgroup normally.  If headers which are marked
7270 with `D' are deleted in a KILL file, it is impossible to read articles
7271 which are marked as read in the previous Gnus sessions.  Marks other
7272 than `D' should be used for articles which should really be deleted.
7273
7274 Entry to this mode calls emacs-lisp-mode-hook and
7275 gnus-kill-file-mode-hook with no arguments, if that value is non-nil."
7276   (interactive)
7277   (kill-all-local-variables)
7278   (use-local-map gnus-kill-file-mode-map)
7279   (set-syntax-table emacs-lisp-mode-syntax-table)
7280   (setq major-mode 'gnus-kill-file-mode)
7281   (setq mode-name "KILL-File")
7282   (lisp-mode-variables nil)
7283   (run-hooks 'emacs-lisp-mode-hook 'gnus-kill-file-mode-hook))
7284
7285 (defun gnus-kill-file-edit-file (newsgroup)
7286   "Begin editing a KILL file of NEWSGROUP.
7287 If NEWSGROUP is nil, the global KILL file is selected."
7288   (interactive "sNewsgroup: ")
7289   (let ((file (gnus-newsgroup-kill-file newsgroup)))
7290     (gnus-make-directory (file-name-directory file))
7291     ;; Save current window configuration if this is first invocation.
7292     (or (and (get-file-buffer file)
7293              (get-buffer-window (get-file-buffer file)))
7294         (setq gnus-winconf-kill-file (current-window-configuration)))
7295     ;; Hack windows.
7296     (let ((buffer (find-file-noselect file)))
7297       (cond ((get-buffer-window buffer)
7298              (pop-to-buffer buffer))
7299             ((eq major-mode 'gnus-group-mode)
7300              (gnus-configure-windows '(1 0 0)) ;Take all windows.
7301              (pop-to-buffer gnus-group-buffer)
7302              (let ((gnus-summary-buffer buffer))
7303                (gnus-configure-windows '(1 1 0)) ;Split into two.
7304                (pop-to-buffer buffer)))
7305             ((eq major-mode 'gnus-summary-mode)
7306              (gnus-configure-windows 'article)
7307              (pop-to-buffer gnus-article-buffer)
7308              (bury-buffer gnus-article-buffer)
7309              (switch-to-buffer buffer))
7310             (t                          ;No good rules.
7311              (find-file-other-window file))
7312             ))
7313     (gnus-kill-file-mode)
7314     ))
7315
7316 (defun gnus-kill-set-kill-buffer ()
7317   (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
7318     (if (get-buffer file)
7319         (set-buffer (get-buffer file))
7320       (set-buffer (find-file-noselect file)))))
7321
7322 (defun gnus-kill-save-kill-buffer ()
7323   (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
7324     (if (get-buffer file)
7325         (save-excursion
7326           (set-buffer (get-buffer file))
7327           (save-buffer)
7328           (kill-buffer (current-buffer))))))
7329
7330 (defun gnus-article-fetch-field (field)
7331   (save-excursion
7332     (set-buffer gnus-article-buffer)
7333     (save-restriction
7334       (widen)
7335       (goto-char 1)
7336       (narrow-to-region 1 (save-excursion 
7337                             (search-forward "\n\n" nil t) (point)))
7338       (goto-char 1)
7339       (prog1
7340           (mail-fetch-field field)
7341         (widen)))))
7342
7343 (defun gnus-kill-file-enter-kill (field regexp)
7344   (save-excursion
7345     (gnus-kill-set-kill-buffer)
7346     (insert (format "(gnus-kill \"%s\" \"%s\") ; ttl=5\n" 
7347                     field regexp))))
7348   
7349 (defun gnus-kill-file-kill-by-subject ()
7350   "Insert KILL command for current subject."
7351   (interactive)
7352   (gnus-kill-file-enter-kill 
7353    "Subject" (regexp-quote (header-subject gnus-current-headers))))
7354
7355 (defun gnus-kill-file-kill-by-author ()
7356   "Insert KILL command for current author."
7357   (interactive)
7358   (gnus-kill-file-enter-kill 
7359    "From" (regexp-quote (header-from gnus-current-headers))))
7360
7361 (defun gnus-kill-file-kill-by-thread ()
7362   "Insert KILL command for current thread."
7363   (interactive)
7364   (gnus-kill-file-enter-kill 
7365    "References" (concat ".*" (regexp-quote 
7366                               (header-id gnus-current-headers)))))
7367
7368 (defun gnus-kill-file-kill-by-xref ()
7369   "Insert KILL command for current xref."
7370   (interactive)
7371   (let ((xref (header-xref gnus-current-headers))
7372         (start 0)
7373         (string "")
7374         group)
7375     (if xref
7376         (progn
7377           (while (string-match " \\([a-zA-Z\.]\\):" xref start)
7378             (if (not (string= (setq group (substring (match-beginning 1) 
7379                                                      (match-end 1)))
7380                               gnus-newsgroup-name))
7381                 (setq string (concat string ".*" (regexp-quote group))))
7382             (setq start (match-end 0)))
7383           (gnus-kill-file-enter-kill 
7384            "Xref" string)))))
7385
7386 (defun gnus-kill-file-apply-buffer ()
7387   "Apply current buffer to current newsgroup."
7388   (interactive)
7389   (if (and gnus-current-kill-article
7390            (get-buffer gnus-summary-buffer))
7391       ;; Assume newsgroup is selected.
7392       (let ((string (concat "(progn \n" (buffer-string) "\n)" )))
7393         (save-excursion
7394           (save-window-excursion
7395             (pop-to-buffer gnus-summary-buffer)
7396             (eval (car (read-from-string string))))))
7397     (ding) (message "No newsgroup is selected.")))
7398
7399 (defun gnus-kill-file-apply-last-sexp ()
7400   "Apply sexp before point in current buffer to current newsgroup."
7401   (interactive)
7402   (if (and gnus-current-kill-article
7403            (get-buffer gnus-summary-buffer))
7404       ;; Assume newsgroup is selected.
7405       (let ((string
7406              (buffer-substring
7407               (save-excursion (forward-sexp -1) (point)) (point))))
7408         (save-excursion
7409           (save-window-excursion
7410             (pop-to-buffer gnus-summary-buffer)
7411             (eval (car (read-from-string string))))))
7412     (ding) (message "No newsgroup is selected.")))
7413
7414 (defun gnus-kill-file-exit ()
7415   "Save a KILL file, then return to the previous buffer."
7416   (interactive)
7417   (save-buffer)
7418   (let ((killbuf (current-buffer)))
7419     ;; We don't want to return to Article buffer.
7420     (and (get-buffer gnus-article-buffer)
7421          (bury-buffer (get-buffer gnus-article-buffer)))
7422     ;; Delete the KILL file windows.
7423     (delete-windows-on killbuf)
7424     ;; Restore last window configuration if available.
7425     (and gnus-winconf-kill-file
7426          (set-window-configuration gnus-winconf-kill-file))
7427     (setq gnus-winconf-kill-file nil)
7428     ;; Kill the KILL file buffer.  Suggested by tale@pawl.rpi.edu.
7429     (kill-buffer killbuf)))
7430
7431 ;; Basic ideas by emv@math.lsa.umich.edu (Edward Vielmetti)
7432
7433 (defun gnus-batch-kill ()
7434   "Run batched KILL.
7435 Usage: emacs -batch -l gnus -f gnus-batch-kill NEWSGROUP ..."
7436   (if (not noninteractive)
7437       (error "gnus-batch-kill is to be used only with -batch"))
7438   (let* ((group nil)
7439          (subscribed nil)
7440          (newsrc nil)
7441          (yes-and-no
7442           (gnus-parse-n-options
7443            (apply (function concat)
7444                   (mapcar (lambda (g) (concat g " "))
7445                           command-line-args-left))))
7446          (yes (car yes-and-no))
7447          (no  (cdr yes-and-no))
7448          ;; Disable verbose message.
7449          (gnus-novice-user nil)
7450          (gnus-large-newsgroup nil))
7451     ;; Eat all arguments.
7452     (setq command-line-args-left nil)
7453     ;; Startup Gnus.
7454     (gnus)
7455     ;; Apply kills to specified newsgroups in command line arguments.
7456     (setq newsrc (copy-sequence gnus-newsrc-assoc))
7457     (while newsrc
7458       (setq group (car (car newsrc)))
7459       (setq subscribed (nth 1 (car newsrc)))
7460       (setq newsrc (cdr newsrc))
7461       (if (and subscribed
7462                (not (zerop (car (gnus-gethash group gnus-newsrc-hashtb))))
7463                (if yes
7464                    (string-match yes group) t)
7465                (or (null no)
7466                    (not (string-match no group))))
7467           (progn
7468             (gnus-summary-read-group group nil t)
7469             (if (eq (current-buffer) (get-buffer gnus-summary-buffer))
7470                 (gnus-summary-exit t))
7471             ))
7472       )
7473     ;; Finally, exit Emacs.
7474     (set-buffer gnus-group-buffer)
7475     (gnus-group-exit)
7476     ))
7477
7478 ;; For KILL files
7479
7480 (defun gnus-apply-kill-file ()
7481   "Apply KILL file to the current newsgroup."
7482   ;; Apply the global KILL file.
7483   (load (gnus-newsgroup-kill-file nil) t nil t)
7484   ;; And then apply the local KILL file.
7485   (load (gnus-newsgroup-kill-file gnus-newsgroup-name) t nil t))
7486
7487 (defun gnus-Newsgroup-kill-file (newsgroup)
7488   "Return the name of a KILL file of NEWSGROUP.
7489 If NEWSGROUP is nil, return the global KILL file instead."
7490   (cond ((or (null newsgroup)
7491              (string-equal newsgroup ""))
7492          ;; The global KILL file is placed at top of the directory.
7493          (expand-file-name gnus-kill-file-name
7494                            (or gnus-kill-files-directory "~/News")))
7495         (gnus-use-long-file-name
7496          ;; Append ".KILL" to capitalized newsgroup name.
7497          (expand-file-name (concat (gnus-capitalize-newsgroup newsgroup)
7498                                    "." gnus-kill-file-name)
7499                            (or gnus-kill-files-directory "~/News")))
7500         (t
7501          ;; Place "KILL" under the hierarchical directory.
7502          (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
7503                                    "/" gnus-kill-file-name)
7504                            (or gnus-kill-files-directory "~/News")))
7505         ))
7506
7507 (defun gnus-newsgroup-kill-file (newsgroup)
7508   "Return the name of a KILL file of NEWSGROUP.
7509 If NEWSGROUP is nil, return the global KILL file instead."
7510   (cond ((or (null newsgroup)
7511              (string-equal newsgroup ""))
7512          ;; The global KILL file is placed at top of the directory.
7513          (expand-file-name gnus-kill-file-name
7514                            (or gnus-kill-files-directory "~/News")))
7515         (gnus-use-long-file-name
7516          ;; Append ".KILL" to newsgroup name.
7517          (expand-file-name (concat newsgroup "." gnus-kill-file-name)
7518                            (or gnus-kill-files-directory "~/News")))
7519         (t
7520          ;; Place "KILL" under the hierarchical directory.
7521          (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
7522                                    "/" gnus-kill-file-name)
7523                            (or gnus-kill-files-directory "~/News")))
7524         ))
7525
7526
7527 (defalias 'gnus-expunge 'gnus-summary-delete-marked-with)
7528
7529 (defun gnus-kill (field regexp &optional command all)
7530   "If FIELD of an article matches REGEXP, execute COMMAND.
7531 Optional 1st argument COMMAND is default to
7532         (gnus-summary-mark-as-read nil \"X\").
7533 If optional 2nd argument ALL is non-nil, articles marked are also applied to.
7534 If FIELD is an empty string (or nil), entire article body is searched for.
7535 COMMAND must be a lisp expression or a string representing a key sequence."
7536   ;; We don't want to change current point nor window configuration.
7537   (save-excursion
7538     (save-window-excursion
7539       ;; Selected window must be Summary buffer to execute keyboard
7540       ;; macros correctly. See command_loop_1.
7541       (switch-to-buffer gnus-summary-buffer 'norecord)
7542       (goto-char (point-min))           ;From the beginning.
7543       (if (null command)
7544           (setq command '(gnus-summary-mark-as-read nil "X")))
7545       (gnus-execute field regexp command nil (not all))
7546       )))
7547
7548 (defun gnus-execute (field regexp form &optional backward ignore-marked)
7549   "If FIELD of article header matches REGEXP, execute lisp FORM (or a string).
7550 If FIELD is an empty string (or nil), entire article body is searched for.
7551 If optional 1st argument BACKWARD is non-nil, do backward instead.
7552 If optional 2nd argument IGNORE-MARKED is non-nil, articles which are
7553 marked as read or ticked are ignored."
7554   (let ((function nil)
7555         (header nil)
7556         (article nil))
7557     (if (string-equal field "")
7558         (setq field nil))
7559     (if (null field)
7560         nil
7561       (or (stringp field)
7562           (setq field (symbol-name field)))
7563       ;; Get access function of header filed.
7564       (setq function (intern-soft (concat "gnus-header-" (downcase field))))
7565       (if (and function (fboundp function))
7566           (setq function (symbol-function function))
7567         (error "Unknown header field: \"%s\"" field)))
7568     ;; Make FORM funcallable.
7569     (if (and (listp form) (not (eq (car form) 'lambda)))
7570         (setq form (list 'lambda nil form)))
7571     ;; Starting from the current article.
7572     (while (gnus-summary-search-subject backward ignore-marked nil)
7573       (setq article (gnus-summary-article-number))
7574       (or (gnus-member-of-range article gnus-newsgroup-killed)
7575           (and ignore-marked
7576                ;; Articles marked as read, ticked and interesting
7577                ;; should be ignored. 
7578                (or (not (memq article gnus-newsgroup-unreads))
7579                    (memq article gnus-newsgroup-marked)
7580                    (memq article gnus-newsgroup-interesting)))
7581           (gnus-execute-1 function regexp form)))))
7582
7583 (defun gnus-execute-1 (function regexp form)
7584   (save-excursion
7585     ;; The point of Summary buffer must be saved during execution.
7586     (let ((article (gnus-summary-article-number)))
7587       (if (null article)
7588           nil                           ;Nothing to do.
7589         (if function
7590             ;; Compare with header field.
7591             (let ((header (gnus-get-header-by-number article))
7592                   (value nil))
7593               (and header
7594                    (progn
7595                      (setq value (funcall function header))
7596                      ;; Number (Lines:) or symbol must be converted to string.
7597                      (or (stringp value)
7598                          (setq value (prin1-to-string value)))
7599                      (string-match regexp value))
7600                    (if (stringp form)   ;Keyboard macro.
7601                        (execute-kbd-macro form)
7602                      (funcall form))))
7603           ;; Search article body.
7604           (let ((gnus-current-article nil) ;Save article pointer.
7605                 (gnus-last-article nil)
7606                 (gnus-break-pages nil)  ;No need to break pages.
7607                 (gnus-mark-article-hook nil)) ;Inhibit marking as read.
7608             (message "Searching for article: %d..." article)
7609             (gnus-article-setup-buffer)
7610             (gnus-article-prepare article t)
7611             (if (save-excursion
7612                   (set-buffer gnus-article-buffer)
7613                   (goto-char (point-min))
7614                   (re-search-forward regexp nil t))
7615                 (if (stringp form)      ;Keyboard macro.
7616                     (execute-kbd-macro form)
7617                   (funcall form))))
7618           ))
7619       )))
7620
7621
7622 \f
7623 ;;; 
7624 ;;; Gnus Posting Functions
7625 ;;;
7626
7627 (defvar gnus-organization-file "/usr/lib/news/organization"
7628   "*Local news organization file.")
7629
7630 (defvar gnus-post-news-buffer "*post-news*")
7631 (defvar gnus-winconf-post-news nil)
7632
7633 (autoload 'news-reply-mode "rnewspost")
7634
7635 ;;; Post news commands of Gnus Group Mode and Summary Mode
7636
7637 (defun gnus-group-post-news ()
7638   "Post an article."
7639   (interactive)
7640   ;; Save window configuration.
7641   (setq gnus-winconf-post-news (current-window-configuration))
7642   ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
7643   (or gnus-newsgroup-name (setq gnus-newsgroup-name (gnus-group-group-name)))
7644   (unwind-protect
7645       (gnus-post-news 'post nil)
7646     (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
7647              (not (zerop (buffer-size))))
7648         ;; Restore last window configuration.
7649         (set-window-configuration gnus-winconf-post-news)))
7650   ;; We don't want to return to Summary buffer nor Article buffer later.
7651   (if (get-buffer gnus-summary-buffer)
7652       (bury-buffer gnus-summary-buffer))
7653   (if (get-buffer gnus-article-buffer)
7654       (bury-buffer gnus-article-buffer)))
7655
7656 (defun gnus-summary-post-news ()
7657   "Post an article."
7658   (interactive)
7659   ;; Save window configuration.
7660   (setq gnus-winconf-post-news (current-window-configuration))
7661   (unwind-protect
7662       (gnus-post-news 'post gnus-newsgroup-name)
7663     (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
7664              (not (zerop (buffer-size))))
7665         ;; Restore last window configuration.
7666         (set-window-configuration gnus-winconf-post-news)))
7667   ;; We don't want to return to Article buffer later.
7668   (if (get-buffer gnus-article-buffer)
7669       (bury-buffer gnus-article-buffer)))
7670
7671 (defun gnus-summary-followup (yank)
7672   "Compose a followup to an article.
7673 If prefix argument YANK is non-nil, original article is yanked automatically."
7674   (interactive "P")
7675   (gnus-summary-select-article t)
7676   (let ((headers gnus-current-headers)
7677         (gnus-newsgroup-name gnus-newsgroup-name))
7678     ;; Check Followup-To: poster.
7679     (set-buffer gnus-article-buffer)
7680     (if (and gnus-use-followup-to
7681              (string-equal "poster" (gnus-fetch-field "followup-to"))
7682              (or (not (eq gnus-use-followup-to t))
7683                  (not (y-or-n-p 
7684                        "Do you want to ignore `Followup-To: poster'? "))))
7685         ;; Mail to the poster.  Gnus is now RFC1036 compliant.
7686         (gnus-summary-reply yank)
7687       ;; Save window configuration.
7688       (setq gnus-winconf-post-news (current-window-configuration))
7689       (unwind-protect
7690           (gnus-post-news 'followup headers gnus-article-buffer yank)
7691         (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
7692                  (not (zerop (buffer-size))))
7693             ;; Restore last window configuration.
7694             (set-window-configuration gnus-winconf-post-news)))
7695       ;; We don't want to return to Article buffer later.
7696       (bury-buffer gnus-article-buffer))))
7697
7698 (defun gnus-summary-followup-with-original ()
7699   "Compose a followup to an article and include the original article."
7700   (interactive)
7701   (gnus-summary-followup t))
7702
7703 (defun gnus-summary-cancel-article ()
7704   "Cancel an article you posted."
7705   (interactive)
7706   (gnus-summary-select-article t)
7707   (gnus-eval-in-buffer-window gnus-article-buffer
7708     (gnus-cancel-news)))
7709
7710 (defun gnus-summary-supersede-article ()
7711   "Compose an article that will supersede a previous article.
7712 This is done simply by taking the old article and adding a Supersedes
7713 header line with the old Message-ID."
7714   (interactive)
7715   (if (not
7716        (string-equal
7717         (downcase (mail-strip-quoted-names 
7718                    (header-from gnus-current-headers)))
7719         (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
7720       (error "This article is not yours."))
7721   (gnus-summary-select-article t)
7722   (save-excursion
7723     (set-buffer gnus-article-buffer)
7724     (let ((buffer-read-only nil))
7725       (goto-char (point-min))
7726       (search-forward "\n\n" nil t)
7727       (if (not (re-search-backward "^Message-ID: " nil t))
7728           (error "No Message-ID in this article"))))
7729   (if (gnus-post-news 'post gnus-newsgroup-name)
7730       (progn
7731         (erase-buffer)
7732         (insert-buffer gnus-article-buffer)
7733         (goto-char (point-min))
7734         (search-forward "\n\n" nil t)
7735         (if (not (re-search-backward "^Message-ID: " nil t))
7736             (error "No Message-ID in this article")
7737           (replace-match "Supersedes: "))
7738         (search-forward "\n\n")
7739         (forward-line -1)
7740         (insert mail-header-separator))))
7741
7742 \f
7743 ;;; Post a News using NNTP
7744
7745 ;;;###autoload
7746 (fset 'sendnews 'gnus-post-news)
7747
7748 ;;;###autoload
7749 (fset 'postnews 'gnus-post-news)
7750
7751 (defun gnus-post-news (method &optional header article-buffer yank)
7752   "Begin editing a new USENET news article to be posted.
7753 Type \\[describe-mode] in the buffer to get a list of commands."
7754   (interactive)
7755   (if (or (not gnus-novice-user)
7756           gnus-expert-user
7757           (not (eq 'post 
7758                    (nth 1 (assoc 
7759                            (format "%s" (car gnus-current-select-method))
7760                            gnus-valid-select-methods))))
7761           (y-or-n-p "Are you sure you want to post to all of USENET? "))
7762       (let ((sumart (if (eq method 'followup)
7763                         (save-excursion
7764                           (set-buffer gnus-summary-buffer)
7765                           (cons (current-buffer) gnus-current-article))))
7766             post-buf)
7767         (if (and gnus-interactive-post
7768                  (not gnus-expert-user)
7769                  (eq method 'post)
7770                  (not header))
7771             (setq header 
7772                   (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
7773         (setq mail-reply-buffer article-buffer)
7774         (setq gnus-post-news-buffer 
7775               (setq post-buf
7776                     (gnus-request-post-buffer method header article-buffer)))
7777         (if (eq method 'post)
7778             (progn
7779               (delete-other-windows)
7780               (switch-to-buffer post-buf))
7781           (delete-other-windows)
7782           (if (not yank)
7783               (progn
7784                 (switch-to-buffer article-buffer)
7785                 (pop-to-buffer post-buf))
7786             (switch-to-buffer post-buf)))
7787         (gnus-overload-functions)
7788         (make-local-variable 'gnus-article-reply)
7789         (setq gnus-article-reply sumart)
7790         ;; Handle author copy using FCC field.
7791         (if gnus-author-copy
7792             (progn
7793               (mail-position-on-field "FCC")
7794               (insert gnus-author-copy)))
7795         (goto-char (point-min))
7796         (if (and (eq method 'post) (not header))
7797             (end-of-line)
7798           (search-forward (concat "\n" mail-header-separator "\n"))
7799           (if yank 
7800               (progn
7801                 (run-hooks 'news-reply-header-hook)
7802                 (mail-yank-original nil)))
7803           (if gnus-post-prepare-function
7804               (funcall gnus-post-prepare-function 
7805                        (if (stringp header) header gnus-newsgroup-name))))))
7806   (message "")
7807   t)
7808
7809 (defun gnus-inews-news ()
7810   "Send a news message."
7811   (interactive)
7812   (let* ((case-fold-search nil)
7813          (server-running (gnus-server-opened gnus-select-method))
7814          (reply gnus-article-reply))
7815     (save-excursion
7816       ;; Connect to default NNTP server if necessary.
7817       ;; Suggested by yuki@flab.fujitsu.junet.
7818       (gnus-start-news-server)          ;Use default server.
7819       ;; NNTP server must be opened before current buffer is modified.
7820       (widen)
7821       (goto-char (point-min))
7822       (run-hooks 'news-inews-hook)
7823       (save-restriction
7824         (narrow-to-region
7825          (point-min)
7826          (progn
7827            (goto-char (point-min))
7828            (search-forward (concat "\n" mail-header-separator "\n"))
7829            (point)))
7830
7831          ;; Correct newsgroups field: change sequence of spaces to comma and 
7832          ;; eliminate spaces around commas.  Eliminate imbedded line breaks.
7833          (goto-char (point-min))
7834          (if (search-forward-regexp "^Newsgroups: +" nil t)
7835              (save-restriction
7836                (narrow-to-region
7837                 (point)
7838                 (if (re-search-forward "^[^ \t]" nil 'end)
7839                     (match-beginning 0)
7840                   (point-max)))
7841                (goto-char (point-min))
7842                (replace-regexp "\n[ \t]+" " ") ;No line breaks (too confusing)
7843                (goto-char (point-min))
7844                (replace-regexp "[ \t\n]*,[ \t\n]*\\|[ \t]+" ",")
7845              ))
7846
7847          ;; Mail the message too if To: or Cc: exists.
7848          (if (or (mail-fetch-field "to" nil t)
7849                  (mail-fetch-field "cc" nil t))
7850              (if gnus-mail-send-method
7851                  (progn
7852                    (message "Sending via mail...")
7853                    (widen)
7854                    (funcall gnus-mail-send-method)
7855                    (message "Sending via mail... done"))
7856                (ding)
7857                (message "No mailer defined.  To: and/or Cc: fields ignored.")
7858                (sit-for 1))))
7859
7860       ;; Send to NNTP server. 
7861       (message "Posting to USENET...")
7862       (if (gnus-inews-article)
7863           (progn
7864             (message "Posting to USENET... done")
7865             (if (and reply
7866                      (get-buffer (car reply))
7867                      (buffer-name (car reply)))
7868                 (progn
7869                   (save-excursion
7870                     (set-buffer gnus-summary-buffer)
7871                     (gnus-summary-mark-article-as-replied 
7872                      (cdr reply))))))
7873         ;; We cannot signal an error.
7874         (ding) (message "Article rejected: %s" 
7875                         (gnus-status-message gnus-select-method)))
7876       (set-buffer-modified-p nil))
7877     ;; If NNTP server is opened by gnus-inews-news, close it by myself.
7878     (or server-running
7879         (gnus-close-server gnus-current-select-method))
7880     (and (fboundp 'bury-buffer) (bury-buffer))
7881     ;; Restore last window configuration.
7882     (and gnus-winconf-post-news
7883          (set-window-configuration gnus-winconf-post-news))
7884     (setq gnus-winconf-post-news nil)
7885     ))
7886
7887 (defun gnus-cancel-news ()
7888   "Cancel an article you posted."
7889   (interactive)
7890   (if (yes-or-no-p "Do you really want to cancel this article? ")
7891       (let ((from nil)
7892             (newsgroups nil)
7893             (message-id nil)
7894             (distribution nil))
7895         (save-excursion
7896           ;; Get header info. from original article.
7897           (save-restriction
7898             (gnus-article-show-all-headers)
7899             (goto-char (point-min))
7900             (search-forward "\n\n" nil 'move)
7901             (narrow-to-region (point-min) (point))
7902             (setq from (mail-fetch-field "from"))
7903             (setq newsgroups (mail-fetch-field "newsgroups"))
7904             (setq message-id (mail-fetch-field "message-id"))
7905             (setq distribution (mail-fetch-field "distribution")))
7906           ;; Verify if the article is absolutely user's by comparing
7907           ;; user id with value of its From: field.
7908           (if (not
7909                (string-equal
7910                 (downcase (mail-strip-quoted-names from))
7911                 (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
7912               (progn
7913                 (ding) (message "This article is not yours."))
7914             ;; Make control article.
7915             (set-buffer (get-buffer-create " *Gnus-canceling*"))
7916             (buffer-disable-undo (current-buffer))
7917             (erase-buffer)
7918             (insert "Newsgroups: " newsgroups "\n"
7919                     "Subject: cancel " message-id "\n"
7920                     "Control: cancel " message-id "\n"
7921                     mail-header-separator "\n"
7922                     )
7923             ;; Send the control article to NNTP server.
7924             (message "Canceling your article...")
7925             (if (gnus-inews-article)
7926                 (message "Canceling your article... done")
7927               (ding) (message "Failed to cancel your article"))
7928             ;; Kill the article buffer.
7929             (kill-buffer (current-buffer))
7930             )))
7931     ))
7932
7933 \f
7934 ;;; Lowlevel inews interface
7935
7936 (defun gnus-inews-article ()
7937   "Post an article in current buffer using NNTP protocol."
7938   (let ((artbuf (current-buffer))
7939         (tmpbuf (get-buffer-create " *Gnus-posting*")))
7940     (widen)
7941     (goto-char (point-max))
7942     ;; require a newline at the end for inews to append .signature to
7943     (or (= (preceding-char) ?\n)
7944         (insert ?\n))
7945     ;; Prepare article headers.  All message body such as signature
7946     ;; must be inserted before Lines: field is prepared.
7947     (save-restriction
7948       (goto-char (point-min))
7949       (narrow-to-region 
7950        (point-min) 
7951        (save-excursion
7952          (search-forward (concat "\n" mail-header-separator "\n")) 
7953          (forward-line -1) 
7954          (point)))
7955       (gnus-inews-insert-headers)
7956       (widen))
7957     (save-excursion
7958       (set-buffer tmpbuf)
7959       (buffer-disable-undo (current-buffer))
7960       (erase-buffer)
7961       (insert-buffer-substring artbuf)
7962       ;; Remove the header separator.
7963       (goto-char (point-min))
7964       (search-forward (concat "\n" mail-header-separator "\n"))
7965       (replace-match "\n\n")
7966       ;; This hook may insert a signature.
7967       (run-hooks 'gnus-prepare-article-hook)
7968       ;; Run final inews hooks.  This hook may do FCC.
7969       ;; The article must be saved before being posted because
7970       ;; `gnus-request-post' modifies the buffer.
7971       (run-hooks 'gnus-inews-article-hook)
7972       ;; Post an article to NNTP server.
7973       ;; Return NIL if post failed.
7974       (prog1
7975           (gnus-request-post gnus-current-select-method)
7976         (kill-buffer (current-buffer)))
7977       )))
7978
7979 (defun gnus-inews-insert-headers ()
7980   "Prepare article headers.
7981 Fields already prepared in the buffer are not modified.
7982 Fields in `gnus-required-headers' will be generated."
7983   (save-excursion
7984     (let ((date (gnus-inews-date))
7985           (message-id (gnus-inews-message-id))
7986           (organization (gnus-inews-organization)))
7987       (goto-char (point-min))
7988       (and (memq 'Path gnus-required-headers)
7989            (or (mail-fetch-field "path")
7990                (gnus-insert-end "Path: " (gnus-inews-path) "\n")))
7991       (and (memq 'From gnus-required-headers)
7992            (or (mail-fetch-field "from")
7993                (gnus-insert-end "From: " (gnus-inews-user-name) "\n")))
7994       ;; If there is no subject, make Subject: field.
7995       (and (memq 'Subject gnus-required-headers)
7996            (or (mail-fetch-field "subject")
7997                (gnus-insert-end "Subject: \n")))
7998       ;; If there is no newsgroups, make Newsgroups: field.
7999       (and (memq 'Newsgroups gnus-required-headers)
8000            (or (mail-fetch-field "newsgroups")
8001                (gnus-insert-end "Newsgroups: \n")))
8002       (and message-id
8003            (memq 'Message-ID gnus-required-headers)
8004            (progn
8005              (if (mail-fetch-field "message-id")
8006                  (progn
8007                    (goto-char (point-min))
8008                    (re-search-forward "^Message-ID" nil t)
8009                    (delete-region (progn (beginning-of-line) (point))
8010                                   (progn (forward-line 1) (point)))))
8011              (gnus-insert-end "Message-ID: " message-id "\n")))
8012       (and date
8013            (memq 'Date gnus-required-headers)
8014            (or (mail-fetch-field "date")
8015                (gnus-insert-end "Date: " date "\n")))
8016       ;; Optional fields in RFC977 and RFC1036
8017       (and organization
8018            (memq 'Organization gnus-required-headers)
8019            (or (mail-fetch-field "organization")
8020                (let ((begin (point-max))
8021                      (fill-column 79)
8022                      (fill-prefix "\t"))
8023                  (gnus-insert-end "Organization: " organization "\n")
8024                  (fill-region-as-paragraph begin (point-max)))))
8025       (and (memq 'Distribution gnus-required-headers)
8026            (or (mail-fetch-field "distribution")
8027                (gnus-insert-end "Distribution: \n")))
8028       (and (memq 'Lines gnus-required-headers)
8029            (or (mail-fetch-field "lines")
8030                (gnus-insert-end "Lines: " (gnus-inews-lines) "\n")))
8031       (and (memq 'X-Newsreader gnus-required-headers)
8032            (or (mail-fetch-field "x-newsreader")
8033                (gnus-insert-end "X-Newsreader: " gnus-version "\n")))
8034       )))
8035
8036
8037 (defun gnus-insert-end (&rest args)
8038   (save-excursion
8039     (goto-char (point-max))
8040     (apply 'insert args)))
8041
8042 (defun gnus-inews-insert-signature ()
8043   "Insert signature file in current article buffer.
8044 If there is a file named .signature-DISTRIBUTION. Set the variable to
8045 nil to prevent appending the signature file automatically.
8046 Signature file is specified by the variable gnus-signature-file."
8047   (save-excursion
8048     (save-restriction
8049       (let ((signature
8050              (if gnus-signature-file
8051                  (expand-file-name gnus-signature-file nil)))
8052             distribution)
8053         (goto-char (point-min))
8054         (search-forward "\n\n")
8055         (narrow-to-region (point-min) (point))
8056         (setq distribution (mail-fetch-field "distribution"))
8057         (widen)
8058         (if signature
8059             (progn
8060               ;; Insert signature.
8061               (if (file-exists-p signature)
8062                   (progn
8063                     (goto-char (point-max))
8064                     (insert "--\n")
8065                     (insert-file-contents signature)))
8066               ))))))
8067
8068 (defun gnus-inews-do-fcc ()
8069   "Process FCC: fields in current article buffer.
8070 Unless the first character of the field is `|', the article is saved
8071 to the specified file using the function specified by the variable
8072 gnus-author-copy-saver.  The default function rmail-output saves in
8073 Unix mailbox format.
8074 If the first character is `|', the contents of the article is send to
8075 a program specified by the rest of the value."
8076   (let ((fcc-list nil)
8077         (fcc-file nil)
8078         (case-fold-search t))           ;Should ignore case.
8079     (save-excursion
8080       (save-restriction
8081         (goto-char (point-min))
8082         (search-forward "\n\n")
8083         (narrow-to-region (point-min) (point))
8084         (goto-char (point-min))
8085         (while (re-search-forward "^FCC:[ \t]*" nil t)
8086           (setq fcc-list
8087                 (cons (buffer-substring
8088                        (point)
8089                        (progn
8090                          (end-of-line)
8091                          (skip-chars-backward " \t")
8092                          (point)))
8093                       fcc-list))
8094           (delete-region (match-beginning 0)
8095                          (progn (forward-line 1) (point))))
8096         ;; Process FCC operations.
8097         (widen)
8098         (while fcc-list
8099           (setq fcc-file (car fcc-list))
8100           (setq fcc-list (cdr fcc-list))
8101           (cond ((string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" fcc-file)
8102                  (let ((program (substring fcc-file
8103                                            (match-beginning 1) (match-end 1))))
8104                    ;; Suggested by yuki@flab.fujitsu.junet.
8105                    ;; Send article to named program.
8106                    (call-process-region (point-min) (point-max) shell-file-name
8107                                         nil nil nil "-c" program)
8108                    ))
8109                 (t
8110                  ;; Suggested by hyoko@flab.fujitsu.junet.
8111                  ;; Save article in Unix mail format by default.
8112                  (if (and gnus-author-copy-saver
8113                           (not (eq gnus-author-copy-saver 'rmail-output)))
8114                      (funcall gnus-author-copy-saver fcc-file)
8115                    (if (and (file-readable-p fcc-file) (rmail-file-p fcc-file))
8116                        (gnus-output-to-rmail fcc-file)
8117                      (rmail-output fcc-file 1 t t)))
8118                  ))
8119           )
8120         ))
8121     ))
8122
8123 (defun gnus-inews-path ()
8124   "Return uucp path."
8125   (let ((login-name (gnus-inews-login-name)))
8126     (cond ((null gnus-use-generic-path)
8127            (concat (nth 1 gnus-select-method) "!" login-name))
8128           ((stringp gnus-use-generic-path)
8129            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
8130            (concat gnus-use-generic-path "!" login-name))
8131           (t login-name))
8132     ))
8133
8134 (defun gnus-inews-user-name ()
8135   "Return user's network address as `NAME@DOMAIN (FULLNAME)'."
8136   (let ((full-name (gnus-inews-full-name)))
8137     (concat (or user-mail-address
8138                 (if (or gnus-user-login-name gnus-use-generic-from
8139                         gnus-local-domain (getenv "DOMAINNAME"))
8140                     (concat (gnus-inews-login-name) "@"
8141                             (gnus-inews-domain-name gnus-use-generic-from))
8142                   user-mail-address))
8143             ;; User's full name.
8144             (cond ((string-equal full-name "") "")
8145                   ((string-equal full-name "&") ;Unix hack.
8146                    (concat " (" (user-login-name) ")"))
8147                   (t
8148                    (concat " (" full-name ")")))
8149             )))
8150
8151 (defun gnus-inews-login-name ()
8152   "Return user login name.
8153 Got from the variable `gnus-user-login-name' and the function
8154 `user-login-name'."
8155   (or gnus-user-login-name (user-login-name)))
8156
8157 (defun gnus-inews-full-name ()
8158   "Return user full name.
8159 Got from the variable `gnus-user-full-name', the environment variable
8160 NAME, and the function `user-full-name'."
8161   (or gnus-user-full-name
8162       (getenv "NAME") (user-full-name)))
8163
8164 (defun gnus-inews-domain-name (&optional genericfrom)
8165   "Return user's domain name.
8166 If optional argument GENERICFROM is a string, use it as the domain
8167 name; if it is non-nil, strip of local host name from the domain name.
8168 If the function `system-name' returns full internet name and the
8169 domain is undefined, the domain name is got from it."
8170   (if (or genericfrom gnus-local-domain (getenv "DOMAINNAME"))
8171       (let ((domain (or (if (stringp genericfrom) genericfrom)
8172                         (getenv "DOMAINNAME")
8173                         gnus-local-domain
8174                         ;; Function `system-name' may return full internet name.
8175                         ;; Suggested by Mike DeCorte <mrd@sun.soe.clarkson.edu>.
8176                         (if (string-match "\\." (system-name))
8177                             (substring (system-name) (match-end 0)))
8178                         (read-string "Domain name (no host): ")))
8179             (host (or (if (string-match "\\." (system-name))
8180                           (substring (system-name) 0 (match-beginning 0)))
8181                       (system-name))))
8182         (if (string-equal "." (substring domain 0 1))
8183             (setq domain (substring domain 1)))
8184         ;; Support GENERICFROM as same as standard Bnews system.
8185         ;; Suggested by ohm@kaba.junet and vixie@decwrl.dec.com.
8186         (cond ((null genericfrom)
8187                (concat host "." domain))
8188               ;;((stringp genericfrom) genericfrom)
8189               (t domain)))
8190     (substring user-mail-address (1+ (string-match "@" user-mail-address)))))
8191
8192 (defun gnus-inews-message-id ()
8193   "Generate unique Message-ID for user."
8194   ;; Message-ID should not contain a slash and should be terminated by
8195   ;; a number.  I don't know the reason why it is so.
8196   (concat "<" (gnus-inews-unique-id) "@" (gnus-inews-domain-name) ">"))
8197
8198 (defun gnus-inews-unique-id ()
8199   "Generate unique ID from user name and current time."
8200   (let ((date (current-time-string))
8201         (name (gnus-inews-login-name)))
8202     (if (string-match "^[^ ]+ \\([^ ]+\\)[ ]+\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) [0-9][0-9]\\([0-9][0-9]\\)"
8203                       date)
8204         (concat (upcase name) "."
8205                 (substring date (match-beginning 6) (match-end 6)) ;Year
8206                 (substring date (match-beginning 1) (match-end 1)) ;Month
8207                 (substring date (match-beginning 2) (match-end 2)) ;Day
8208                 (substring date (match-beginning 3) (match-end 3)) ;Hour
8209                 (substring date (match-beginning 4) (match-end 4)) ;Minute
8210                 (substring date (match-beginning 5) (match-end 5)) ;Second
8211                 )
8212       (error "Cannot understand current-time-string: %s." date))
8213     ))
8214
8215 (defun gnus-current-time-zone (time)
8216   "The local time zone in effect at TIME, or nil if not known."
8217   (let ((z (and (fboundp 'current-time-zone) (current-time-zone time))))
8218     (if (and z (car z)) z gnus-local-timezone)))
8219
8220 (defun gnus-inews-date ()
8221   "Date string of today.
8222 If `current-time-zone' works, or if `gnus-local-timezone' is set correctly,
8223 this yields a date that conforms to RFC 822.  Otherwise a buggy date will
8224 be generated; this might work with some older news servers."
8225   (let* ((now (and (fboundp 'current-time) (current-time)))
8226          (zone (gnus-current-time-zone now)))
8227     (if zone
8228         (gnus-inews-valid-date now zone)
8229       ;; No timezone info.
8230       (gnus-inews-buggy-date now))))
8231
8232 (defun gnus-inews-valid-date (&optional time zone)
8233   "A date string that represents TIME and conforms to the Usenet standard.
8234 TIME is optional and defaults to the current time.
8235 Some older versions of Emacs always act as if TIME is nil.
8236 The optional argument ZONE specifies the local time zone (default GMT)."
8237   (timezone-make-date-arpa-standard
8238    (if (fboundp 'current-time)
8239        (current-time-string time)
8240      (current-time-string))
8241    zone "GMT"))
8242
8243 (defun gnus-inews-buggy-date (&optional time)
8244   "A buggy date string that represents TIME.
8245 TIME is optional and defaults to the current time.
8246 Some older versions of Emacs always act as if TIME is nil."
8247   (let ((date (if (fboundp 'current-time)
8248                   (current-time-string time)
8249                 (current-time-string))))
8250     (if (string-match "^[^ ]+ \\([^ ]+\\)[ ]+\\([0-9]+\\) \\([0-9:]+\\) [0-9][0-9]\\([0-9][0-9]\\)"
8251                       date)
8252         (concat (substring date (match-beginning 2) (match-end 2)) ;Day
8253                 " "
8254                 (substring date (match-beginning 1) (match-end 1)) ;Month
8255                 " "
8256                 (substring date (match-beginning 4) (match-end 4)) ;Year
8257                 " "
8258                 (substring date (match-beginning 3) (match-end 3))) ;Time
8259       (error "Cannot understand current-time-string: %s." date))
8260     ))
8261
8262 (defun gnus-inews-organization ()
8263   "Return user's organization.
8264 The ORGANIZATION environment variable is used if defined.
8265 If not, the variable gnus-local-organization is used instead.
8266 If the value begins with a slash, it is taken as the name of a file
8267 containing the organization."
8268   ;; The organization must be got in this order since the ORGANIZATION
8269   ;; environment variable is intended for user specific while
8270   ;; gnus-local-organization is for machine or organization specific.
8271
8272   (let* ((private-file (expand-file-name "~/.organization" nil))
8273          (organization (or (getenv "ORGANIZATION")
8274                            gnus-local-organization
8275                            private-file)))
8276     (and (stringp organization)
8277          (> (length organization) 0)
8278          (string-equal (substring organization 0 1) "/")
8279          ;; Get it from the user and system file.
8280          ;; Suggested by roland@wheaties.ai.mit.edu (Roland McGrath).
8281          (let ((dist (mail-fetch-field "distribution")))
8282            (setq organization
8283                  (cond ((file-exists-p (concat organization "-" dist))
8284                         (concat organization "-" dist))
8285                        ((file-exists-p organization) organization)
8286                        ((file-exists-p gnus-organization-file)
8287                         gnus-organization-file)
8288                        (t organization)))
8289            ))
8290     (cond ((not (stringp organization)) nil)
8291           ((and (string-equal (substring organization 0 1) "/")
8292                 (file-exists-p organization))
8293            ;; If the first character is `/', assume it is the name of
8294            ;; a file containing the organization.
8295            (save-excursion
8296              (let ((tmpbuf (get-buffer-create " *Gnus organization*")))
8297                (set-buffer tmpbuf)
8298                (erase-buffer)
8299                (insert-file-contents organization)
8300                (prog1 (buffer-string)
8301                  (kill-buffer tmpbuf))
8302                )))
8303           ((string-equal organization private-file) nil) ;No such file
8304           (t organization))
8305     ))
8306
8307 (defun gnus-inews-lines ()
8308   "Count the number of lines and return numeric string."
8309   (save-excursion
8310     (save-restriction
8311       (widen)
8312       (goto-char (point-min))
8313       (search-forward "\n\n" nil 'move)
8314       (int-to-string (count-lines (point) (point-max))))))
8315
8316 \f
8317 ;;;
8318 ;;; Gnus Mail Functions 
8319 ;;;
8320
8321 (autoload 'news-mail-reply "rnewspost")
8322 (autoload 'news-mail-other-window "rnewspost")
8323
8324 ;;; Mail reply commands of Gnus Summary Mode
8325
8326 (defun gnus-summary-reply (yank)
8327   "Reply mail to news author.
8328 If prefix argument YANK is non-nil, original article is yanked automatically.
8329 Customize the variable gnus-mail-reply-method to use another mailer."
8330   (interactive "P")
8331   ;; Bug fix by jbw@bigbird.bu.edu (Joe Wells)
8332   ;; Stripping headers should be specified with mail-yank-ignored-headers.
8333   (gnus-summary-select-article t)
8334   (setq gnus-winconf-post-news (current-window-configuration))
8335   (let ((gnus-newsgroup-name gnus-newsgroup-name))
8336     (bury-buffer gnus-article-buffer)
8337     (funcall gnus-mail-reply-method yank)))
8338
8339 (defun gnus-summary-reply-with-original ()
8340   "Reply mail to news author with original article.
8341 Customize the variable gnus-mail-reply-method to use another mailer."
8342   (interactive)
8343   (gnus-summary-reply t))
8344
8345 (defun gnus-summary-mail-forward ()
8346   "Forward the current message to another user.
8347 Customize the variable gnus-mail-forward-method to use another mailer."
8348   (interactive)
8349   (gnus-summary-select-article)
8350   (switch-to-buffer gnus-article-buffer)
8351   (widen)
8352   (delete-other-windows)
8353   (bury-buffer gnus-article-buffer)
8354   (funcall gnus-mail-forward-method))
8355
8356 (defun gnus-summary-mail-other-window ()
8357   "Compose mail in other window.
8358 Customize the variable gnus-mail-other-window-method to use another mailer."
8359   (interactive)
8360   (gnus-summary-select-article)
8361   (switch-to-buffer gnus-article-buffer)
8362   (widen)
8363   (delete-other-windows)
8364   (bury-buffer gnus-article-buffer)
8365   (funcall gnus-mail-other-window-method))
8366
8367 (defun gnus-mail-reply-using-mail (&optional yank to-address)
8368   (save-excursion
8369     (set-buffer gnus-summary-buffer)
8370     (let ((info (nth 2 (gnus-gethash gnus-newsgroup-name gnus-newsrc-hashtb)))
8371           (group (gnus-group-real-name gnus-newsgroup-name))
8372           (cur (cons (current-buffer) gnus-current-article))
8373           from subject date to reply-to message-of
8374           references message-id sender follow-to)
8375       (set-buffer (get-buffer-create "*mail*"))
8376       (mail-mode)
8377       (make-local-variable 'gnus-article-reply)
8378       (setq gnus-article-reply cur)
8379       (local-set-key "\C-c\C-c" 'gnus-mail-send-and-exit)
8380       (local-set-key "\C-c\C-y" 'gnus-mail-yank-original)
8381       (if (and (buffer-modified-p)
8382                (> (buffer-size) 0)
8383                (not (y-or-n-p "Unsent article being composed; erase it? ")))
8384           ()
8385         (erase-buffer)
8386         (save-excursion
8387           (set-buffer gnus-article-buffer)
8388           (goto-char (point-min))
8389           (narrow-to-region (point-min)
8390                             (progn (search-forward "\n\n") (point)))
8391           (set-text-properties (point-min) (point-max) nil)
8392           (if (and (boundp 'gnus-reply-to-function)
8393                    gnus-reply-to-function)
8394               (save-excursion
8395                 (save-restriction
8396                   (gnus-narrow-to-headers)
8397                   (setq follow-to (funcall gnus-reply-to-function group)))))
8398           (setq from (mail-fetch-field "from"))
8399           (setq date (mail-fetch-field "date"))
8400           (and from
8401                (let ((stop-pos 
8402                       (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
8403                  (setq message-of
8404                        (concat (if stop-pos (substring from 0 stop-pos) from)
8405                                "'s message of " date))))
8406           (setq sender (mail-fetch-field "sender"))
8407           (setq subject (or (mail-fetch-field "subject")
8408                             "Re: none"))
8409           (or (string-match "^[Rr][Ee]:" subject)
8410               (setq subject (concat "Re: " subject)))
8411           (setq reply-to (mail-fetch-field "reply-to"))
8412           (setq references (mail-fetch-field "references"))
8413           (setq message-id (mail-fetch-field "message-id"))
8414           (widen))
8415         (setq news-reply-yank-from from)
8416         (setq news-reply-yank-message-id message-id)
8417         (mail-setup (or to-address follow-to reply-to from sender)
8418                     subject message-of nil gnus-article-buffer nil)
8419         ;; Fold long references line to follow RFC1036.
8420         (mail-position-on-field "References")
8421         (let ((begin (- (point) (length "References: ")))
8422               (fill-column 78)
8423               (fill-prefix "\t"))
8424           (if references (insert references))
8425           (if (and references message-id) (insert " "))
8426           (if message-id (insert message-id))
8427           ;; The region must end with a newline to fill the region
8428           ;; without inserting extra newline.
8429           (fill-region-as-paragraph begin (1+ (point))))
8430         (goto-char (point-min))
8431         (search-forward (concat "\n" mail-header-separator "\n"))
8432         (if yank
8433             (let ((last (point)))
8434               (run-hooks 'news-reply-header-hook)
8435               (mail-yank-original nil)
8436               (goto-char last))))
8437       (if (not yank)
8438           (let ((mail (current-buffer)))
8439             (switch-to-buffer gnus-article-buffer)
8440             (delete-other-windows)
8441             (switch-to-buffer-other-window mail))
8442         (delete-other-windows)
8443         (switch-to-buffer (current-buffer))))))
8444
8445 (defun gnus-mail-yank-original ()
8446   (interactive)
8447   (run-hooks 'news-reply-header-hook)
8448   (mail-yank-original nil))
8449
8450 (defun gnus-mail-send-and-exit ()
8451   (interactive)
8452   (let ((reply gnus-article-reply))
8453     (mail-send-and-exit nil)
8454     (if (and reply
8455              (get-buffer (car reply))
8456              (buffer-name (car reply)))
8457         (progn
8458           (set-buffer (car reply))
8459           (gnus-summary-mark-article-as-replied 
8460            (cdr reply)))))
8461   (if gnus-winconf-post-news
8462       (set-window-configuration gnus-winconf-post-news)))
8463
8464 (defun gnus-mail-forward-using-mail ()
8465   "Forward the current message to another user using mail."
8466   ;; This is almost a carbon copy of rmail-forward in rmail.el.
8467   (let ((forward-buffer (current-buffer))
8468         (subject
8469          (concat "[" gnus-newsgroup-name "] "
8470                  ;;(mail-strip-quoted-names (gnus-fetch-field "From")) ": "
8471                  (or (gnus-fetch-field "Subject") ""))))
8472     ;; If only one window, use it for the mail buffer.
8473     ;; Otherwise, use another window for the mail buffer
8474     ;; so that the Rmail buffer remains visible
8475     ;; and sending the mail will get back to it.
8476     (if (if (one-window-p t)
8477             (mail nil nil subject)
8478           (mail-other-window nil nil subject))
8479         (save-excursion
8480           (goto-char (point-max))
8481           (insert "------- Start of forwarded message -------\n")
8482           (insert-buffer forward-buffer)
8483           (goto-char (point-max))
8484           (insert "------- End of forwarded message -------\n")
8485           ;; You have a chance to arrange the message.
8486           (run-hooks 'gnus-mail-forward-hook)
8487           ))))
8488
8489 (defun gnus-mail-other-window-using-mail ()
8490   "Compose mail other window using mail."
8491   (news-mail-other-window)
8492   (gnus-overload-functions))
8493
8494 \f
8495 ;;;
8496 ;;; Dribble file
8497 ;;;
8498
8499 (defvar gnus-dribble-ignore nil)
8500
8501 (defun gnus-dribble-file-name ()
8502   (concat gnus-startup-file "-dribble"))
8503
8504 (defun gnus-dribble-open ()
8505   (save-excursion 
8506     (set-buffer 
8507      (setq gnus-dribble-buffer (find-file-noselect (gnus-dribble-file-name))))
8508     (buffer-disable-undo (current-buffer))
8509     (bury-buffer gnus-dribble-buffer)
8510     (auto-save-mode t)
8511     (goto-char (point-max))))
8512
8513 (defun gnus-dribble-enter (string)
8514   (if (not gnus-dribble-ignore)
8515       (let ((obuf (current-buffer)))
8516         (set-buffer gnus-dribble-buffer)
8517         (insert string "\n")
8518         (set-window-point (get-buffer-window (current-buffer)) (point-max))
8519         (set-buffer obuf))))
8520
8521 (defun gnus-dribble-read-file ()
8522   (let ((dribble-file (gnus-dribble-file-name)))
8523     (save-excursion 
8524       (set-buffer (setq gnus-dribble-buffer 
8525                         (get-buffer-create 
8526                          (file-name-nondirectory dribble-file))))
8527       (gnus-add-current-to-buffer-list)
8528       (erase-buffer)
8529       (set-visited-file-name dribble-file)
8530       (buffer-disable-undo (current-buffer))
8531       (bury-buffer (current-buffer))
8532       (set-buffer-modified-p nil)
8533       (let ((auto (make-auto-save-file-name))
8534             (gnus-dribble-ignore t))
8535         (if (or (file-exists-p auto) (file-exists-p dribble-file))
8536             (progn
8537               (if (file-newer-than-file-p auto dribble-file)
8538                   (setq dribble-file auto))
8539               (insert-file-contents dribble-file)
8540               (if (not (zerop (buffer-size)))
8541                   (set-buffer-modified-p t))
8542               (if (y-or-n-p "Auto-save file exists. Do you want to read it? ")
8543                   (progn
8544                     (message "Reading %s..." dribble-file) 
8545                     (eval-current-buffer)
8546                     (message "Reading %s...done" dribble-file)))))))))
8547
8548 (defun gnus-dribble-delete-file ()
8549   (save-excursion
8550     (set-buffer gnus-dribble-buffer)
8551     (let ((auto (make-auto-save-file-name)))
8552       (if (file-exists-p auto)
8553           (delete-file auto))
8554       (if (file-exists-p (gnus-dribble-file-name))
8555           (delete-file (gnus-dribble-file-name)))
8556       (erase-buffer)
8557       (set-buffer-modified-p nil))))
8558
8559 (defun gnus-dribble-save ()
8560   ;; Bug by Evan Welsh <welsh@epcc.ed.ac.uk>.
8561   (if (and gnus-dribble-buffer
8562            (buffer-name gnus-dribble-buffer))
8563       (save-excursion
8564         (set-buffer gnus-dribble-buffer)
8565         (save-buffer))))
8566
8567 (defun gnus-dribble-clear ()
8568   (save-excursion
8569     (if (and gnus-dribble-buffer
8570              (get-buffer gnus-dribble-buffer)
8571              (buffer-name (get-buffer gnus-dribble-buffer)))
8572         (progn
8573           (set-buffer gnus-dribble-buffer)
8574           (erase-buffer)
8575           (set-buffer-modified-p nil)
8576           (setq buffer-saved-size (buffer-size))))))
8577
8578 ;;;
8579 ;;; Server Communication
8580 ;;;
8581
8582 (defun gnus-start-news-server (&optional confirm)
8583   "Open a method for getting news.
8584 If CONFIRM is non-nil, the user will be asked for an NNTP server."
8585   (let (how where)
8586     (if gnus-current-select-method
8587         ;; Stream is already opened.
8588         nil
8589       ;; Open NNTP server.
8590       (if (null gnus-nntp-service) (setq gnus-nntp-server nil))
8591       (if confirm
8592           (progn
8593             ;; Read server name with completion.
8594             (setq gnus-nntp-server
8595                   (completing-read "NNTP server: "
8596                                    (cons (list gnus-nntp-server)
8597                                          gnus-secondary-servers)
8598                                    nil nil gnus-nntp-server))
8599             (setq gnus-select-method
8600                   (list 'nntp gnus-nntp-server)))
8601
8602 ;       (debug)
8603         (if (and gnus-nntp-server 
8604                  (stringp gnus-nntp-server)
8605                  (not (string= gnus-nntp-server "")))
8606             (setq gnus-select-method
8607                   (cond ((or (string= gnus-nntp-server "")
8608                              (string= gnus-nntp-server "::"))
8609                          (list 'nnspool (system-name)))
8610                         ((string-match ":" gnus-nntp-server)
8611                          (list 'mhspool gnus-nntp-server))
8612                         (t
8613                          (list 'nntp gnus-nntp-server))))))
8614
8615       (setq how (car gnus-select-method))
8616       (setq where (car (cdr gnus-select-method)))
8617       (cond ((eq how 'nnspool)
8618              (require 'nnspool)
8619              (message "Looking up local news spool..."))
8620             ((eq how 'mhspool)
8621              (require 'mhspool)
8622              (message "Looking up private directory..."))
8623             (t
8624              (require 'nntp)))
8625       (setq gnus-current-select-method gnus-select-method)
8626       (run-hooks 'gnus-open-server-hook)
8627       (or 
8628        ;; gnus-open-server-hook might have opened it
8629        (gnus-server-opened gnus-select-method)  
8630        (gnus-open-server gnus-select-method)
8631        (error "%s" (gnus-nntp-message 
8632                     (format "Cannot open NNTP server on %s" 
8633                             where))))
8634       gnus-select-method)))
8635
8636 (defun gnus-check-news-server (method)
8637   "If the news server is down, start it up again."
8638   (let ((method (if method method gnus-select-method)))
8639     (if (gnus-server-opened method)
8640         ;; Stream is already opened.
8641         t
8642       ;; Open NNTP server.
8643       (message "Opening server %s on %s..." (car method) (nth 1 method))
8644       (run-hooks 'gnus-open-server-hook)
8645       (message "")
8646       (or (gnus-server-opened method)
8647           (gnus-open-server method)))))
8648
8649 (defun gnus-nntp-message (&optional message)
8650   "Check the status of the NNTP server.
8651 If the status of the server is clear and MESSAGE is non-nil, MESSAGE
8652 is returned insted of the status string."
8653   (let ((status (gnus-status-message gnus-current-select-method))
8654         (message (or message "")))
8655     (if (and (stringp status) (> (length status) 0))
8656         status message)))
8657
8658 (defun gnus-get-function (method function)
8659   (let ((func (intern (format "%s-%s" (car method) function))))
8660     (if (not (fboundp func)) 
8661         (progn
8662           (require (car method))
8663           (if (not (fboundp func)) 
8664               (error "No such function: %s" func))))
8665     func))
8666
8667 ;; Specifying port number suggested by Stephane Laveau <laveau@corse.inria.fr>.
8668 (defun gnus-open-server (method)
8669   (apply (gnus-get-function method 'open-server) (cdr method)))
8670
8671 (defun gnus-close-server (method)
8672   (funcall (gnus-get-function method 'close-server) (nth 1 method)))
8673
8674 (defun gnus-request-list (method)
8675   (funcall (gnus-get-function method 'request-list) (nth 1 method)))
8676
8677 (defun gnus-request-list-newsgroups (method)
8678   (funcall (gnus-get-function method 'request-list-newsgroups) (nth 1 method)))
8679
8680 (defun gnus-server-opened (method)
8681   (funcall (gnus-get-function method 'server-opened) (nth 1 method)))
8682
8683 (defun gnus-status-message (method)
8684   (funcall (gnus-get-function method 'status-message) (nth 1 method)))
8685
8686 (defun gnus-request-group (group &optional dont-check)
8687   (let ((method (gnus-find-method-for-group group)))
8688     (funcall (gnus-get-function method 'request-group) 
8689              (gnus-group-real-name group) (nth 1 method) dont-check)))
8690
8691 (defun gnus-retrieve-headers (articles group)
8692   (let ((method (gnus-find-method-for-group group)))
8693     (funcall (gnus-get-function method 'retrieve-headers) 
8694              articles (gnus-group-real-name group) (nth 1 method))))
8695
8696 (defun gnus-request-article (article group buffer)
8697   (let ((method (gnus-find-method-for-group group)))
8698     (funcall (gnus-get-function method 'request-article) 
8699              article (gnus-group-real-name group) (nth 1 method) buffer)))
8700
8701 (defun gnus-request-head (article group)
8702   (let ((method (gnus-find-method-for-group group)))
8703     (funcall (gnus-get-function method 'request-head) 
8704              article (gnus-group-real-name group) (nth 1 method))))
8705
8706 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
8707 (defun gnus-request-post-buffer (post header artbuf)
8708    (let* ((group gnus-newsgroup-name)
8709           (info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
8710           (method
8711            (if (and gnus-post-method
8712                     (memq 'post (member (car gnus-current-select-method) 
8713                                         gnus-valid-select-methods)))
8714                gnus-post-method
8715              gnus-current-select-method)))
8716     (funcall (gnus-get-function method 'request-post-buffer) 
8717              post header artbuf (gnus-group-real-name group) info)))
8718
8719 (defun gnus-request-post (method)
8720   (and gnus-post-method
8721        (memq 'post (member (car method) gnus-valid-select-methods))
8722        (setq method gnus-post-method))
8723   (funcall (gnus-get-function method 'request-post) 
8724            (nth 1 method)))
8725
8726 (defun gnus-request-expire-articles (articles group)
8727   (let ((method (gnus-find-method-for-group group)))
8728     (funcall (gnus-get-function method 'request-expire-articles) 
8729              articles (gnus-group-real-name group) (nth 1 method))))
8730
8731 (defun gnus-request-move-article (article group server accept-function)
8732   (let ((method (gnus-find-method-for-group group)))
8733     (funcall (gnus-get-function method 'request-move-article) 
8734              article (gnus-group-real-name group) 
8735              (nth 1 method) accept-function)))
8736
8737 (defun gnus-request-accept-article (group)
8738   (let ((func (if (symbolp group) group
8739                 (car (gnus-find-method-for-group group)))))
8740     (funcall (intern (format "%s-request-accept-article" func))
8741              (gnus-group-real-name group))))
8742
8743 (defun gnus-find-method-for-group (group)
8744   (let ((info (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
8745     (if (or (not info)
8746             (not (nth 4 info)))
8747         gnus-select-method
8748       (nth 4 info))))
8749
8750 (defun gnus-check-backend-function (func group)
8751   (let ((method (if (stringp group) (car (gnus-find-method-for-group group))
8752                  group)))
8753     (fboundp (intern (format "%s-%s" method func)))))
8754
8755 (defun gnus-methods-using (method)
8756   (let ((valids gnus-valid-select-methods)
8757         outs)
8758     (while valids
8759       (if (memq method (car valids)) 
8760           (setq outs (cons (car valids) outs)))
8761       (setq valids (cdr valids)))
8762     outs))
8763
8764 ;;; 
8765 ;;; Active & Newsrc File Handling
8766 ;;;
8767
8768 ;; Newsrc related functions.
8769 ;; Gnus internal format of gnus-newsrc-assoc:
8770 ;; (("alt.general" 3 (1 . 1))
8771 ;;  ("alt.misc"    3 ((1 . 10) (12 . 15)))
8772 ;;  ("alt.test"    7 (1 . 99) (45 57 93)) ...)
8773 ;; The first item is the group name; the second is the subscription
8774 ;; level; the third is either a range of a list of ranges of read
8775 ;; articles, the optional fourth element is a list of marked articles,
8776 ;; the optional fifth element is the select method.
8777 ;;
8778 ;; Gnus internal format of gnus-newsrc-hashtb:
8779 ;; (95 ("alt.general" 3 (1 . 1)) ("alt.misc" 3 ((1 . 10) (12 . 15))) ...)
8780 ;; This is the entry for "alt.misc". The first element is the number
8781 ;; of unread articles in "alt.misc". The cdr of this entry is the
8782 ;; element *before* "alt.misc" in gnus-newsrc-assoc, which makes is
8783 ;; trivial to remove or add new elements into gnus-newsrc-assoc
8784 ;; without scanning the entire list. So, to get the actual information
8785 ;; of "alt.misc", you'd say something like 
8786 ;; (nth 2 (gnus-gethash "alt.misc" gnus-newsrc-hashtb))
8787 ;;
8788 ;; Gnus internal format of gnus-active-hashtb:
8789 ;; ((1 . 1))
8790 ;;  (5 . 10))
8791 ;;  (67 . 99)) ...)
8792 ;; The only element in each entry in this hash table is a range of
8793 ;; (possibly) available articles. (Articles in this range may have
8794 ;; been expired or cancelled.)
8795 ;;
8796 ;; Gnus internal format of gnus-killed-list and gnus-zombie-list:
8797 ;; ("alt.misc" "alt.test" "alt.general" ...)
8798
8799 (defun gnus-setup-news (&optional rawfile level)
8800   "Setup news information.
8801 If RAWFILE is non-nil, the .newsrc file will also be read.
8802 If LEVEL is non-nil, the news will be set up at level LEVEL."
8803   (let ((init (not (and gnus-newsrc-assoc
8804                         gnus-active-hashtb
8805                         (not rawfile)))))
8806     ;; Clear some variables to re-initialize news information.
8807     (if init
8808         (setq gnus-newsrc-assoc nil
8809               gnus-active-hashtb nil))
8810     ;; Read the acitve file and create `gnus-active-hashtb'.
8811     ;; If `gnus-read-active-file' is nil, then we just create an empty
8812     ;; hash table. The partial filling out of the hash table will be
8813     ;; done in `gnus-get-unread-articles'.
8814     (if gnus-read-active-file
8815         (gnus-read-active-file)
8816       (setq gnus-active-hashtb (make-vector 4095 0)))
8817
8818     ;; Read the newsrc file and create `gnus-newsrc-hashtb'.
8819     (if init (gnus-read-newsrc-file rawfile))
8820     ;; Find the number of unread articles in each non-dead group.
8821     (gnus-get-unread-articles level)
8822     ;; Find new newsgroups and treat them.
8823     (if (and init gnus-check-new-newsgroups gnus-read-active-file)
8824         (gnus-find-new-newsgroups))
8825     (if (and init gnus-check-bogus-newsgroups gnus-read-active-file)
8826         (gnus-check-bogus-newsgroups))))
8827
8828 (defun gnus-find-new-newsgroups ()
8829   "Search for new newsgroups and add them.
8830 Each new newsgroup will be treated with `gnus-subscribe-newsgroup-method.'
8831 The `-n' option line from .newsrc is respected."
8832   (interactive)
8833   (if (not gnus-have-read-active-file) (gnus-read-active-file))
8834   (if (not (gnus-check-first-time-used))
8835       (let ((groups 0)
8836             group new-newsgroups)
8837         (if (not gnus-killed-hashtb) (gnus-make-hashtable-from-killed))
8838         ;; Go though every newsgroup in `gnus-active-hashtb' and compare
8839         ;; with `gnus-newsrc-hashtb' and `gnus-killed-hashtb'.
8840         (mapatoms
8841          (lambda (sym)
8842            (setq group (symbol-name sym))
8843            (if (or (gnus-gethash group gnus-killed-hashtb)
8844                    (gnus-gethash group gnus-newsrc-hashtb))
8845                ()
8846              (if (and gnus-newsrc-options-n-yes
8847                       (string-match gnus-newsrc-options-n-yes group))
8848                  (progn
8849                    (setq groups (1+ groups))
8850                    (gnus-sethash group group gnus-killed-hashtb)
8851                    (funcall gnus-subscribe-options-newsgroup-method group))
8852                (if (or (null gnus-newsrc-options-n-no)
8853                        (not (string-match gnus-newsrc-options-n-no group)))
8854                    ;; Add this group.
8855                    (progn
8856                      (setq groups (1+ groups))
8857                      (gnus-sethash group group gnus-killed-hashtb)
8858                      (if gnus-subscribe-hierarchical-interactive
8859                          (setq new-newsgroups (cons group new-newsgroups))
8860                        (funcall gnus-subscribe-newsgroup-method group)))))))
8861          gnus-active-hashtb)
8862         (if new-newsgroups 
8863             (gnus-subscribe-hierarchical-interactive new-newsgroups))
8864         ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
8865         (if (> groups 0)
8866             (message "%d new newsgroup%s arrived." 
8867                      groups (if (> groups 1) "s have" " has"))))))
8868
8869 (defun gnus-check-first-time-used ()
8870   (if (or (file-exists-p gnus-startup-file)
8871           (file-exists-p (concat gnus-startup-file ".el"))
8872           (file-exists-p (concat gnus-startup-file ".eld")))
8873       nil
8874     (message "First time user; subscribing you to default groups")
8875     (let ((groups gnus-default-subscribed-newsgroups)
8876           group)
8877       (if (eq groups t)
8878           nil
8879         (setq groups (or groups gnus-backup-default-subscribed-newsgroups))
8880         (mapatoms
8881          (lambda (sym)
8882            (setq group (symbol-name sym))
8883            (if (and gnus-newsrc-options-n-yes
8884                     (string-match gnus-newsrc-options-n-yes group))
8885                (funcall gnus-subscribe-options-newsgroup-method group)
8886              (and (or (null gnus-newsrc-options-n-no)
8887                       (not (string-match gnus-newsrc-options-n-no group)))
8888                   (setq gnus-killed-list (cons group gnus-killed-list)))))
8889          gnus-active-hashtb)
8890         (while groups
8891           (if (gnus-gethash (car groups) gnus-active-hashtb)
8892               (gnus-group-change-level (car groups) 3 9))
8893           (setq groups (cdr groups)))))))
8894
8895 ;; `gnus-group-change-level' is the fundamental function for changing
8896 ;; subscription levels of newsgroups. This might mean just changing
8897 ;; from level 1 to 2, which is pretty trivial, from 2 to 6 or back
8898 ;; again, which subscribes/unsubscribes a group, which is equally
8899 ;; trivial. Changing from 1-7 to 8-9 means that you kill a group, and
8900 ;; from 8-9 to 1-7 means that you remove the group from the list of
8901 ;; killed (or zombie) groups and add them to the (kinda) subscribed
8902 ;; groups. And last but not least, moving from 8 to 9 and 9 to 8,
8903 ;; which is trivial.
8904 ;; ENTRY can either be a string (newsgroup name) or a list (if
8905 ;; FROMKILLED is t, it's a list on the format (NUM INFO-LIST),
8906 ;; otherwise it's a list in the format of the `gnus-newsrc-hashtb'
8907 ;; entries. 
8908 ;; LEVEL is the new level of the group, OLDLEVEL is the old level and
8909 ;; PREVIOUS is the group (in hashtb entry format) to insert this group
8910 ;; after. 
8911 (defun gnus-group-change-level (entry level &optional oldlevel
8912                                       previous fromkilled)
8913   (let (group info active num)
8914     ;; Glean what info we can from the arguments
8915     (if (consp entry)
8916         (if fromkilled (setq group (nth 1 entry))
8917           (setq group (car (nth 2 entry))))
8918       (setq group entry))
8919     (if (and (stringp entry)
8920              oldlevel 
8921              (< oldlevel 8))
8922         (setq entry (gnus-gethash entry gnus-newsrc-hashtb)))
8923     (if (and (not oldlevel)
8924              (listp entry))
8925         (setq oldlevel (car (cdr (nth 2 entry)))))
8926     (if (stringp previous)
8927         (setq previous (gnus-gethash previous gnus-newsrc-hashtb)))
8928
8929     (gnus-dribble-enter
8930      (format "(gnus-group-change-level %S %S %S %S %S)" 
8931              group level oldlevel (car (nth 2 previous)) fromkilled))
8932     
8933     ;; Then we remove the newgroup from any old structures, if needed.
8934     ;; If the group was killed, we remove it from the killed or zombie
8935     ;; list. If not, and it is in fact going to be killed, we remove
8936     ;; it from the newsrc hash table and assoc.
8937     (cond ((>= oldlevel 8)
8938            (if (= oldlevel 8)
8939                (setq gnus-zombie-list (delete group gnus-zombie-list))
8940              (setq gnus-killed-list (delete group gnus-killed-list))))
8941           (t
8942            (if (>= level 8)
8943                (progn
8944                  (gnus-sethash (car (nth 2 entry))
8945                                nil gnus-newsrc-hashtb)
8946                  (if (nth 3 entry)
8947                      (setcdr (gnus-gethash (car (nth 3 entry))
8948                                            gnus-newsrc-hashtb)
8949                              (cdr entry)))
8950                  (setcdr (cdr entry) (cdr (cdr (cdr entry))))))))
8951
8952     ;; Finally we enter (if needed) the list where it is supposed to
8953     ;; go, and change the subscription level. If it is to be killed,
8954     ;; we enter it into the killed or zombie list.
8955     (cond ((>= level 8)
8956            (if (= level 8)
8957                (setq gnus-zombie-list (cons group gnus-zombie-list))
8958              (setq gnus-killed-list (cons group gnus-killed-list))))
8959           (t
8960            ;; If the list is to be entered into the newsrc assoc, and
8961            ;; it was killed, we have to create an entry in the newsrc
8962            ;; hashtb format and fix the pointers in the newsrc assoc.
8963            (if (>= oldlevel 8)
8964                (progn
8965                  (if (listp entry)
8966                      (progn
8967                        (setq info (cdr entry))
8968                        (setq num (car entry)))
8969                    (setq active (gnus-gethash group gnus-active-hashtb))
8970                    (setq num (- (1+ (cdr active)) (car active)))
8971                    (setq info (list group level (cons 1 (1- (car active))))))
8972                  (setq entry (cons info (if previous (cdr (cdr previous))
8973                                           (cdr gnus-newsrc-assoc))))
8974                  (setcdr (if previous (cdr previous) gnus-newsrc-assoc)
8975                          entry)
8976                  (gnus-sethash group (cons num (if previous (cdr previous)
8977                                                  gnus-newsrc-assoc))
8978                                gnus-newsrc-hashtb)
8979                  (if (cdr entry)
8980                      (setcdr (gnus-gethash (car (car (cdr entry)))
8981                                            gnus-newsrc-hashtb)
8982                              entry)))
8983              ;; It was alive, and it is going to stay alive, so we
8984              ;; just change the level and don't change any pointers or
8985              ;; hash table entries.
8986              (setcar (cdr (car (cdr (cdr entry)))) level))))))
8987
8988 (defun gnus-kill-newsgroup (newsgroup)
8989   "Obsolete function. Kills a newsgroup."
8990   (gnus-group-change-level (gnus-gethash newsgroup gnus-newsrc-hashtb) 9))
8991
8992 (defun gnus-check-bogus-newsgroups (&optional confirm)
8993   "Delete bogus newsgroups.
8994 If CONFIRM is non-nil, the user has to confirm the deletion of every
8995 newsgroup." 
8996   (let ((newsrc (cdr gnus-newsrc-assoc))
8997         (dead-lists '(gnus-killed-list gnus-zombie-list))
8998         bogus group killed)
8999     (message "Checking bogus newsgroups...")
9000     (if (not gnus-have-read-active-file) (gnus-read-active-file))
9001     ;; Find all bogus newsgroup that are subscribed.
9002     (while newsrc
9003       (setq group (car (car newsrc)))
9004       (if (or (gnus-gethash group gnus-active-hashtb)
9005               (nth 4 (car newsrc))
9006               (and confirm
9007                    (not (y-or-n-p
9008                          (format "Delete bogus newsgroup: %s " group)))))
9009           ;; Active newsgroup.
9010           ()
9011         ;; Found a bogus newsgroup.
9012         (setq bogus (cons group bogus)))
9013       (setq newsrc (cdr newsrc)))
9014     ;; Remove all bogus subscribed groups by first killing them, and
9015     ;; then removing them from the list of killed groups.
9016     (while bogus
9017       (gnus-group-change-level 
9018        (gnus-gethash (car bogus) gnus-newsrc-hashtb) 9)
9019       (setq gnus-killed-list (delq (car bogus) gnus-killed-list))
9020       (setq bogus (cdr bogus)))
9021     ;; Then we remove all bogus groups from the list of killed and
9022     ;; zombie groups. They are are deleted without confirmation.
9023     (while dead-lists
9024       (setq killed (symbol-value (car dead-lists)))
9025       (while killed
9026         (setq group (car killed))
9027         (or (gnus-gethash group gnus-active-hashtb)
9028             ;; The group is bogus.
9029             (setq bogus (cons group bogus)))
9030         (setq killed (cdr killed)))
9031       (while bogus
9032         (set (car dead-lists)
9033              (delq (car bogus) (symbol-value (car dead-lists))))
9034         (setq bogus (cdr bogus)))
9035       (setq dead-lists (cdr dead-lists)))
9036     (message "Checking bogus newsgroups... done")))
9037
9038 ;; Go though `gnus-newsrc-assoc' and compare with `gnus-active-hashtb'
9039 ;; and compute how many unread articles there are in each group.
9040 (defun gnus-get-unread-articles (&optional level)
9041   (let ((newsrc (cdr gnus-newsrc-assoc))
9042         (level (or level 7))
9043         info group active)
9044     (message "Checking new news...")
9045     (while newsrc
9046       (setq info (car newsrc))
9047       (setq group (car info))
9048
9049       ;; Check foreign newsgroups. If the user doesn't want to check
9050       ;; them, or they can't be checked, for instance, if the news
9051       ;; server can't be reached, we just set the number of unread
9052       ;; articles in this newsgroup to t. This means that Gnus
9053       ;; thinks that there are unread articles, but it has no idea how
9054       ;; many. 
9055       (if (nth 4 info)
9056           (and (or (if (numberp gnus-activate-foreign-newsgroups)
9057                        (> (nth 1 info) gnus-activate-foreign-newsgroups)
9058                      (not gnus-activate-foreign-newsgroups))
9059                    (not (gnus-activate-foreign-newsgroup info)))
9060                (progn
9061                  (gnus-sethash group nil gnus-active-hashtb)
9062                  (setcar (gnus-gethash group gnus-newsrc-hashtb) t))))
9063
9064       (if (or (and (> (nth 1 info) level)
9065                    (not (car (gnus-gethash group gnus-newsrc-hashtb)))
9066                    (setcar (gnus-gethash group gnus-newsrc-hashtb) t))
9067               (not (or (setq active (gnus-gethash group gnus-active-hashtb))
9068                        (and (not gnus-read-active-file)
9069                             (setq active (gnus-activate-newsgroup 
9070                                           (car info)))))))
9071           ;; If this is a bogus group, there's not much we can do.
9072           ()
9073         (gnus-get-unread-articles-in-group info active))
9074       (setq newsrc (cdr newsrc)))
9075     (message "Checking new news... done")))
9076
9077
9078 ;; Create a hash table out of the newsrc alist. The `car's of the
9079 ;; alist elements are used as keys.
9080 (defun gnus-make-hashtable-from-newsrc-alist ()
9081   (let ((alist gnus-newsrc-assoc)
9082          prev)
9083     (setq gnus-newsrc-hashtb (gnus-make-hashtable (length alist)))
9084     (setq alist 
9085           (setq prev (setq gnus-newsrc-assoc 
9086                            (cons (list "dummy.group" 0 (cons 0 0)) alist))))
9087     (while alist
9088       (gnus-sethash (car (car alist)) (cons nil prev) gnus-newsrc-hashtb)
9089       (setq prev alist)
9090       (setq alist (cdr alist)))))
9091
9092 (defun gnus-make-hashtable-from-killed ()
9093   "Create a hash table from the killed and zombie lists."
9094   (let ((lists '(gnus-killed-list gnus-zombie-list))
9095         list)
9096     (setq gnus-killed-hashtb 
9097           (gnus-make-hashtable 
9098            (+ (length gnus-killed-list) (length gnus-zombie-list))))
9099     (while lists
9100       (setq list (symbol-value (car lists)))
9101       (setq lists (cdr lists))
9102       (while list
9103         (gnus-sethash (car list) (car list) gnus-killed-hashtb)
9104         (setq list (cdr list))))))
9105
9106 (defun gnus-get-unread-articles-in-group (info active)
9107   (let (num srange lowest range group)
9108     ;; Modify the list of read articles according to what articles 
9109     ;; are available; then tally the unread articles and add the
9110     ;; number to the group hash table entry.
9111     (setq range (nth 2 info))
9112     (setq num 0)
9113     (cond ((not range)
9114            (setq num (- (1+ (cdr active)) (car active))))
9115           ((atom (car range))
9116            ;; Fix a single (num . num) range according to the
9117            ;; active hash table.
9118            (if (< (cdr range) (car active)) (setcdr range (car active)))
9119            ;; Compute number of unread articles.
9120            (setq num (- (cdr active) (- (1+ (cdr range)) (car range))))
9121            (if (< num 0) (setq num 0)))
9122           (t
9123            ;; The read list is a list of ranges. Fix them according to
9124            ;; the active hash table.
9125            (setq srange range)
9126            (setq lowest (1- (car active)))
9127            (while (and (< (cdr (car srange)) lowest))
9128              (if (and (cdr srange)
9129                       (<= (cdr (car srange)) (1+ lowest)))
9130                  (progn
9131                    (setcdr (car srange) (cdr (car (cdr srange))))
9132                    (setcdr srange (cdr (cdr srange))))
9133                (setcdr (car srange) lowest)))
9134            ;; Compute the number of unread articles.
9135            (while range
9136              (setq num (+ num (- (1+ (cdr (car range))) 
9137                                  (car (car range)))))
9138              (setq range (cdr range)))
9139            (setq num (- (cdr active) num))))
9140     (setcar (gnus-gethash (car info) gnus-newsrc-hashtb) num)
9141     ;; Suggested by Sudish Joseph <joseph@cis.ohio-state.edu>.
9142     ;; Active will be (n . n-1) for groups that have no articles
9143     ;; whatsoever, which makes the number of unread articles wrong, so:
9144     (if (< num 0) 0 num)))
9145
9146 (defun gnus-activate-foreign-newsgroup (info)
9147   (and (gnus-check-news-server (nth 4 info))
9148        (gnus-activate-newsgroup (car info) (gnus-group-real-name (car info)))))
9149
9150 (defun gnus-activate-newsgroup (group &optional real-group-name)
9151   (let (active)
9152     (if (gnus-request-group group)
9153         (save-excursion
9154           (set-buffer nntp-server-buffer)
9155           (goto-char 1)
9156           (if (looking-at "[0-9]+ [0-9]+ \\([0-9]+\\) \\([0-9]+\\)")
9157               (gnus-sethash group 
9158                (setq active
9159                      (cons (string-to-int (buffer-substring (match-beginning 1)
9160                                                             (match-end 1)))
9161                            (string-to-int 
9162                             (buffer-substring (match-beginning 2) 
9163                                               (match-end 2)))))
9164                gnus-active-hashtb))))
9165     active))
9166
9167 (defun gnus-update-read-articles 
9168   (group unread unselected ticked &optional domarks replied expirable killed
9169          interesting bookmark)
9170   "Update the list of read and ticked articles in GROUP using the
9171 UNREAD and TICKED lists.
9172 Note: UNSELECTED has to be sorted over `<'."
9173   (let* ((active (gnus-gethash group gnus-active-hashtb))
9174          (entry (gnus-gethash group gnus-newsrc-hashtb))
9175          (number (car entry))
9176          (info (nth 2 entry))
9177          (marked (nth 3 info))
9178          (prev 1)
9179          (unread (sort (copy-sequence unread) (function <)))
9180          last read)
9181     (if (not info)
9182         ;; There is no info on this group if it was, in fact,
9183         ;; killed. Gnus stores no information on killed groups, so
9184         ;; there's nothing to be done. 
9185         ;; One could store the information somewhere temporarily,
9186         ;; perhaps... Hmmm... 
9187         ()
9188       ;; Remove any negative articles numbers.
9189       (while (and unread (< (car unread) 0))
9190         (setq unread (cdr unread)))
9191       (if (not (and (numberp number) (= 0 number)))
9192           (setq unread (nconc unselected unread)))
9193       ;; Set the number of unread articles in gnus-newsrc-hashtb.
9194       (if (not (eq 'nnvirtual (car gnus-current-select-method)))
9195           (setcar entry (length unread)))
9196       ;; Compute the ranges of read articles by looking at the list of
9197       ;; unread articles.  
9198       (while unread
9199         (if (/= (car unread) prev)
9200             (setq read (cons (cons prev (1- (car unread))) read)))
9201         (setq prev (1+ (car unread)))
9202         (setq unread (cdr unread)))
9203       (if (<= prev (cdr active))
9204           (setq read (cons (cons prev (cdr active)) read)))
9205       ;; Enter this list into the group info.
9206       (setcar (cdr (cdr info)) 
9207               (if (> (length read) 1) (nreverse read) (car read)))
9208       ;; Enter the list of ticked articles.
9209       (gnus-set-marked-articles 
9210        info ticked
9211        (or (and domarks replied) (cdr (assq 'reply marked)))
9212        (or (and domarks expirable) (cdr (assq 'expire marked)))
9213        (or (and domarks killed) (cdr (assq 'killed marked)))
9214        (or (and domarks interesting) (cdr (assq 'interesting marked)))
9215        (or (and domarks bookmark) (cdr (assq 'bookmark marked)))))))
9216
9217 (defun gnus-read-active-file ()
9218   "Get active file from NNTP server."
9219   (gnus-group-set-mode-line)
9220   (setq gnus-have-read-active-file t)
9221   ;; Make sure a connection to NNTP server is alive.
9222   (gnus-check-news-server gnus-select-method)
9223   (let ((mesg (format "Reading active file from %s via %s..."
9224                       (nth 1 gnus-select-method) (car gnus-select-method))))
9225     (message mesg)
9226     (if (gnus-request-list gnus-select-method) ; Get active 
9227         (save-excursion
9228           (set-buffer nntp-server-buffer)
9229           (gnus-active-to-gnus-format)
9230           (setq gnus-have-read-active-file t)
9231           (message "%s...done" mesg))
9232       (error "Cannot read active file from NNTP server."))))
9233
9234 ;; rewritten by jwz based on ideas from Rick Sladkey <jrs@world.std.com>
9235 ;; Further rewrites by lmi.
9236 (defun gnus-active-to-gnus-format ()
9237   "Convert active file format to internal format.
9238 Lines matching gnus-ignored-newsgroups are ignored."
9239   (let ((cur (current-buffer)))
9240     ;; Delete unnecessary lines.
9241     (goto-char (point-min))
9242     (delete-matching-lines gnus-ignored-newsgroups)
9243     ;; Make large enough hash table.
9244     (setq gnus-active-hashtb
9245           (gnus-make-hashtable (count-lines (point-min) (point-max))))
9246     ;; Store active file in hashtable.
9247     (save-restriction
9248       (goto-char (point-min))
9249       (if (or (re-search-forward "\n.\r?$" nil t)
9250               (goto-char (point-max)))
9251           (progn
9252             (beginning-of-line)
9253             (narrow-to-region (point-min) (point))))
9254       (goto-char (point-min))
9255       (if (string-match "%[oO]" gnus-group-line-format)
9256           ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
9257           ;; If we want information on moderated groups, we use this
9258           ;; loop...   
9259           (let ((mod-hashtb (make-vector 7 0))
9260                 group max mod)
9261             (while (not (eobp))
9262               (setq group (let ((obarray gnus-active-hashtb))
9263                             (read cur)))
9264               (setq max (read cur))
9265               (set group (cons (read cur) max))
9266               ;; Enter moderated groups into a list.
9267               (if (string= 
9268                    (symbol-name  (let ((obarray mod-hashtb)) (read cur)))
9269                    "m")
9270                   (setq gnus-moderated-list 
9271                         (cons (symbol-name group) gnus-moderated-list)))
9272               (forward-line 1)))
9273         ;; And if we do not care about moderation, we use this loop,
9274         ;; which is faster.
9275         (let (group max)
9276           (while (not (eobp))
9277             ;; group gets set to a symbol interned in gnus-active-hashtb
9278             ;; (what a hack!!)
9279             (setq group (let ((obarray gnus-active-hashtb))
9280                           (read cur)))
9281             (setq max (read cur))
9282             (set group (cons (read cur) max))
9283             (forward-line 1)))))))
9284
9285 (defun gnus-read-newsrc-file (&optional force)
9286   "Read startup file.
9287 If FORCE is non-nil, the .newsrc file is read."
9288   (setq gnus-current-startup-file (gnus-make-newsrc-file gnus-startup-file))
9289   ;; Reset variables that might be defined in the .newsrc.eld file.
9290   (let ((variables gnus-variable-list))
9291     (while variables
9292       (set (car variables) nil)
9293       (setq variables (cdr variables))))
9294   (let* ((newsrc-file gnus-current-startup-file)
9295          (quick-file (concat newsrc-file ".el")))
9296     (save-excursion
9297       ;; We always load the .newsrc.eld file. If always contains
9298       ;; much information that can not be gotten from the .newsrc
9299       ;; file (ticked articles, killed groups, foreign methods, etc.)
9300       (gnus-read-newsrc-el-file quick-file)
9301  
9302       (if (or force
9303               (and (file-newer-than-file-p newsrc-file quick-file)
9304                    (file-newer-than-file-p newsrc-file 
9305                                            (concat quick-file "d")))
9306               (not gnus-newsrc-assoc))
9307           ;; We read the .newsrc file. Note that if there if a
9308           ;; .newsrc.eld file exists, it has already been read, and
9309           ;; the `gnus-newsrc-hashtb' has been created. While reading
9310           ;; the .newsrc file, Gnus will only use the information it
9311           ;; can find there for changing the data already read -
9312           ;; ie. reading the .newsrc file will not trash the data
9313           ;; already read (except for read articles).
9314           (save-excursion
9315             (message "Reading %s..." newsrc-file)
9316             (set-buffer (find-file-noselect newsrc-file))
9317             (buffer-disable-undo (current-buffer))
9318             (gnus-newsrc-to-gnus-format)
9319             (kill-buffer (current-buffer))
9320             (message "Reading %s... done" newsrc-file)))
9321       (gnus-dribble-read-file))))
9322
9323 (defun gnus-read-newsrc-el-file (file)
9324   (let ((ding-file (concat file "d")))
9325     ;; We always, always read the .eld file.
9326     (message "Reading %s..." ding-file)
9327     (condition-case nil
9328         (load ding-file t t t)
9329       (error nil))
9330     (gnus-make-hashtable-from-newsrc-alist)
9331     (if (not (file-newer-than-file-p file ding-file))
9332         ()
9333       ;; Old format quick file
9334       (message "Reading %s..." file)
9335       ;; The .el file is newer than the .eld file, so we read that one
9336       ;; as well. 
9337       (gnus-read-old-newsrc-el-file file))))
9338
9339 ;; Parse the old-style quick startup file
9340 (defun gnus-read-old-newsrc-el-file (file)
9341   (let (newsrc killed marked group g m len info)
9342     (prog1
9343         (let (gnus-killed-assoc gnus-marked-assoc gnus-newsrc-assoc)
9344           (prog1
9345               (condition-case nil
9346                   (load file t t t)
9347                 (error nil))
9348             (setq newsrc gnus-newsrc-assoc
9349                   killed gnus-killed-assoc
9350                   marked gnus-marked-assoc)))
9351       (setq gnus-newsrc-assoc nil)
9352       (while newsrc
9353         (setq group (car newsrc))
9354         (let ((info (nth 2 (gnus-gethash (car group) gnus-newsrc-hashtb))))
9355           (if info
9356               (progn
9357                 ;; Bug by Kimball Collins <kpc@ptolemy.arc.nasa.gov>. 
9358                 (setcar (nthcdr 2 info) (cdr (cdr group)))
9359                 (setcar (cdr info) (if (nth 1 group) 3 6))
9360                 (setq gnus-newsrc-assoc (cons info gnus-newsrc-assoc)))
9361             (setq gnus-newsrc-assoc
9362                   (cons 
9363                    (setq info
9364                          (list (car group)
9365                                (if (nth 1 group) 3 6) (cdr (cdr group))))
9366                    gnus-newsrc-assoc)))
9367           (if (setq m (assoc (car group) marked))
9368             (setcdr (cdr (cdr info)) (cons (list (cons 'tick (cdr m))) nil))))
9369         (setq newsrc (cdr newsrc)))
9370       (setq newsrc killed)
9371       (while newsrc
9372         (setcar newsrc (car (car newsrc)))
9373         (setq newsrc (cdr newsrc)))
9374       (setq gnus-killed-list killed))
9375     (setq gnus-newsrc-assoc (nreverse gnus-newsrc-assoc))
9376     (gnus-make-hashtable-from-newsrc-alist)))
9377       
9378 (defun gnus-make-newsrc-file (file)
9379   "Make server dependent file name by catenating FILE and server host name."
9380   (let* ((file (expand-file-name file nil))
9381          (real-file (concat file "-" (nth 1 gnus-select-method))))
9382     (if (file-exists-p real-file)
9383         real-file file)
9384     ))
9385
9386 ;; jwz: rewrote this function to be much more efficient, and not be subject
9387 ;; to regexp overflow errors when it encounters very long lines -- the old
9388 ;; behavior was to blow off the rest of the *file* when a line was encountered
9389 ;; that was too long to match!!  Now it uses only simple looking-at calls, and
9390 ;; doesn't create as many temporary strings.  It also now handles multiple
9391 ;; consecutive options lines (before it only handled the first.)
9392 ;; Tiny rewrite by lmi. 
9393 (defun gnus-newsrc-to-gnus-format ()
9394   "Parse current buffer as .newsrc file."
9395   ;; We have to re-initialize these variables (except for
9396   ;; gnus-killed-list) because quick startup file may contain bogus
9397   ;; values.
9398   (setq gnus-newsrc-options nil)
9399   (setq gnus-newsrc-options-n-yes nil)
9400   (setq gnus-newsrc-options-n-no nil)
9401   (setq gnus-newsrc-assoc nil)
9402   (gnus-parse-options-lines)
9403   (gnus-parse-newsrc-body))
9404
9405 (defun gnus-parse-options-lines ()
9406   ;; newsrc.5 seems to indicate that the options line can come anywhere
9407   ;; in the file, and that there can be any number of them:
9408   ;;
9409   ;;       An  options  line  starts  with  the  word  options (left-
9410   ;;       justified).  Then there are the list of  options  just  as
9411   ;;       they would be on the readnews command line.  For instance:
9412   ;;
9413   ;;       options -n all !net.sf-lovers !mod.human-nets -r
9414   ;;       options -c -r
9415   ;;
9416   ;;       A string of lines beginning with a space or tab after  the
9417   ;;       initial  options  line  will  be  considered  continuation
9418   ;;       lines.
9419   ;;
9420   ;; For now, we only accept it at the beginning of the file.
9421
9422   (goto-char (point-min))
9423   (skip-chars-forward " \t\n")
9424   (setq gnus-newsrc-options nil)
9425   (while (looking-at "^options[ \t]*\\(.*\\)\n")
9426     ;; handle consecutive options lines
9427     (setq gnus-newsrc-options (concat gnus-newsrc-options
9428                                       (if gnus-newsrc-options "\n\t")
9429                                       (buffer-substring (match-beginning 1)
9430                                                         (match-end 1))))
9431     (forward-line 1)
9432     (while (looking-at "[ \t]+\\(.*\\)\n")
9433       ;; handle subsequent continuation lines of this options line
9434       (setq gnus-newsrc-options (concat gnus-newsrc-options "\n\t"
9435                                         (buffer-substring (match-beginning 1)
9436                                                           (match-end 1))))
9437       (forward-line 1)))
9438   ;; Gather all "-n" options lines.
9439   (let ((start 0)
9440         (result nil))
9441     (if gnus-newsrc-options
9442         (while (and (string-match "^[ \t]*-n\\([^\n]*\\)$"
9443                                   gnus-newsrc-options
9444                                   start)
9445                     (setq start (match-end 0)))
9446           (setq result (concat result
9447                                (and result " ")
9448                                (substring gnus-newsrc-options
9449                                           (match-beginning 1)
9450                                           (match-end 1))))))
9451     (let ((yes-and-no (and result (gnus-parse-n-options result))))
9452       (setq gnus-newsrc-options-n-yes (car yes-and-no))
9453       (setq gnus-newsrc-options-n-no (cdr yes-and-no)))
9454     nil))
9455
9456 (defun gnus-parse-newsrc-body ()
9457   ;; Point has been positioned after the options lines.  We shouldn't
9458   ;; see any more in here.
9459
9460   (let ((subscribe nil)
9461         (read-list nil)
9462         (line (1+ (count-lines (point-min) (point))))
9463         newsgroup
9464         p p2)
9465     (save-restriction
9466       (skip-chars-forward " \t")
9467       (while (not (eobp))
9468         (cond
9469          ((= (following-char) ?\n)
9470           ;; skip blank lines
9471           nil)
9472          (t
9473           (setq p (point))
9474           (skip-chars-forward "^:!\n")
9475           (if (= (following-char) ?\n)
9476               (error "line %d is unparsable in %s" line (buffer-name)))
9477           (setq p2 (point))
9478           (skip-chars-backward " \t")
9479
9480           ;; #### note: we could avoid consing a string here by binding obarray
9481           ;; and reading the newsgroup directly into the gnus-newsrc-hashtb,
9482           ;; then setq'ing newsgroup to symbol-name of that, like we do in
9483           ;; gnus-active-to-gnus-format.
9484           (setq newsgroup (buffer-substring p (point)))
9485           (goto-char p2)
9486
9487           (setq subscribe (= (following-char) ?:))
9488           (setq read-list nil)
9489
9490           (forward-char 1)              ; after : or !
9491           (skip-chars-forward " \t")
9492           (while (not (= (following-char) ?\n))
9493             (skip-chars-forward " \t")
9494             (or
9495              (and (cond
9496                    ((looking-at "\\([0-9]+\\)-\\([0-9]+\\)") ; a range
9497                     (setq read-list
9498                           (cons
9499                            (cons
9500                             (progn
9501                               ;; faster that buffer-substring/string-to-int
9502                               (narrow-to-region (point-min) (match-end 1))
9503                               (read (current-buffer)))
9504                             (progn
9505                               (narrow-to-region (point-min) (match-end 2))
9506                               (forward-char) ; skip over "-"
9507                               (prog1
9508                                   (read (current-buffer))
9509                                 (widen))))
9510                            read-list))
9511                     t)
9512                    ((looking-at "[0-9]+")
9513                     ;; faster that buffer-substring/string-to-int
9514                     (narrow-to-region (point-min) (match-end 0))
9515                     (setq p (read (current-buffer)))
9516                     (widen)
9517                     (setq read-list (cons (cons p p) read-list))
9518                     t)
9519                    (t
9520                     ;; bogus chars in ranges
9521                     nil))
9522                   (progn
9523                     (goto-char (match-end 0))
9524                     (skip-chars-forward " \t")
9525                     (cond ((= (following-char) ?,)
9526                            (forward-char 1)
9527                            t)
9528                           ((= (following-char) ?\n)
9529                            t)
9530                           (t
9531                            ;; bogus char after range
9532                            nil))))
9533              ;; if we get here, the parse failed
9534              (progn
9535                (end-of-line)            ; give up on this line
9536                (ding)
9537                (message "Ignoring bogus line %d for %s in %s"
9538                         line newsgroup (buffer-name))
9539                (sleep-for 1))))
9540           (if read-list
9541               (let ((info (nth 2 (gnus-gethash newsgroup gnus-newsrc-hashtb))))
9542                 (if info
9543                     (progn
9544                       (setcar (nthcdr 2 info) (nreverse read-list))
9545                       (setcar (cdr info) (if subscribe 2 6))
9546                       (setq gnus-newsrc-assoc (cons info gnus-newsrc-assoc)))
9547                   (setq gnus-newsrc-assoc
9548                         (cons 
9549                          (cons newsgroup 
9550                                (cons (if subscribe 2 6) (nreverse read-list)))
9551                          gnus-newsrc-assoc))))
9552             (setq gnus-killed-list (cons newsgroup gnus-killed-list)))))
9553         (setq line (1+ line))
9554         (forward-line 1))))
9555   (setq gnus-newsrc-assoc (nreverse gnus-newsrc-assoc))
9556   (gnus-make-hashtable-from-newsrc-alist)
9557   nil)
9558
9559 (defun gnus-parse-n-options (options)
9560   "Parse -n NEWSGROUPS options and return a cons of YES and NO regexps."
9561   (let ((yes nil)
9562         (no nil)
9563         (yes-or-no nil)                 ;`!' or not.
9564         (newsgroup nil))
9565     ;; Parse each newsgroup description such as "comp.all".  Commas
9566     ;; and white spaces can be a newsgroup separator.
9567     (while
9568         (string-match "^[ \t\n,]*\\(!?\\)\\([^- \t\n,][^ \t\n,]*\\)" options)
9569       (setq yes-or-no
9570             (substring options (match-beginning 1) (match-end 1)))
9571       (setq newsgroup
9572             (regexp-quote
9573              (substring options
9574                         (match-beginning 2) (match-end 2))))
9575       (setq options (substring options (match-end 2)))
9576       ;; Rewrite "all" to ".+" not ".*".  ".+" requires at least one
9577       ;; character.
9578       (while (string-match "\\(^\\|\\\\[.]\\)all\\(\\\\[.]\\|$\\)" newsgroup)
9579         (setq newsgroup
9580               (concat (substring newsgroup 0 (match-end 1))
9581                       ".+"
9582                       (substring newsgroup (match-beginning 2)))))
9583       ;; It is yes or no.
9584       (cond ((string-equal yes-or-no "!")
9585              (setq no (cons newsgroup no)))
9586             ((string-equal newsgroup ".+")) ;Ignore `all'.
9587             (t
9588              (setq yes (cons newsgroup yes))))
9589       )
9590     ;; Make a cons of regexps from parsing result.
9591     ;; We have to append \(\.\|$\) to prevent matching substring of
9592     ;; newsgroup.  For example, "jp.net" should not match with
9593     ;; "jp.network".
9594     ;; Fixes for large regexp problems are from yonezu@nak.math.keio.ac.jp.
9595     (cons (if yes
9596               (concat "^\\("
9597                       (apply (function concat)
9598                              (mapcar
9599                               (lambda (newsgroup)
9600                                 (concat newsgroup "\\|"))
9601                               (cdr yes)))
9602                       (car yes) "\\)\\(\\.\\|$\\)"))
9603           (if no
9604               (concat "^\\("
9605                       (apply (function concat)
9606                              (mapcar
9607                               (lambda (newsgroup)
9608                                 (concat newsgroup "\\|"))
9609                               (cdr no)))
9610                       (car no) "\\)\\(\\.\\|$\\)")))
9611     ))
9612
9613 (defun gnus-save-newsrc-file ()
9614   "Save to .newsrc FILE."
9615   ;; Note: We cannot save .newsrc file if all newsgroups are removed
9616   ;; from the variable gnus-newsrc-assoc.
9617   (and (or gnus-newsrc-assoc gnus-killed-list)
9618        gnus-current-startup-file
9619        (save-excursion
9620          (if (= 0 (save-excursion
9621                     (set-buffer gnus-dribble-buffer)
9622                     (buffer-size)))
9623              (message "(No changes need to be saved)")
9624            (if gnus-save-newsrc-file
9625                (let ((make-backup-files t)
9626                      (version-control nil)
9627                      (require-final-newline t)) ;Don't ask even if requested.
9628                  (message "Saving %s..." gnus-current-startup-file)
9629                  ;; Make backup file of master newsrc.
9630                  ;; You can stop or change version control of backup file.
9631                  ;; Suggested by jason@violet.berkeley.edu.
9632                  (run-hooks 'gnus-save-newsrc-hook)
9633                  (gnus-gnus-to-newsrc-format)
9634                  (message "Saving %s... done" gnus-current-startup-file)))
9635            ;; Quickly loadable .newsrc.
9636            (set-buffer (get-buffer-create " *Gnus-newsrc*"))
9637            (gnus-add-current-to-buffer-list)
9638            (buffer-disable-undo (current-buffer))
9639            (erase-buffer)
9640            (message "Saving %s.eld..." gnus-current-startup-file)
9641            (gnus-gnus-to-quick-newsrc-format)
9642            (let ((make-backup-files nil)
9643                  (version-control nil)
9644                  (require-final-newline t)) ;Don't ask even if requested.
9645              (write-region 1 (point-max) 
9646                            (concat gnus-current-startup-file ".eld") 
9647                            nil 'nomesg))
9648            (kill-buffer (current-buffer))
9649            (message "Saving %s.eld... done" gnus-current-startup-file)
9650            (gnus-dribble-delete-file)))))
9651
9652 (defun gnus-gnus-to-quick-newsrc-format ()
9653   "Insert Gnus variables such as gnus-newsrc-assoc in lisp format."
9654   (insert ";; (ding) Gnus startup file.\n")
9655   (insert ";; Never delete this file - touch .newsrc instead to force Gnus\n")
9656   (insert ";; to read .newsrc.\n")
9657   (let ((variables gnus-variable-list)
9658         (gnus-newsrc-assoc (cdr gnus-newsrc-assoc))
9659         variable)
9660     ;; insert lisp expressions.
9661     (while variables
9662       (setq variable (car variables))
9663       (and (boundp variable)
9664            (symbol-value variable)
9665            (or gnus-save-killed-list 
9666                (not (or (eq variable 'gnus-killed-list)
9667                         (eq variable 'gnus-zombie-list))))
9668            (insert "(setq " (symbol-name variable) " '"
9669                    (prin1-to-string (symbol-value variable))
9670                    ")\n"))
9671       (setq variables (cdr variables)))))
9672
9673 (defun gnus-gnus-to-newsrc-format ()
9674   (let ((newsrc (cdr gnus-newsrc-assoc))
9675         group ranges)
9676     (save-excursion
9677       (set-buffer (create-file-buffer gnus-startup-file))
9678       (buffer-disable-undo (current-buffer))
9679       (erase-buffer)
9680       ;; Bug by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
9681       (if gnus-newsrc-options (insert "options " gnus-newsrc-options "\n"))
9682       (while newsrc
9683         (setq group (car newsrc))
9684         (insert (car group) (if (>= (nth 1 group) 6) "!" ":"))
9685         (if (setq ranges (nth 2 group))
9686             (progn
9687               (insert " ")
9688               (gnus-ranges-to-newsrc-format
9689                (if (atom (car ranges)) (list ranges) ranges))))
9690         (insert "\n")
9691         (setq newsrc (cdr newsrc)))
9692       (write-region 1 (point-max) gnus-startup-file nil 'nomesg)
9693       (kill-buffer (current-buffer)))))
9694
9695 (defun gnus-ranges-to-newsrc-format (ranges)
9696   "Insert ranges of read articles."
9697   (let ((range nil))                    ;Range is a pair of BEGIN and END.
9698     (while ranges
9699       (setq range (car ranges))
9700       (setq ranges (cdr ranges))
9701       (cond ((= (car range) (cdr range))
9702              (if (= (car range) 0)
9703                  (setq ranges nil)      ;No unread articles.
9704                (insert (int-to-string (car range)))
9705                (if ranges (insert ","))
9706                ))
9707             (t
9708              (insert (int-to-string (car range))
9709                      "-"
9710                      (int-to-string (cdr range)))
9711              (if ranges (insert ","))
9712              ))
9713       )))
9714
9715 (defun gnus-read-descriptions-file ()
9716   (message "Reading descriptions file...")
9717   (if (not (gnus-request-list-newsgroups gnus-select-method))
9718       (progn
9719         (message "Couldn't read newsgroups descriptions")
9720         nil)
9721     (let (group)
9722       (setq gnus-description-hashtb 
9723             (gnus-make-hashtable (length gnus-active-hashtb)))
9724       (save-excursion
9725         (save-restriction
9726           (set-buffer nntp-server-buffer)
9727           (goto-char (point-min))
9728           (delete-non-matching-lines "^[a-zA-Z\\.0-9]+[ \t]")
9729           (goto-char (point-min))
9730           (if (or (search-forward "\n.\n" nil t)
9731                   (goto-char (point-max)))
9732               (progn
9733                 (beginning-of-line)
9734                 (narrow-to-region (point-min) (point))))
9735           (goto-char (point-min))
9736           (while (not (eobp))
9737             (setq group (let ((obarray gnus-description-hashtb))
9738                           (read (current-buffer))))
9739             (skip-chars-forward " \t")
9740             (set group (buffer-substring 
9741                         (point) (save-excursion (end-of-line) (point))))
9742             (forward-line 1))))
9743       (message "Reading descriptions file...done")
9744       t)))
9745
9746 (provide 'gnus)
9747
9748 ;;; gnus.el ends here