1 ;;; (ding) Gnus: a newsreader for GNU Emacs
2 ;; Copyright (C) 1987,88,89,90,93,94 Free Software Foundation, Inc.
4 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
5 ;; Lars Ingebrigtsen <larsi@ifi.uio.no>
8 ;; This file is part of GNU Emacs.
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)
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.
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.
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
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)
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.
54 For instance, if you want to get your news via NNTP from
55 \"flab.flab.edu\" on port 23, you could say:
57 (setq gnus-select-method '(nntp \"flab.flab.edu\" 23))
59 If you want to use your local spool, say:
61 (setq gnus-select-method (list 'nnspool (system-name)))
63 If you use this variable, you must set `gnus-nntp-server' to nil.")
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.
73 The value must be a valid method as discussed in the documentation of
74 `gnus-select-method'.")
76 (defvar gnus-default-nntp-server nil
77 "*Specify a default NNTP server.
78 This variable should be defined in paths.el.")
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.")
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'
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:
97 (setq gnus-select-method '(nntp "my.nntp.server" 899))")
99 (defvar gnus-startup-file "~/.newsrc"
100 "*Your `.newsrc' file. Use `.newsrc-SERVER' instead if it exists.")
102 (defvar gnus-signature-file "~/.signature"
103 "*Your `.signature' file.")
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
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.")
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.")
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).")
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
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.")
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
145 Here's an example `gnus-followup-to-function':
147 (setq gnus-followup-to-function
149 (cond ((string= group \"mail.list\")
150 (or (mail-fetch-field \"sender\")
151 (mail-fetch-field \"from\")))
153 (or (mail-fetch-field \"reply-to\")
154 (mail-fetch-field \"from\"))))))")
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.")
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.")
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.
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:
176 \(setq gnus-author-copy \"|/usr/local/lib/mh/rcvstore +Article\")")
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.")
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.")
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.")
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.")
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).
199 Gnus provides the following functions:
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).")
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.")
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.")
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.")
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.")
222 (defvar gnus-kill-file-name "KILL"
223 "*Suffix of the kill files.")
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
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*.")
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.")
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.")
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.")
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].")
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].")
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.")
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.
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
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.")
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.")
305 (defvar gnus-interactive-catchup t
306 "*Require your confirmation when catching up a newsgroup if non-nil.")
308 (defvar gnus-interactive-post t
309 "*Newsgroup and subject will be asked for if non-nil.")
311 (defvar gnus-interactive-exit t
312 "*Require your confirmation when exiting Gnus if non-nil.")
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
320 (defvar gnus-user-login-name nil
321 "*The login name of the user.
322 Got from the function `user-login-name' if undefined.")
324 (defvar gnus-user-full-name nil
325 "*The full name of the user.
326 Got from the NAME environment variable if undefined.")
328 (defvar gnus-show-mime nil
329 "*Show MIME message if non-nil.")
331 (defvar gnus-show-threads t
332 "*Show conversation threads in Summary Mode if non-nil.")
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.")
339 (defvar gnus-thread-hide-killed t
340 "*Non-nil means hide killed thread subtrees automatically.")
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.")
346 (defvar gnus-thread-indent-level 4
347 "*Indentation of thread subtrees.")
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
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
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.")
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'.")
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'.")
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
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
392 (defvar gnus-show-all-headers nil
393 "*Show all headers of an article if non-nil.")
395 (defvar gnus-save-all-headers t
396 "*Save all headers of an article if non-nil.")
398 (defvar gnus-inhibit-startup-message nil
399 "The startup message will not be displayed if this function is non-nil.")
401 (defvar gnus-auto-extend-newsgroup t
402 "*Extend visible articles to forward and backward if non-nil.")
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'.")
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.")
418 (defvar gnus-auto-select-same nil
419 "*Select the next article with the same subject automagically if non-nil.")
421 (defvar gnus-auto-center-summary t
422 "*Always center the current summary in Gnus Summary window if non-nil.")
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'.")
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'.")
433 (defvar gnus-page-delimiter "^\^L"
434 "*Regexp describing line-beginnings that separate pages of news article.")
436 (defvar gnus-digest-show-summary t
437 "*Show a summary of undigestified messages if non-nil.")
439 (defvar gnus-digest-separator "^Subject:[ \t]"
440 "*Regexp that separates messages in a digest article.")
442 (defvar gnus-use-full-window t
443 "*Non-nil means to take up the entire screen of Emacs.")
445 (defvar gnus-window-configuration
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'.")
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.")
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.")
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.")
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.")
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'.")
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.")
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
503 options -n no.all alt.all
505 Gnus will the subscribe all new newsgroups in these hierarchies with
506 the subscription method in this variable.")
508 (defvar gnus-group-mode-hook nil
509 "*A hook for Gnus Group Mode.")
511 (defvar gnus-summary-mode-hook nil
512 "*A hook for Gnus Summary Mode.")
514 (defvar gnus-article-mode-hook nil
515 "*A hook for Gnus Article Mode.")
517 (defvar gnus-kill-file-mode-hook nil
518 "*A hook for Gnus KILL File Mode.")
520 (defvar gnus-open-server-hook nil
521 "*A hook called just before opening connection to news server.")
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
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.")
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.")
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.")
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'.")
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:
552 (setq gnus-select-group-hook
555 ;; First of all, sort by date.
556 (gnus-keysort-headers
557 (function string-lessp)
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)
566 (downcase (gnus-simplify-subject (gnus-header-subject a) t))
567 (gnus-simplify-subject (gnus-header-subject a) t)))))))
569 If you'd like to simplify subjects like the
570 `gnus-summary-next-same-subject' command does, you can use the
573 (setq gnus-select-group-hook
576 (mapcar (lambda (header)
579 (gnus-simplify-subject
580 (gnus-header-subject header) 're-only)))
581 gnus-newsgroup-headers))))
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'.
590 If you'd like to run RMAIL on a digest article automagically, you can
591 use the following hook:
593 \(setq gnus-select-article-hook
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)
605 (defvar gnus-select-digest-hook
608 ;; Reply-To: is required by `undigestify-rmail-message'.
609 (or (mail-position-on-field "Reply-to" t)
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):
617 \(setq gnus-select-digest-hook
620 ;; Reply-To: is required by `undigestify-rmail-message'.
621 (or (mail-position-on-field \"Reply-to\" t)
623 (mail-position-on-field \"Reply-to\")
624 (insert (gnus-fetch-field \"From\")))))))")
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.")
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.
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
641 \(setq gnus-apply-kill-hook
644 (cond ((string-match \"control\" gnus-newsgroup-name)
645 (gnus-kill \"Subject\" \"rmgroup\")
646 (gnus-expunge \"X\"))))))")
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'.")
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
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.")
664 (defvar gnus-suspend-gnus-hook nil
665 "*A hook called when suspending (not exiting) Gnus.")
667 (defvar gnus-exit-gnus-hook (list 'nntp-request-close)
668 "*A hook called when exiting Gnus.")
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.")
674 (defvar gnus-auto-expirable-newsgroups nil
675 "*All newsgroups that match this regexp will have all read articles automatically marked as expirable.")
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:
681 'alt.binaries': Do you want to subscribe to this hierarchy? ([d]ys):
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.")
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.
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
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
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
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
721 It works along the same lines as a normal formatting string,
722 with some simple extensions.
724 %N Article number, left padded with spaces (integer)
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 \"=\")
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.
747 (defvar gnus-summary-mode-line-format "(ding) %G/%A %Z"
748 "*The format specification for the Summary mode line.")
750 (defvar gnus-article-mode-line-format "(ding) %G/%A %S"
751 "*The format specification for the Article mode line.")
753 (defconst gnus-group-mode-line-format "(ding) List of Newsgroups {%M:%S}"
754 "*The format specification for the Newsgroup mode line.")
758 ;; Site dependent variables. You have to define these variables in
759 ;; site-init.el, default.el or your .emacs.
761 (defvar gnus-local-timezone nil
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.
768 For backwards compatibility, it may also be a string like \"JST\",
769 but strings are obsolescent: you should use numeric offsets instead.")
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.")
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.")
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.")
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)")
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.")
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.")
809 ;; Internal variables.
811 (defvar caesar-translate-table nil)
813 (defvar gnus-dribble-buffer nil)
815 (defvar gnus-article-reply nil)
817 (defvar gnus-newsgroup-dependencies nil)
819 (defconst gnus-group-edit-buffer "*Gnus edit newsgroup*")
821 (defvar gnus-default-subscribe-level 2
822 "Default subscription level.")
824 (defvar gnus-default-unsubscribe-level 6
825 "Default unsubscription level.")
827 (defvar gnus-default-kill-level 9
828 "Default kill level.")
830 (defconst gnus-group-line-format-alist
831 (list (list ?M 'marked ?c)
832 (list ?S 'subscribed ?c)
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)))
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)
848 (list ?A 'address ?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)
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).")
867 (defconst gnus-summary-dummy-line-format-alist
868 (list (list ?S 'subject ?s)
869 (list ?N 'number ?d)))
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)
877 (list ?S 'subject ?s)
878 (list ?u 'unselected ?d)))
880 (defconst gnus-group-mode-line-format-alist
881 (list (list ?S 'news-server ?s)
882 (list ?M 'news-method ?s)))
884 (defvar gnus-have-read-active-file nil)
886 (defconst gnus-foreign-group-prefix "foreign.")
888 (defconst gnus-version "(ding) Gnus v0.5"
889 "Version numbers of this version of Gnus.")
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.")
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")
904 (defvar gnus-buffer-list nil
905 "Gnus buffers that should be killed when exiting.")
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.")
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)'.")
919 (defvar gnus-newsrc-options nil
920 "Options line in the .newsrc file.")
922 (defvar gnus-newsrc-options-n-yes nil
923 "Regexp representing subscribed newsgroups.")
925 (defvar gnus-newsrc-options-n-no nil
926 "Regexp representing unsubscribed newsgroups.")
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.")
932 (defvar gnus-newsrc-hashtb nil
933 "Hashtable of gnus-newsrc-assoc.")
935 (defvar gnus-killed-list nil
936 "List of killed newsgroups.")
938 (defvar gnus-killed-hashtb nil
939 "Hash table equivalent of gnus-killed-list.")
941 (defvar gnus-zombie-list nil
942 "List of almost dead newsgroups.")
944 (defvar gnus-description-hashtb nil
945 "Descriptions of newsgroups (from the file 'newsgroups').")
947 (defvar gnus-list-of-killed-groups nil
948 "List of newsgroups that have recently been killed by the user.")
950 (defvar gnus-xref-hashtb nil
951 "Hash table of cross-posted articles.")
953 (defvar gnus-active-hashtb nil
954 "Hashtable of active articles.")
956 (defvar gnus-moderated-list nil
957 "List of moderated newsgroups.")
959 (defvar gnus-current-startup-file nil
960 "Startup file for the current host.")
962 (defvar gnus-last-search-regexp nil
963 "Default regexp for article search command.")
965 (defvar gnus-last-shell-command nil
966 "Default shell command on article.")
968 (defvar gnus-current-select-method nil
969 "The current method for selecting a newsgroup.")
971 (defvar gnus-have-all-newsgroups nil)
973 (defvar gnus-article-internal-prepare-hook nil)
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.")
985 (defvar gnus-newsgroup-unreads nil
986 "List of unread articles in the current newsgroup.")
988 (defvar gnus-newsgroup-unselected nil
989 "List of unselected unread articles in the current newsgroup.")
991 (defvar gnus-newsgroup-marked nil
992 "List of ticked articles in the current newsgroup (a subset of unread art).")
994 (defvar gnus-newsgroup-killed nil
995 "List of ranges of articles that have been through the kill process.")
997 (defvar gnus-newsgroup-replied nil
998 "List of articles that have been replied to in the current newsgroup.")
1000 (defvar gnus-newsgroup-expirable nil
1001 "List of articles in the current newsgroup that can be expired.")
1003 (defvar gnus-newsgroup-processable nil
1004 "List of articles in the current newsgroup that can be processed.")
1006 (defvar gnus-newsgroup-bookmarks nil
1007 "List of articles in the current newsgroup that have bookmarks.")
1009 (defvar gnus-newsgroup-interesting nil
1010 "List of interesting articles in the current newsgroup.")
1012 (defvar gnus-newsgroup-headers nil
1013 "List of article headers in the current newsgroup.")
1014 (defvar gnus-newsgroup-headers-hashtb-by-number nil)
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)
1024 ;; Save window configuration.
1025 (defvar gnus-winconf-kill-file nil)
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)
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)
1040 (defvar gnus-reffed-article-number nil)
1042 (defvar rmail-default-file (expand-file-name "~/XMBOX"))
1043 (defvar rmail-default-rmail-file (expand-file-name "~/XNEWS"))
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))
1059 (defvar gnus-mark-article-hook
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.
1069 If you'd like to tick articles instead, use the following hook:
1071 \(setq gnus-mark-article-hook
1074 (gnus-summary-tick-article gnus-current-article)
1075 (gnus-summary-set-current-mark \"+\"))))")
1077 ;; Define some autoload functions Gnus may use.
1079 (autoload 'metamail-buffer "metamail")
1080 (autoload 'Info-goto-node "info")
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")
1087 (autoload 'rmail-output "rmailout"
1088 "Append this message to Unix mail file named FILE-NAME." t)
1089 (autoload 'mail-position-on-field "sendmail")
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"))
1098 (put 'gnus-group-mode 'mode-class 'special)
1099 (put 'gnus-summary-mode 'mode-class 'special)
1100 (put 'gnus-article-mode 'mode-class 'special)
1102 (autoload 'gnus-uu-ctl-map "gnus-uu" nil nil 'keymap)
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)
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)))
1116 (pop-to-buffer (, buffer))
1118 (select-window GnusStartBufferWindow)))))
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)
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)))))
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))))
1139 (defsubst gnus-buffer-substring (beg end)
1140 (buffer-substring (match-beginning beg) (match-end end)))
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))
1150 ;;; Gnus Utility Functions
1153 (defsubst gnus-extract-address-components (from)
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))))
1165 (defun gnus-fetch-field (field)
1166 "Return the value of the header FIELD of current article."
1169 (gnus-narrow-to-headers)
1170 (mail-fetch-field field))))
1172 (defun gnus-goto-colon ()
1174 (search-forward ":" (save-excursion (end-of-line) (point)) t))
1176 (defun gnus-prefs-p (&rest values)
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))
1192 (defun gnus-narrow-to-headers ()
1196 (if (search-forward "\n\n")
1197 (narrow-to-region 1 (1- (point))))))
1199 ;; Get a number that is suitable for hashing; bigger than MIN
1200 (defun gnus-create-hash-size (min)
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
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)))
1230 (defun gnus-format-max-width (var length)
1232 (if (> (length (setq result (eval var))) length)
1233 (format "%s" (substring result 0 length))
1234 (format "%s" result))))
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
1243 spec flist fstring b newspec max-width elem beg)
1245 (set-buffer (get-buffer-create "*gnus work*"))
1246 (buffer-disable-undo (current-buffer))
1247 (gnus-add-current-to-buffer-list)
1251 (while (re-search-forward "%[-0-9]*\\(,[0-9]*\\)*\\(.\\)" nil t)
1252 (setq spec (string-to-char (buffer-substring (match-beginning 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))
1260 (buffer-substring (1+ (match-beginning 1)) (match-end 1))))
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))
1268 (setq flist (cons (list 'gnus-format-max-width
1269 (car elem) max-width) flist))
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.
1278 (setq fstring (buffer-substring 1 (point-max)))
1279 (kill-buffer (current-buffer)))
1280 (cons 'format (cons fstring (nreverse flist)))))
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)))
1288 ;; Article file names when saving.
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."
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"))))
1302 (string-equal (file-name-directory default)
1303 (file-name-directory last-file))
1304 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1306 (or last-file default))))
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."
1314 (concat (if gnus-use-long-file-name
1316 (gnus-newsgroup-directory-form newsgroup))
1317 "/" (int-to-string (header-number headers)))
1318 (or gnus-article-save-directory "~/News"))))
1320 (string-equal (file-name-directory default)
1321 (file-name-directory last-file))
1322 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1324 (or last-file default))))
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."
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"))))
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."
1343 (if gnus-use-long-file-name
1345 (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
1346 (or gnus-article-save-directory "~/News"))))
1348 ;; For subscribing new newsgroup
1350 (defun gnus-subscribe-hierarchical-interactive (groups)
1351 (let ((groups (sort groups 'string<))
1352 prefixes prefix start rest ans group starts)
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)
1363 (concat "^" (substring (car groups) 0 (match-end 0))))
1364 (string-match prefix (car (cdr groups))))
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))
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)))
1381 (string-match prefix
1382 (setq group (car groups))))
1383 (gnus-sethash group group gnus-killed-hashtb)
1384 (funcall gnus-subscribe-newsgroup-method
1386 (setq groups (cdr groups)))
1387 (setq starts (cdr starts)))
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))))
1395 (message "Subscribe '%s'? ([n]yq)" (car groups))
1396 (setq ans (read-char))
1398 (funcall gnus-subscribe-newsgroup-method (car groups))
1399 (gnus-sethash group group gnus-killed-hashtb))
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))))
1407 (setq gnus-killed-list (cons group gnus-killed-list))
1408 (gnus-sethash group group gnus-killed-hashtb)))
1409 (setq groups (cdr groups)))))))
1411 (defun gnus-subscribe-randomly (newsgroup)
1412 "Subscribe new NEWSGROUP by making it the first newsgroup."
1413 (gnus-subscribe-newsgroup newsgroup))
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))
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)))
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)
1430 (set-buffer (find-file-noselect gnus-current-startup-file))
1431 (let ((groupkey newgroup)
1433 (while (and (not before) groupkey)
1434 (goto-char (point-min))
1436 (concat "^\\(" (regexp-quote groupkey) ".*\\)[!:]")))
1437 (while (and (re-search-forward groupkey-re nil t)
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)
1444 (if (string-match "^\\(.*\\)\\.[^.]+$" groupkey)
1445 (substring groupkey (match-beginning 1) (match-end 1)))))
1446 (gnus-subscribe-newsgroup newgroup before))))
1448 (defun gnus-subscribe-interactively (newsgroup)
1449 "Subscribe new NEWSGROUP interactively.
1450 It is inserted in hierarchical newsgroup order if subscribed. If not,
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))))
1456 (defun gnus-subscribe-zombies (newsgroup)
1457 "Make new NEWSGROUP a zombie group."
1458 (setq gnus-zombie-list (cons newsgroup gnus-zombie-list)))
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
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))
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))
1478 ;; Replace all occurrences of `.' with `/'.
1480 (if (= (aref newsgroup idx) ?.)
1481 (aset newsgroup idx ?/))
1482 (setq idx (1+ idx)))
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))
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)
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)))
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))))
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.
1530 (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1531 (setq subject (substring subject 0 (match-beginning 0)))))
1532 ;; Return subject string.
1536 (defun gnus-add-current-to-buffer-list ()
1537 (setq gnus-buffer-list (cons (current-buffer) gnus-buffer-list)))
1539 ;; Functions accessing headers.
1540 ;; Functions are more convenient than macros in some case.
1542 (defun gnus-header-number (header)
1543 "Return article number in HEADER."
1544 (header-number header))
1546 (defun gnus-header-subject (header)
1547 "Return subject string in HEADER."
1548 (header-subject header))
1550 (defun gnus-header-from (header)
1551 "Return author string in HEADER."
1552 (header-from header))
1554 (defun gnus-header-xref (header)
1555 "Return xref string in HEADER."
1556 (header-xref header))
1558 (defun gnus-header-lines (header)
1559 "Return lines in HEADER."
1560 (header-lines header))
1562 (defun gnus-header-date (header)
1563 "Return date in HEADER."
1564 (header-date header))
1566 (defun gnus-header-id (header)
1567 "Return Id in HEADER."
1570 (defun gnus-header-references (header)
1571 "Return references in HEADER."
1572 (header-references header))
1574 (defun gnus-clear-system ()
1575 "Clear all variables and buffers."
1576 ;; Clear Gnus variables.
1577 (let ((variables gnus-variable-list))
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))))
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
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))
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)))
1636 (zerop (nth 1 windows)))
1638 (zerop (nth 2 windows))))))
1639 ;; No need to change window configuration.
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))))
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))
1682 (switch-to-buffer gnus-group-buffer 'norecord)
1684 (and (not (zerop subheight))
1686 (switch-to-buffer gnus-summary-buffer 'norecord)
1688 (and (not (zerop artheight))
1690 ;; If Article buffer does not exist, it will be created
1692 (gnus-article-setup-buffer)
1693 (switch-to-buffer gnus-article-buffer 'norecord)))))
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)
1707 (gnus-article-setup-buffer)
1708 (switch-to-buffer gnus-article-buffer t))))
1710 (defun gnus-version ()
1711 "Version numbers of this version of Gnus."
1713 (let ((methods gnus-valid-select-methods)
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
1721 (setq meth (intern (concat (car (car methods)) "-version")))
1723 (stringp (symbol-value meth))
1724 (setq mess (concat mess "; " (symbol-value meth))))
1725 (setq methods (cdr methods)))
1728 (defun gnus-info-find-node ()
1729 "Find Info documentation of Gnus."
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)))))
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."
1744 (overloads (or overloads gnus-overload-functions)))
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)))
1756 ;; List and range functions
1758 (defun gnus-last-element (list)
1759 "Return last element of LIST."
1761 (setq list (cdr list)))
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)))
1768 (setq list1 (delq (car list2) list1))
1769 (setq list2 (cdr list2)))
1773 (defun gnus-intersection (list1 list2)
1776 (if (memq (car list2) list1)
1777 (setq result (cons (car list2) result)))
1778 (setq list2 (cdr list2)))
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
1786 (let* ((numbers (sort numbers (function <)))
1787 (first (car numbers))
1788 (last (car 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))
1801 (nreverse (cons (cons first last) result)))))
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
1807 (let (first last result)
1808 (if (atom (car ranges))
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))))
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))))
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))
1829 range nranges first last)
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)
1847 (if (and (cdr ranges) (>= (1+ (cdr (car ranges)))
1848 (car (car (cdr ranges)))))
1850 (setcdr (car ranges) (cdr (car (cdr ranges))))
1851 (setcdr ranges (cdr (cdr ranges))))
1852 (setq ranges (cdr ranges))))
1853 (if (not (cdr inrange))
1857 (defun gnus-member-of-range (number ranges)
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)))
1871 (if gnus-group-mode-map
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)
1930 ;; Make a menu bar item.
1931 (define-key gnus-group-mode-map [menu-bar Gnus]
1932 (cons "Gnus" (make-sparse-keymap "Gnus")))
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))
1947 ;; Make a menu bar item.
1948 (define-key gnus-group-mode-map [menu-bar groups]
1949 (cons "Groups" (make-sparse-keymap "Groups")))
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))
1956 (define-key gnus-group-mode-map [menu-bar groups separator-2]
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))
1964 (define-key gnus-group-mode-map [menu-bar groups separator-1]
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))
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:
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
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))
2044 (defun gnus-mouse-pick-group (e)
2047 (gnus-group-read-group nil))
2049 (defalias '\(ding\) 'gnus)
2052 (defun gnus (&optional arg)
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."
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)))
2065 (switch-to-buffer (get-buffer-create gnus-group-buffer))
2066 (gnus-add-current-to-buffer-list)
2068 (gnus-start-news-server (and arg (not level))))
2069 (if (not (gnus-server-opened gnus-select-method))
2071 ;; NNTP server is successfully open.
2072 (gnus-update-format-specifications)
2073 (let ((buffer-read-only nil))
2075 (if (not gnus-inhibit-startup-message)
2077 (gnus-group-startup-message)
2079 (run-hooks 'gnus-startup-hook)
2080 (gnus-setup-news nil (or level 7))
2082 (or (not gnus-novice-user)
2084 (gnus-group-describe-briefly)) ;Show brief help message.
2085 (gnus-group-list-groups (or level 5))))))
2087 (defun gnus-group-startup-message (&optional x y)
2088 "Insert startup message in current buffer."
2089 ;; Insert the message.
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)))
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."
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))
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))))
2136 (not (re-search-forward
2137 (gnus-group-make-regexp (car (car newsrc)))
2139 (setq newsrc (cdr newsrc))))))
2140 ;; Adjust cursor point.
2141 (gnus-group-position-cursor))))
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)
2158 ;; List alive newsgroups.
2160 (setq info (car 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))
2168 (gnus-group-insert-group-line
2169 nil group (car (cdr info)) (nth 3 info) unread
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))
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)))))
2185 (setq newsrc (set (car lists)
2186 (sort (symbol-value (car lists))
2187 (function string<))))
2189 (setq group (car newsrc)
2190 newsrc (cdr newsrc))
2191 (insert (format " %c *: %s" mark group))
2193 (insert (format " %s %d\n" group
2194 (if (= mark ?Z) 8 9)))
2195 (set-text-properties beg (1- (point))
2197 (setq lists (cdr lists))))
2199 (gnus-group-set-mode-line)
2200 (setq gnus-have-all-newsgroups all)
2201 (run-hooks 'gnus-group-prepare-hook)))
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))
2209 (defun gnus-group-set-info (info)
2210 (let ((entry (gnus-gethash (car info) gnus-newsrc-hashtb)))
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
2218 (error "No such group: %s" (car info)))))
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)))
2228 (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2231 (delete-region (point) (save-excursion (forward-line 1) (point)))
2232 (gnus-group-insert-group-line-info group)
2234 (gnus-group-position-cursor)))
2236 (defun gnus-group-insert-group-line-info (group)
2237 (let ((entry (gnus-gethash group gnus-newsrc-hashtb))
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))))
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))
2254 (subscribed (cond ((< level 6) ? )
2258 (buffer-read-only nil)
2259 (newsgroup-description
2260 (if gnus-description-hashtb
2261 (or (gnus-gethash group gnus-description-hashtb) "")
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) ""))
2268 (if method (format "(%s:%s)" (car method) (car (cdr method))) ""))
2269 (number (if (eq number t) "*" number))
2272 (let ((group (if method (gnus-group-real-name group) group)))
2273 ;; Insert the visible text.
2274 (insert (eval gformat)))
2276 ;; Insert the invisible info on the end of the line.
2278 ;; The info is GROUP UNREAD MARKED LEVEL.
2281 group (if (or (stringp number) (> number 0)) ?+ ? )
2283 (set-text-properties b (point) '(invisible t))
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))
2293 (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
2296 (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2298 ;; Buffer may be narrowed.
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.
2308 (looking-at regexp)))
2309 ((and (> (/ (buffer-size) 2) (point)) ;In the first half.
2311 (goto-char (point-min))
2312 (re-search-forward regexp nil t))))
2314 (goto-char (point-max))
2315 (re-search-backward regexp nil t))))
2316 ;; GROUP is listed in current buffer. So, delete old 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))
2326 (not (re-search-forward (gnus-group-make-regexp
2327 (car (car entry))) nil t)))
2328 (setq entry (cdr entry)))
2330 (goto-char (point-max)))))
2331 (if (or visible (not visible-only))
2333 (gnus-group-insert-group-line-info group)
2334 (forward-line -1) ; Move point back to the inserted line.
2336 (gnus-group-set-mode-line))
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
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))
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))))
2354 (defun gnus-group-group-name ()
2355 "Get the name of the newsgroup on the current line."
2357 (let ((buffer-read-only nil))
2359 (if (re-search-forward " \\([^ ]*\\)...$" nil t)
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)))))))
2366 (defun gnus-group-group-level ()
2367 "Get the level of the newsgroup on the current line."
2371 (let ((c (following-char)))
2372 (if (and (>= c ?1) (<= c ?9))
2375 (defun gnus-group-make-regexp (newsgroup)
2376 "Return regexp that will match the line that NEWSGROUP is on."
2377 (concat " " (regexp-quote newsgroup) "...$"))
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
2386 (let ((regexp (if all "...$" "\\+.[1-5]$")))
2391 (re-search-backward regexp nil t))
2393 (re-search-forward regexp nil t))
2394 (gnus-group-position-cursor)))
2395 (let ((beg (point)))
2396 (while (and (< level 10)
2398 (let ((regexp (format "%s.%d$" (if all "." "\\+") level)))
2403 (re-search-backward regexp nil t))
2405 (re-search-forward regexp nil t)))))
2406 (setq level (1+ level)))
2409 ;; Gnus Group mode command
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."
2416 (let ((group (gnus-group-group-name))
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))))
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)))
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."
2435 (gnus-group-read-group all t))
2437 (defun gnus-group-jump-to-group (group)
2438 "Jump to newsgroup GROUP."
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)))
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
2457 (gnus-group-next-unread-group n t))
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
2466 Returns the difference between N and the number of skips actually
2469 (let ((backward (< n 0))
2472 (gnus-group-search-forward backward all level))
2474 (if (/= 0 n) (message "No more%s newsgroups%s" (if all "" " unread")
2475 (if level " on this level or higher" "")))
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
2483 (gnus-group-next-unread-group (- n) t))
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
2490 (gnus-group-next-unread-group (- n)))
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
2498 (gnus-group-next-unread-group n t (gnus-group-group-level))
2499 (gnus-group-position-cursor))
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
2506 (gnus-group-next-unread-group (- n) t (gnus-group-group-level))
2507 (gnus-group-position-cursor))
2509 (defun gnus-group-add-newsgroup (&optional name how where)
2510 "Add a new newsgroup."
2512 (let ((methods gnus-valid-select-methods)
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)))
2521 (setq how (completing-read (format "%s method: " name) methods nil t)))
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)
2529 (gnus-group-insert-group-line-info nname)))
2531 (defun gnus-group-edit-newsgroup ()
2533 (let ((group (gnus-group-group-name))
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)
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)))
2545 (defun gnus-group-edit-newsgroup-done ()
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))
2554 (defun gnus-group-make-mail-groups (method)
2555 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
2561 (gnus-methods-using 'mail) nil t "nnmail"))))
2562 (let ((groups nnmail-split-methods)
2565 (setq group (concat gnus-foreign-group-prefix (car (car groups))))
2566 (if (not (gnus-gethash group gnus-newsrc-hashtb))
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)
2572 (gnus-group-insert-group-line-info group)))
2573 (setq groups (cdr groups)))))
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."
2583 (if (or (not gnus-interactive-catchup) ;Without confirmation?
2587 "Do you really want to mark all articles as read? "
2588 "Mark all unread articles as read? ")))
2594 (gnus-group-catchup (gnus-group-group-name) all)
2595 (gnus-group-update-group-line)
2597 (= 0 (gnus-group-next-unread-group 1))))))
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."
2604 (gnus-group-catchup-current n 'all))
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))
2614 ;; Do the updating only if the newsgroup isn't killed
2617 (setq ticked (if all nil (cdr (assq 'tick (nth 3 (nth 2 entry))))))
2618 (gnus-update-read-articles group ticked nil ticked)))
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"))
2626 (assq 'expire (nth 3 (nth 2 (gnus-gethash newsgroup
2627 gnus-newsrc-hashtb))))))
2629 (gnus-check-backend-function
2630 'gnus-request-expire-articles newsgroup))
2632 (gnus-request-expire-articles (cdr expirable) newsgroup)))))
2634 (defun gnus-group-expire-all-groups ()
2635 "Expire all expirable articles in all newsgroups."
2637 (let ((newsrc (cdr gnus-newsrc-assoc)))
2639 (gnus-group-expire-articles (car (car newsrc)))
2640 (setq newsrc (cdr newsrc)))))
2642 (defun gnus-group-set-current-level (n)
2643 "Set the level of the current group to the numeric prefix."
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))
2649 (gnus-group-change-level group n (gnus-group-group-level))
2650 (gnus-group-update-group-line))
2651 (error "Illegal level: %s" n)))))
2653 (defun gnus-group-unsubscribe-current-group (arg)
2654 "Toggle subscribe from/to unsubscribe current group."
2656 (let ((group (gnus-group-group-name)))
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"))))
2665 (defun gnus-group-unsubscribe-group (group &optional level)
2666 "Toggle subscribe from/to unsubscribe GROUP.
2667 New newsgroup is added to .newsrc automatically."
2669 (list (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
2670 (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
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
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)))
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."
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.
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)
2708 (defun gnus-group-kill-all-zombies ()
2709 "Kill all zombie newsgroups."
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))
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]."
2722 ;; Exclude a line where current point is on.
2736 (beginning-of-line) ;Important when LINES < 1
2737 (gnus-group-kill-group lines)))
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."
2746 (let ((buffer-read-only nil)
2748 (while (>= (setq n (1- n)) 0)
2749 (setq group (gnus-group-group-name))
2751 (signal 'end-of-buffer nil))
2752 (setq level (gnus-group-group-level))
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)))
2765 (gnus-group-position-cursor)
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."
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
2784 (while (and (not (setq prev (gnus-group-group-name)))
2785 (= 0 (forward-line -1))))
2787 (setq prev (car (car gnus-newsrc-assoc))))
2788 (gnus-group-change-level
2790 (gnus-gethash prev gnus-newsrc-hashtb)
2792 (gnus-group-insert-group-line-info (nth 1 info))
2793 (setq gnus-list-of-killed-groups
2794 (cdr gnus-list-of-killed-groups)))
2796 (gnus-group-position-cursor)
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."
2803 (setq arg (or arg 7))
2804 (gnus-group-list-groups arg t))
2806 (defun gnus-group-list-killed ()
2807 "List all killed newsgroups in the Newsgroup buffer."
2809 (gnus-group-prepare 9 t 9)
2810 (goto-char (point-min))
2811 (gnus-group-position-cursor))
2813 (defun gnus-group-list-zombies ()
2814 "List all zombie newsgroups in the Newsgroup buffer."
2816 (gnus-group-prepare 8 t 8)
2817 (goto-char (point-min))
2818 (gnus-group-position-cursor))
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."
2825 (if (and gnus-read-active-file (not arg))
2826 (gnus-read-active-file))
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))
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."
2838 (let ((backward (< n 0))
2843 (and (setq group (gnus-group-group-name))
2844 (gnus-activate-newsgroup
2845 group (gnus-group-real-name group))
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)))
2852 (= 0 (gnus-group-next-group 1)))
2854 (if (/= 0 n) (message "No more newsgroups"))
2857 (defun gnus-group-describe-group (&optional group)
2858 "Display a description of the current newsgroup."
2860 (let ((group (or group (gnus-group-group-name))))
2862 (message "No group on current line")
2863 (and (or gnus-description-hashtb
2864 (gnus-read-descriptions-file))
2866 (or (gnus-gethash group gnus-description-hashtb)
2867 "No description available"))))))
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."
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)
2881 (insert (format " *: %-20s %s" (symbol-name group)
2882 (symbol-value group)))
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)))
2890 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
2891 (defun gnus-group-save-newsrc ()
2892 "Save the Gnus startup files."
2894 (gnus-save-newsrc-file))
2896 (defun gnus-group-restart (&optional arg)
2897 "Force Gnus to read the .newsrc file."
2899 (gnus-save-newsrc-file)
2900 (gnus-setup-news 'force)
2901 (gnus-group-list-groups (or arg 5) gnus-have-all-newsgroups))
2903 (defun gnus-group-read-init-file ()
2904 "Read the Gnus elisp init file."
2906 (gnus-read-init-file))
2908 (defun gnus-group-check-bogus-groups ()
2909 "Check bogus newsgroups."
2911 (gnus-check-bogus-newsgroups (not gnus-expert-user)) ;Require confirmation.
2912 (gnus-group-list-groups 5 gnus-have-all-newsgroups))
2914 (defun gnus-group-mail ()
2915 "Start composing a mail."
2919 (defun gnus-group-edit-global-kill ()
2920 "Edit a global KILL file."
2922 (setq gnus-current-kill-article nil) ;No articles selected.
2923 (gnus-kill-file-edit-file nil) ;Nil stands for global KILL file.
2925 (substitute-command-keys
2926 "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
2928 (defun gnus-group-edit-local-kill ()
2929 "Edit a local KILL file."
2931 (setq gnus-current-kill-article nil) ;No articles selected.
2932 (gnus-kill-file-edit-file (gnus-group-group-name))
2934 (substitute-command-keys
2935 "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
2937 (defun gnus-group-force-update ()
2938 "Update `.newsrc' file."
2940 (gnus-save-newsrc-file))
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."
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)))
2960 (defun gnus-group-clear-dribble ()
2961 "Clear all information from the dribble buffer."
2963 (gnus-dribble-clear))
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."
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
2974 (y-or-n-p "Are you sure you want to quit reading news? "))
2976 (message "") ;Erase "Yes or No" question.
2977 (run-hooks 'gnus-exit-gnus-hook)
2978 (gnus-save-newsrc-file)
2979 (gnus-clear-system))))
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."
2985 (if (or noninteractive ;For gnus-batch-kill
2986 (zerop (buffer-size))
2987 (not (gnus-server-opened gnus-select-method))
2990 (format "Quit reading news without saving %s? "
2991 (file-name-nondirectory gnus-current-startup-file))))
2993 (message "") ;Erase "Yes or No" question.
2994 (run-hooks 'gnus-exit-gnus-hook)
2996 (gnus-clear-system))))
2998 (defun gnus-group-describe-briefly ()
2999 "Give a one line description of the Group mode commands."
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")))
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."
3011 (list (list (completing-read "Select method: "
3012 gnus-valid-select-methods
3014 (read-string "Server name: "))))
3015 (gnus-browse-foreign-server method))
3019 ;;; Browse Server Mode
3022 (defvar gnus-browse-server-mode-hook nil)
3023 (defvar gnus-browse-server-mode-map nil)
3025 (if gnus-browse-server-mode-map
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)
3049 (defvar gnus-browse-current-method nil)
3051 (defun gnus-browse-foreign-server (method)
3052 (setq gnus-browse-current-method method)
3053 (let ((gnus-select-method method)
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))
3063 (gnus-browse-server-mode)
3064 (setq mode-line-buffer-identification
3066 "(ding) Browse Server {%s:%s}" (car method) (car (cdr method))))
3068 (set-buffer nntp-server-buffer)
3069 (let ((cur (current-buffer)))
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)
3076 (- (read cur) (read cur)))
3078 (setq groups (sort groups
3080 (string< (car l1) (car l2)))))
3081 (let ((buffer-read-only nil))
3083 (setq group (car groups))
3085 (format "K%7d: %s\n" (cdr group) (car group)))
3086 (setq groups (cdr groups))))
3087 (switch-to-buffer (current-buffer))
3089 (gnus-group-position-cursor)))
3091 (defun gnus-browse-server-mode ()
3092 "Major mode for reading network news."
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))
3105 (defun gnus-browse-read-group ()
3106 "Not implemented, and will probably never be."
3108 (error "You can't read while browsing"))
3110 (defun gnus-browse-unsubscribe-current-group (arg)
3111 "(Un)subscribe to the next ARG groups."
3113 (let ((ward (if (< arg 0) -1 1))
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" ))
3123 (defun gnus-browse-unsubscribe-group ()
3125 (buffer-read-only nil)
3129 (if (= (following-char) ?K) (setq sub t))
3130 (re-search-forward ": \\(.*\\)$" nil t)
3132 (concat gnus-foreign-group-prefix
3133 (buffer-substring (match-beginning 1) (match-end 1))))
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)
3143 (gnus-group-change-level group 9 3)
3147 (defun gnus-browse-exit ()
3148 "Quit browsing and return to the Newsgroup buffer."
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))
3155 (defun gnus-browse-describe-briefly ()
3156 "Give a one line description of the Group mode commands."
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")))
3163 ;;; Gnus Summary Mode
3166 (defvar gnus-summary-kill-map nil)
3167 (define-prefix-command 'gnus-summary-kill-map)
3169 (if gnus-summary-mode-map
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)
3274 (define-key gnus-summary-mode-map [menu-bar misc]
3275 (cons "Misc" (make-sparse-keymap "misc")))
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))
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))
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))
3294 (define-key gnus-summary-mode-map [menu-bar sort]
3295 (cons "Sort" (make-sparse-keymap "sort")))
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))
3306 (define-key gnus-summary-mode-map [menu-bar show/hide]
3307 (cons "Show/Hide" (make-sparse-keymap "show/hide")))
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))
3328 (define-key gnus-summary-mode-map [menu-bar action]
3329 (cons "Action" (make-sparse-keymap "action")))
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))
3344 (define-key gnus-summary-mode-map [menu-bar action ignore]
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))
3352 (define-key gnus-summary-mode-map [menu-bar action lambda]
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))
3368 (define-key gnus-summary-mode-map [menu-bar move]
3369 (cons "Move" (make-sparse-keymap "move")))
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))
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:
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
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
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
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
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
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
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
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
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
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
3501 (kill-all-local-variables)
3502 (let ((locals gnus-summary-local-variables))
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))
3524 (defun gnus-mouse-pick-article (e)
3527 (gnus-summary-next-page nil))
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)))
3537 (defun gnus-summary-insert-dummy-line (sformat subject number)
3539 (setq sformat gnus-summary-dummy-line-format-spec))
3542 (insert (eval sformat))
3545 (insert (format "%s Z %d 0" subject number))
3546 (set-text-properties b (point) '(invisible t))
3549 (defun gnus-summary-insert-line
3550 (sformat header level current unread replied expirable print-subject
3553 (setq sformat gnus-summary-line-format-spec))
3554 (let* ((thread-space (if (< level 1) "" (make-string (frame-width) ? )))
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 ?= ?\[))
3572 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
3573 (if (not (numberp lines)) (setq lines 0))
3575 (insert (eval sformat))
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))
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)
3592 ;; You can change the order of subjects in this hook.
3593 (run-hooks 'gnus-select-group-hook)
3594 (gnus-summary-prepare)
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.
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)))
3637 (kill-buffer (get-buffer kill-buffer))))))
3638 ;; Cannot select newsgroup GROUP.
3639 (message "Couldn't select newsgroup")
3640 (gnus-summary-position-cursor)))
3642 (defun gnus-summary-prepare ()
3643 "Prepare summary list of current newsgroup in Summary buffer."
3644 (let ((buffer-read-only nil))
3646 (gnus-summary-prepare-threads
3647 (if gnus-show-threads
3648 (gnus-gather-threads (gnus-make-threads))
3649 gnus-newsgroup-headers)
3651 (gnus-summary-delete-interesting)
3652 ;; Erase header retrieval 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)))
3659 (defun gnus-summary-delete-interesting ()
3660 (let ((int gnus-newsgroup-interesting)
3661 (buffer-read-only nil)
3664 (if (gnus-summary-goto-subject (car int))
3667 (setq cur-level (gnus-summary-thread-level))
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.
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)))))
3681 (defun gnus-gather-threads (threads)
3682 "Gather threads that have lost their roots."
3683 (if (not gnus-gather-loose-threads)
3685 (let ((hashtb (gnus-make-hashtable 1023))
3688 thread subject hthread)
3690 (setq subject (header-subject (car (car threads))))
3691 (if (setq hthread (gnus-gethash subject hashtb))
3693 (if (not (stringp (car (car hthread))))
3694 (setcar hthread (list subject (car hthread))))
3696 (append (car hthread) (cons (car threads) nil)))
3697 (setcdr prev (cdr threads))
3698 (setq threads prev))
3699 (gnus-sethash subject threads hashtb))
3701 (setq threads (cdr threads)))
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.
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
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)))
3725 (if (not (string= subject
3726 (gnus-simplify-subject-re
3727 (header-subject (car headers)))))
3729 (setq mroots (cons (car headers) mroots))
3730 (setcdr prev (cdr headers))))
3732 headers (cdr headers)))))))
3733 gnus-newsgroup-dependencies)
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.)
3742 (< (header-number h1) (header-number h2)))))
3743 ;; Now we have all the roots, so we go through all them all and
3745 (mapcar (lambda (root) (gnus-make-sub-thread root)) roots)))
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)))))
3754 (lambda (top) (gnus-make-sub-thread top)) children)))))
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)
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.
3770 (setq header (car thread))
3771 (setq header thread))
3772 (if (stringp header)
3773 ;; The header is a dummy root.
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)))))
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)))
3785 (gnus-summary-prepare-threads (list (car thread)) 1 t)
3786 (setq thread (cdr thread))))
3788 ;; We do not make a root for the gathered
3789 ;; sub-threads at all.
3791 ;; Print the sub-threads.
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) ? )
3805 (memq number gnus-newsgroup-replied)
3806 (memq number gnus-newsgroup-expirable)
3808 (and gnus-thread-ignore-subject
3809 (not (string= (gnus-simplify-subject-re old-subject)
3810 (gnus-simplify-subject-re subject)))))
3812 (setq old-subject subject)
3813 ;; Recursively print subthreads.
3816 (gnus-summary-prepare-threads
3817 (cdr thread) (1+ level)))))))
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))
3840 ;; Select all active articles.
3841 (setq articles (gnus-uncompress-sequence
3842 (gnus-gethash group gnus-active-hashtb))))
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))
3849 (let ((number (length articles))
3851 (if (> number gnus-large-newsgroup)
3857 "How many articles from %s (default %d): "
3858 gnus-newsgroup-name number))))
3860 (if (string-equal input "")
3861 number (string-to-int input))))
3864 (if (< (abs selected) number)
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
3874 gnus-newsgroup-unreads))
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))
3884 (setq gnus-newsgroup-unselected
3886 gnus-newsgroup-unselected
3887 gnus-newsgroup-unreads)))
3890 ;; Select no articles.
3891 (setq gnus-newsgroup-unselected articles)
3892 (setq articles 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)
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
3907 (header-number headers))
3908 gnus-newsgroup-headers)))
3909 ;; Ticked articles must be a subset of unread articles.
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))
3929 (setq gnus-newsgroup-end
3930 (if gnus-newsgroup-headers
3931 (header-number (gnus-last-element gnus-newsgroup-headers))
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.
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))
3956 ;; There are four types of marked articles - ticked, replied,
3957 ;; expirable and interesting.
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.
3964 (if (or (memq (car m) gnus-newsgroup-unreads)
3965 (memq (car m) gnus-newsgroup-unselected))
3967 (setcdr prev (cdr m)))
3969 ((eq 'bookmark (car prev))
3970 ;; Bookmarks should be a subset of active articles.
3972 (if (< (car (car m)) (car active))
3973 (setcdr prev (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
3987 (if (< (car m) (car active))
3988 (setcdr prev (cdr m))
3991 (setq marked-lists (cdr marked-lists)))
3992 ;; Remove all lists that are empty.
3993 (setq marked-lists (nth 3 info))
3996 (while (= 1 (length (car marked-lists)))
3997 (setq marked-lists (cdr marked-lists)))
3998 (setq m (cdr (setq prev marked-lists)))
4000 (if (= 1 (length (car m)))
4001 (setcdr prev (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)))
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."
4017 (setq newmarked (cons (cons 'tick ticked) nil)))
4019 (setq newmarked (cons (cons 'reply replied) newmarked)))
4021 (setq newmarked (cons (cons 'expire expirable) newmarked)))
4023 (setq newmarked (cons (cons 'killed killed) newmarked)))
4025 (setq newmarked (cons (cons 'interesting interesting) newmarked)))
4027 (setq newmarked (cons (cons 'bookmark bookmark) 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)))
4035 (setcdr (nthcdr 2 info) (cons newmarked nil))))))
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)
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))))
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)
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))))
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)
4077 (setq header (car headers))
4078 (if (and (setq xrefs (header-xref header))
4079 (not (memq (header-number header) unreads)))
4082 (while (string-match "\\([^ :]+\\):\\([0-9]+\\)" xrefs start)
4083 (setq start (match-end 0))
4084 (setq group (concat prefix (substring xrefs (match-beginning 1)
4087 (string-to-int (substring xrefs (match-beginning 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)))
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))
4103 (if (string= from-newsgroup (setq name (symbol-name group)))
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)
4115 gnus-current-select-method)))
4118 ;; Set the new list of read articles in this group.
4119 (setcar (nthcdr 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))
4128 (if (atom (car range))
4130 (setq num (- (cdr active) (- (1+ (cdr range))
4132 (if (< num 0) (setq num 0)))
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.
4140 ;; Update the Newsgroup buffer.
4141 (gnus-group-update-group name t)))))))
4144 (defsubst gnus-header-value ()
4145 (buffer-substring (match-end 0) (save-excursion (end-of-line) (point))))
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)
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)
4158 (while (re-search-forward "^[23][0-9]+ \\([0-9]+\\)" nil t)
4164 header (make-vector 9 nil)
4166 (goto-char (match-beginning 1))
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\\): "
4173 (setq char (downcase (following-char)))
4176 (header-set-subject header
4177 (setq subject (gnus-header-value))))
4179 (header-set-from header (setq from (gnus-header-value))))
4181 (header-set-xref header (gnus-header-value)))
4183 (header-set-lines header
4184 (string-to-int (gnus-header-value))))
4186 (header-set-date header (gnus-header-value)))
4188 (header-set-id header (setq id (gnus-header-value))))
4190 (setq references (gnus-header-value))
4191 (setq end (match-end 0))
4197 (search-backward ">" end t)
4200 (search-backward "<" end t)
4203 (setq in-reply-to (gnus-header-value))))
4206 (header-set-references header references)
4208 (string-match "<[^>]+>" in-reply-to)
4209 (header-set-references
4211 (substring in-reply-to (match-beginning 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))
4225 (search-forward "\n.\n" nil t))
4226 (setq gnus-newsgroup-dependencies dependencies)
4227 (nreverse headers))))
4229 ;; The following macros and functions were written by Felix Lee
4230 ;; <flee@cse.psu.edu>.
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))
4242 (defmacro gnus-nov-skip-field ()
4243 '(search-forward "\t" eol 'end))
4245 (defmacro gnus-nov-field ()
4248 (progn (gnus-nov-skip-field) (1- (point)))))
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))
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)))
4268 (eq number (car sequence))
4270 (setq sequence (cdr sequence))
4275 ;; overview: [num subject from date id refs chars lines misc]
4279 (gnus-nov-field) ; subject
4280 (gnus-nov-field) ; from
4281 (gnus-nov-field) ; date
4282 (setq id (gnus-nov-field)) ; id
4285 (let ((beg (point)))
4286 (search-forward "\t" eol)
4287 (if (search-backward ">" beg t)
4288 (setq ref (buffer-substring
4291 (search-backward "<" beg t)
4294 (gnus-nov-field)) ; refs
4297 (if (/= (following-char) ?\t)
4300 (gnus-nov-field)) ; misc
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))))
4312 (setq headers (nreverse headers))
4313 (setq gnus-newsgroup-dependencies dependencies)
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)
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))
4329 (goto-char (1+ (match-end 0)))
4330 (setq xref (buffer-substring (point)
4331 (progn (end-of-line) (point))))
4333 (set-buffer gnus-summary-buffer)
4334 (header-set-xref gnus-current-headers xref))))))))
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)
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))
4346 (defun gnus-make-headers-hashtable-by-number ()
4347 "Make hashtable for the variable gnus-newsgroup-headers by number."
4349 (headers gnus-newsgroup-headers))
4350 (setq gnus-newsgroup-headers-hashtb-by-number
4351 (gnus-make-hashtable (length 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))
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)
4364 (while (and (not header)
4366 (setq artnum (1- artnum))
4367 (setq header (gnus-read-header artnum)))
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)
4375 (while (and (not header)
4377 (setq artnum (1+ artnum))
4378 (setq header (gnus-read-header artnum)))
4381 (defun gnus-extend-newsgroup (header &optional backward)
4382 "Extend newsgroup selection with HEADER.
4383 Optional argument BACKWARD means extend toward backward."
4385 (let ((artnum (header-number header)))
4386 (setq gnus-newsgroup-headers
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)))))
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."
4400 (set-buffer gnus-group-buffer)
4402 ;; We don't want to alter current point of Group mode buffer.
4403 (if (gnus-group-search-forward
4405 (if use-level (gnus-group-group-level) nil))
4406 (gnus-group-group-name))
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
4417 (function re-search-backward) (function re-search-forward)))
4418 ;; We have to take care of hidden lines.
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 "^[- ]" "^."))))
4430 (if (funcall func regexp nil t)
4432 (goto-char (match-beginning 0))
4433 (gnus-summary-article-number))
4435 ;; Adjust cursor point.
4436 (gnus-summary-position-cursor))))
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
4443 If BACKWARD is non-nil, the search will be performed backwards instead."
4444 (gnus-summary-search-subject backward unread subject))
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))
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
4459 (if (re-search-forward " [-0-9]+ [0-9]+[\n\r]" nil t)
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)))
4467 (defun gnus-summary-thread-level ()
4468 "The thread level of the article on the current line."
4471 (if (re-search-forward " [0-9]+[\n\r]" nil t)
4473 (goto-char (match-beginning 0))
4474 (read (current-buffer)))
4475 ;; We return zero if we couldn't find anything.
4478 (defun gnus-summary-article-mark ()
4479 "The mark on the current line."
4482 (if (re-search-forward ". [-0-9]+ [0-9]+[\n\r]" nil t)
4483 (char-after (match-beginning 0)))))
4485 (defun gnus-summary-subject-string ()
4486 "Return current subject string or nil if nothing."
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)
4495 (buffer-substring beg end)
4496 (set-text-properties beg end '(invisible t))))
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))))
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))
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))))
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))
4536 unread first nlast unread)
4537 ;; If none are read, then all are unread.
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.
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.
4562 ;; Gnus Summary mode commands.
4564 ;; Various summary commands
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."
4571 (not gnus-interactive-catchup) ;Without confirmation?
4575 "Do you really want to mark everything as read? "
4576 "Delete all articles not marked as unread? ")))
4578 (gnus-set-difference gnus-newsgroup-unreads
4579 (if (not all) gnus-newsgroup-marked))))
4580 (message "") ;Erase "Yes or No" question.
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))
4589 (gnus-summary-exit)))
4592 (defun gnus-summary-catchup-all-and-exit (&optional quietly)
4593 "Mark all articles in this newsgroup as read, and then exit."
4595 (gnus-summary-catchup-and-exit t quietly))
4597 (defun gnus-summary-toggle-truncation (arg)
4598 "Toggle truncation of summary lines.
4599 With arg, turn line truncation on iff arg is positive."
4601 (setq truncate-lines
4602 (if (null arg) (not truncate-lines)
4603 (> (prefix-numeric-value arg) 0)))
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."
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
4615 (gnus-summary-jump-to-group gnus-newsgroup-name)
4616 (gnus-group-read-group show-all t)
4617 (gnus-summary-goto-subject current-subject)
4620 (defun gnus-summary-rescan-group (all)
4621 "Exit the newsgroup, ask for new articles, and select the newsgroup."
4623 (gnus-summary-exit t)
4624 (gnus-summary-jump-to-group gnus-newsgroup-name)
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))
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."
4635 (let ((group gnus-newsgroup-name)
4637 (buf (current-buffer)))
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)
4665 ;; If exiting temporary, caller should adjust Group mode
4666 ;; buffer point by itself.
4668 ;; Return to Group mode buffer.
4669 (if (and (get-buffer buf)
4670 (eq mode 'gnus-summary-mode))
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))))
4678 (defun gnus-summary-quit ()
4679 "Quit reading current newsgroup without updating read article info."
4681 (if (y-or-n-p "Do you really wanna quit reading this group? ")
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))))
4694 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
4695 (defun gnus-summary-describe-group ()
4696 "Describe the current newsgroup."
4698 (gnus-group-describe-group gnus-newsgroup-name))
4700 (defun gnus-summary-describe-briefly ()
4701 "Describe Summary mode commands briefly."
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")))
4706 ;; Walking around Group mode buffer from Summary mode.
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."
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))
4718 (message "Exiting %s..." gnus-newsgroup-name)
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))))))
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."
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)))
4748 (message "Exiting %s..." gnus-newsgroup-name)
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))
4772 ;; Walking around summary lines.
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
4781 (let ((backward (< n 0))
4784 (gnus-summary-search-forward unread nil backward))
4786 (gnus-summary-recenter)
4787 (if (/= 0 n) (message "No more%s articles" (if unread " unread" "")))
4788 ; (gnus-summary-position-cursor)
4791 (defun gnus-summary-next-unread-subject (n)
4792 "Go to next N'th unread summary line."
4794 (gnus-summary-next-subject n t))
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."
4800 (gnus-summary-next-subject (- n) unread))
4802 (defun gnus-summary-prev-unread-subject (n)
4803 "Go to previous N'th unread summary line."
4805 (gnus-summary-next-subject (- n) t))
4807 (defun gnus-summary-goto-subject (article)
4808 "Go the subject line of ARTICLE."
4812 (completing-read "Article number: "
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)))
4822 (if (re-search-forward (format "[^Z] %d [0-9]+[\n\r]" article)
4824 (goto-char (match-beginning 0))
4828 (gnus-summary-position-cursor)
4831 ;; Walking around summary lines with displaying articles.
4833 (defun gnus-summary-expand-window ()
4834 "Expand Summary window to show headers full window."
4836 (gnus-configure-windows 'summary)
4837 (pop-to-buffer gnus-summary-buffer))
4839 (defun gnus-summary-display-article (article &optional all-header)
4840 "Display ARTICLE in Article buffer."
4841 (setq gnus-summary-buffer (current-buffer))
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)
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.
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))
4869 ;; The requested article is different from the current article.
4870 (gnus-summary-display-article article all-headers)
4872 (gnus-article-show-all-headers))
4873 (gnus-configure-windows 'article)
4874 (pop-to-buffer gnus-summary-buffer))))
4876 (defun gnus-summary-set-current-mark (&optional current-mark)
4877 "Obsolete function."
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."
4885 (cond ((gnus-summary-display-article
4886 (gnus-summary-search-forward unread subject)))
4888 gnus-auto-select-same
4889 (gnus-set-difference gnus-newsgroup-unreads
4890 (append gnus-newsgroup-marked
4891 gnus-newsgroup-interesting))
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
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
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"))
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))
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))
4933 (and gnus-auto-select-next
4934 ;;(null (gnus-set-difference gnus-newsgroup-unreads
4935 ;; gnus-newsgroup-marked))
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
4945 ;; Ignore characters typed ahead.
4946 (not (input-pending-p))
4948 ;; Keep just the event type of 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)))
4956 (format " (Type %s for %s [%s])"
4957 (single-key-description cmd)
4960 group gnus-newsrc-hashtb)))
4961 (format " (Type %s to exit %s)"
4962 (single-key-description cmd)
4963 gnus-newsgroup-name))
4965 ;; Select next unread newsgroup automagically.
4966 (cond ((and auto-select
4967 (eq gnus-auto-select-next 'quietly))
4969 (gnus-summary-next-group nil group))
4971 ;; Confirm auto selection.
4972 (let* ((event (read-event))
4977 (if (and (eq event type) (eq event cmd))
4978 (gnus-summary-next-group nil group)
4979 (setq unread-command-events (list event)))))
4984 (defun gnus-summary-next-unread-article ()
4985 "Select unread article after current one."
4987 (gnus-summary-next-article t (and gnus-auto-select-same
4988 (gnus-summary-subject-string)))
4989 (gnus-summary-position-cursor))
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."
4996 (cond ((gnus-summary-display-article
4997 (gnus-summary-search-backward unread subject)))
4999 gnus-auto-select-same
5000 (gnus-set-difference gnus-newsgroup-unreads
5001 (append gnus-newsgroup-marked
5002 gnus-newsgroup-interesting))
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
5010 ;; Ignore given SUBJECT, and try again.
5011 (gnus-summary-prev-article unread nil))
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))
5026 (message "No more articles"))
5029 (defun gnus-summary-prev-unread-article ()
5030 "Select unred article before current one."
5032 (gnus-summary-prev-article t (and gnus-auto-select-same
5033 (gnus-summary-subject-string))))
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."
5040 (setq gnus-summary-buffer (current-buffer))
5041 (let ((article (gnus-summary-article-number))
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))))
5059 (defun gnus-summary-prev-page (lines)
5060 "Show previous page of selected article.
5061 Argument LINES specifies lines to be scrolled down."
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))))
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)."
5080 (gnus-summary-select-article)
5081 (gnus-eval-in-buffer-window gnus-article-buffer
5083 (if (gnus-article-next-page lines)
5084 (message "End of message")))
5086 (gnus-article-prev-page (- 0 lines))))
5089 (defun gnus-summary-next-same-subject ()
5090 "Select next article which has the same subject as current one."
5092 (gnus-summary-next-article nil (gnus-summary-subject-string)))
5094 (defun gnus-summary-prev-same-subject ()
5095 "Select previous article which has the same subject as current one."
5097 (gnus-summary-prev-article nil (gnus-summary-subject-string)))
5099 (defun gnus-summary-next-unread-same-subject ()
5100 "Select next unread article which has the same subject as current one."
5102 (gnus-summary-next-article t (gnus-summary-subject-string)))
5104 (defun gnus-summary-prev-unread-same-subject ()
5105 "Select previous unread article which has the same subject as current one."
5107 (gnus-summary-prev-article t (gnus-summary-subject-string)))
5109 (defun gnus-summary-first-unread-article ()
5110 "Select the first unread article.
5111 Return nil if there are no unread articles."
5113 (let ((begin (point)))
5115 (if (re-search-forward " [-0-9]+ [0-9]+[\n\r]" nil t)
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.
5123 (message "No more unread articles")
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."
5132 (completing-read "Article number: "
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)))
5142 (defun gnus-summary-goto-last-article ()
5143 "Go to last subject line."
5145 (if gnus-last-article
5146 (gnus-summary-goto-article gnus-last-article)))
5149 ;; Summary article oriented commands
5151 (defun gnus-summary-refer-parent-article ()
5152 "Refer parent article of current article."
5154 (let ((ref (header-references gnus-current-headers))
5156 (if (or (not ref) (equal ref ""))
5157 (error "No references in this article"))
5158 (and (string-match "<[^<>]*>[ \t]*$" ref)
5160 (substring ref (match-beginning 0) (match-end 0))))
5161 (if (stringp parent)
5162 (gnus-summary-refer-article parent)
5163 (error "Possibly malformed references"))))
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
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
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)))
5187 (gnus-summary-insert-line
5188 nil gnus-current-headers 0 nil ?D nil nil t)
5190 (gnus-summary-position-cursor)
5192 (error "No such references")))
5194 (defun gnus-summary-next-digest (nth)
5195 "Move to head of NTH next digested message."
5197 (gnus-summary-select-article)
5198 (gnus-eval-in-buffer-window gnus-article-buffer
5199 (gnus-article-next-digest (or nth 1))
5202 (defun gnus-summary-prev-digest (nth)
5203 "Move to head of NTH previous digested message."
5205 (gnus-summary-select-article)
5206 (gnus-eval-in-buffer-window gnus-article-buffer
5207 (gnus-article-prev-digest (or nth 1))
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
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."
5218 (gnus-summary-select-article)
5220 (let ((artbuf gnus-article-buffer)
5221 (digbuf (get-buffer-create gnus-digest-buffer))
5222 (mail-header-separator ""))
5224 (gnus-add-current-to-buffer-list)
5225 (buffer-disable-undo (current-buffer))
5226 (setq buffer-read-only nil)
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)
5235 (rmail-set-message-counters)
5236 (rmail-show-message)
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
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
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
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)
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")))
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")))
5296 (defun gnus-summary-isearch-article ()
5297 "Do incremental search forward on current article."
5299 (gnus-summary-select-article)
5300 (gnus-eval-in-buffer-window gnus-article-buffer
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."
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
5319 (error "Search failed: \"%s\"" regexp)
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."
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
5338 (error "Search failed: \"%s\"" regexp)
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.
5349 (function re-search-backward) (function re-search-forward)))
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
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
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
5373 (goto-char (if backward (point-max) (point-min)))
5374 (setq found (funcall re-search regexp nil t)))
5377 ;; Adjust article pointer.
5378 (or (eq last gnus-current-article)
5379 (setq gnus-last-article last))
5380 ;; Return T if found such article.
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."
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.
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
5407 (call-interactively '(, (key-binding command)))))
5409 (message "Executing %s... done" (key-description command)))))
5411 (defun gnus-summary-beginning-of-article ()
5412 "Scroll the article back to the beginning."
5414 (gnus-summary-select-article)
5415 (gnus-eval-in-buffer-window gnus-article-buffer
5417 (goto-char (point-min))
5418 (if gnus-break-pages
5419 (gnus-narrow-to-page))
5422 (defun gnus-summary-end-of-article ()
5423 "Scroll to the end of the article."
5425 (gnus-summary-select-article)
5426 (gnus-eval-in-buffer-window gnus-article-buffer
5428 (goto-char (point-max))
5429 (if gnus-break-pages
5430 (gnus-narrow-to-page))
5433 (defun gnus-summary-show-article ()
5434 "Force re-fetching of the current article."
5436 (gnus-summary-select-article gnus-have-all-headers t))
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."
5444 (set-buffer gnus-article-buffer)
5445 (let ((buffer-read-only nil))
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))))))
5453 (defun gnus-summary-show-all-headers ()
5454 "Make all header lines visible."
5456 (gnus-article-show-all-headers))
5458 (defun gnus-summary-toggle-mime (arg)
5459 "Toggle MIME processing.
5460 If ARG is a positive number, turn MIME processing on."
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))
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."
5472 (gnus-summary-select-article)
5473 (gnus-overload-functions)
5474 (gnus-eval-in-buffer-window gnus-article-buffer
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)
5487 (defun gnus-summary-stop-page-breaking ()
5488 "Stop page breaking in the current article."
5490 (gnus-summary-select-article)
5491 (gnus-eval-in-buffer-window gnus-article-buffer (widen)))
5493 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
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.)"
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))
5516 (setq articles (cons (gnus-summary-article-number)
5518 (gnus-summary-search-forward nil nil backward))
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))
5527 (format "Where do you want to move %s? "
5528 (if (> (length articles) 1)
5529 (format "these %d articles" (length articles))
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)
5538 (gnus-request-move-article
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)
5545 (gnus-gethash (car art-group) gnus-newsrc-hashtb)
5546 (gnus-gethash (concat gnus-foreign-group-prefix
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!
5562 (message "Couldn't move article %s" (car articles)))
5563 (setq articles (cdr articles)))))
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.)"
5579 (setq respool-method
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))
5586 ;; Summary marking commands.
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."
5594 (setq unmark (prefix-numeric-value unmark)))
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"))
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."
5612 (setq unmark (prefix-numeric-value unmark)))
5614 (gnus-summary-mark-same-subject
5615 (gnus-summary-subject-string) unmark)))
5616 ;; If marked as read, go to next unread subject.
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"))
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."
5630 (cond ((null unmark)
5631 (gnus-summary-mark-as-read nil "K"))
5633 (gnus-summary-tick-article nil t))
5635 (gnus-summary-tick-article)))
5637 (gnus-summary-search-forward nil subject))
5638 (cond ((null unmark)
5639 (gnus-summary-mark-as-read nil "K"))
5641 (gnus-summary-tick-article nil t))
5643 (gnus-summary-tick-article)))
5644 (setq count (1+ count))
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.
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."
5660 (let ((backward (< n 0))
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))))
5670 (if (/= 0 n) (message "No more articles"))
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."
5678 (gnus-summary-mark-as-processable n t))
5680 (defun gnus-summary-unmark-all-processable ()
5681 "Remove the process mark from all articles."
5684 (while gnus-newsgroup-processable
5685 (gnus-summary-remove-process-mark (car gnus-newsgroup-processable))))
5686 (gnus-summary-position-cursor))
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."
5694 (let ((backward (< n 0))
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))))
5704 (if (/= 0 n) (message "No more articles"))
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."
5712 (gnus-summary-mark-as-expirable n t))
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)
5720 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
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)
5733 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5738 (if (memq article gnus-newsgroup-processable) ?# ? ))
5741 (defun gnus-summary-expire-articles ()
5742 "Expire all articles that are marked as expirable in the current group."
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))))
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)
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
5781 (set-buffer gnus-article-buffer)
5786 (search-forward "\n\n" nil t)
5789 gnus-newsgroup-bookmarks))
5790 (message "A bookmark has been added to the current article."))
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)))
5799 (setq gnus-newsgroup-bookmarks
5800 (delq old gnus-newsgroup-bookmarks))
5801 (message "Removed bookmark."))
5802 (message "No bookmark in current article."))))
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."
5810 (gnus-summary-mark-forward n "I"))
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)
5818 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
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)
5831 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5836 (if (memq article gnus-newsgroup-expirable) ?X ? ))
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
5847 (let ((backward (< n 0))
5850 (gnus-summary-mark-article nil unread)
5851 (= 0 (gnus-summary-next-subject (if backward -1 1)
5854 (if (/= 0 n) (message "No more %sarticles" (if unread "" "unread ")))
5855 (gnus-set-mode-line 'summary)
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
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)))
5871 (if (gnus-summary-goto-subject article)
5873 (gnus-summary-show-thread)
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))
5881 (set-text-properties (1- (point)) (point) '(invisible t))
5882 ;; Fix the visible mark.
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)))))
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)))
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))))
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."
5936 (gnus-summary-mark-forward n "-"))
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."
5944 (gnus-summary-mark-forward (- n) "-"))
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 " " "-")))
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
5960 (gnus-summary-mark-forward n))
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
5967 (gnus-summary-mark-forward (- n)))
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))
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."
5981 (gnus-summary-mark-forward n " "))
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."
5987 (gnus-summary-mark-forward (- n) " "))
5989 (defun gnus-summary-delete-marked-as-read ()
5990 "Delete lines that are marked as read."
5992 (if gnus-newsgroup-unreads
5993 (let ((buffer-read-only nil))
5995 (goto-char (point-min))
5996 ;; Fix by Jim Sisolak <sisolak@trans4.neep.wisc.edu>.
5997 (delete-matching-lines "^[DK]"))
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")))
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))
6010 (goto-char (point-min))
6011 (delete-matching-lines (concat "^[" marks "]")))
6013 (or (zerop (buffer-size))
6015 (gnus-summary-prev-subject 1)
6016 (gnus-summary-position-cursor)))))
6018 (defun gnus-summary-show-all-interesting ()
6019 "Display all the hidden articles that are marked as interesting."
6021 (let ((int gnus-newsgroup-interesting-subjects)
6022 (buffer-read-only nil))
6024 (error "No interesting articles hidden."))
6025 (goto-char (point-min))
6028 (insert (cdr (car int)))
6029 (setq int (cdr int))))
6030 (gnus-summary-position-cursor)
6031 (setq gnus-newsgroup-interesting-subjects nil)))
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."
6038 (not gnus-interactive-catchup) ;Without confirmation?
6042 "Do you really want to mark everything as read? "
6043 "Delete all articles not marked as unread? ")))
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)
6051 (gnus-summary-mark-as-read (car unmarked) "C")
6052 (setq unmarked (cdr unmarked))
6056 (defun gnus-summary-catchup-to-here ()
6057 "Mark all unticked articles before the current one as read."
6060 (let ((current (gnus-summary-article-number)))
6061 (goto-char (point-min))
6062 (while (not (= (gnus-summary-article-number) current))
6064 (if (/= ?- (following-char))
6065 (gnus-summary-mark-as-read))
6066 (gnus-summary-next-subject 1))))
6068 (defun gnus-summary-catchup-all (&optional quietly)
6069 "Mark all articles in this newsgroup as read."
6071 (gnus-summary-catchup t quietly))
6073 ;; Thread-based commands.
6075 (defun gnus-summary-toggle-threads (arg)
6076 "Toggle showing conversation threads.
6077 If ARG is positive number, turn showing conversation threads on."
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)))
6086 (defun gnus-summary-show-all-threads ()
6089 (if gnus-show-threads
6091 (let ((buffer-read-only nil))
6092 (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)))))
6094 (defun gnus-summary-show-thread ()
6095 "Show thread subtrees."
6097 (if gnus-show-threads
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)))))
6104 (defun gnus-summary-hide-all-threads ()
6105 "Hide all thread subtrees."
6107 (if gnus-show-threads
6109 (goto-char (point-min))
6110 (gnus-summary-hide-thread)
6111 (while (gnus-summary-search-forward)
6112 (gnus-summary-hide-thread)))))
6114 (defun gnus-summary-hide-thread ()
6115 "Hide thread subtrees."
6117 (if gnus-show-threads
6119 (let ((buffer-read-only nil)
6121 (level (gnus-summary-thread-level))
6123 ;; Go forward until either the buffer ends or the subthread
6125 (while (and (= 0 (forward-line 1))
6126 (> (gnus-summary-thread-level) level))
6128 (subst-char-in-region start end ?\n ?\^M t)))))
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))
6138 (re-search-backward " 0[\n\r]" nil t)
6141 (and (re-search-backward " 0[\n\r]" nil t)
6142 (re-search-forward (format " %s[\n\r]" level) end t)))
6144 (if (not (and (re-search-forward " 0[\n\r]" nil t)
6146 (re-search-forward " 0[\n\r]" nil t)
6149 (re-search-forward (format " %s[\n\r]" level) nil t)))
6151 (/= (point) start)))
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
6159 (let ((backward (< n 0))
6162 (gnus-summary-go-to-next-thread backward))
6164 (gnus-summary-position-cursor)
6165 (if (/= 0 n) (message "No more threads" ))
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
6173 (gnus-summary-next-thread (- n)))
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))
6181 (level-diff (if up -1 1))
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)))))
6188 (/= start (point))))
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
6199 (gnus-summary-go-down-thread up))
6201 (gnus-summary-position-cursor)
6202 (if (/= 0 n) (message "Can't go further" ))
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
6211 (gnus-summary-down-thread (- n)))
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."
6219 (setq unmark (prefix-numeric-value unmark)))
6221 (level (gnus-summary-thread-level)))
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
6230 (if (not (and (= 0 (forward-line 1))
6231 (> (gnus-summary-thread-level) level)))
6232 (setq killing nil))))
6233 ;; Hide killed subtrees.
6235 gnus-thread-hide-killed
6236 (gnus-summary-hide-thread))
6237 ;; If marked as read, go to next unread subject.
6239 ;; Go to next unread subject.
6240 (gnus-summary-next-subject 1 t)))
6241 (gnus-set-mode-line 'summary))
6243 ;; Summary sorting commands
6245 (defun gnus-summary-sort-by-number (reverse)
6246 "Sort Summary buffer by article number.
6247 Argument REVERSE means reverse order."
6249 (gnus-summary-keysort-summary
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."
6261 (gnus-summary-keysort-summary
6262 (function string-lessp)
6264 (if case-fold-search
6265 (downcase (header-from a))
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."
6275 (gnus-summary-keysort-summary
6276 (function string-lessp)
6278 (if case-fold-search
6279 (downcase (gnus-simplify-subject (header-subject a) 're-only))
6280 (gnus-simplify-subject (header-subject a) 're-only)))
6284 (defun gnus-summary-sort-by-date (reverse)
6285 "Sort Summary buffer by date.
6286 Argument REVERSE means reverse order."
6288 (gnus-summary-keysort-summary
6289 (function string-lessp)
6291 (gnus-sortable-date (header-date a)))
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)
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)
6313 ;; Basic ideas by flee@cs.psu.edu (Felix Lee)
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
6323 (gnus-keysort (nreverse gnus-newsgroup-headers) predicate key))
6324 (gnus-keysort gnus-newsgroup-headers predicate key)))
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)
6332 (funcall predicate (car a) (car b))))))
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
6341 (nreverse (sort (nreverse gnus-newsgroup-headers) predicate))
6342 (sort gnus-newsgroup-headers predicate)))
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)))
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)))
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)))
6370 ;; Summary saving commands.
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."
6380 (let (articles process)
6381 (if (and n (numberp n))
6382 (let ((backward (< n 0))
6386 (setq articles (cons (gnus-summary-article-number)
6388 (gnus-summary-search-forward nil nil backward))
6390 (setq articles (sort articles (function <))))
6391 (if gnus-newsgroup-processable
6393 (setq articles (setq gnus-newsgroup-processable
6394 (nreverse gnus-newsgroup-processable)))
6396 (setq articles (list (gnus-summary-article-number)))))
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."))
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)))
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."
6418 (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
6419 (gnus-summary-save-article arg)))
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."
6428 (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail))
6429 (gnus-summary-save-article arg)))
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."
6438 (funcall gnus-rmail-save-name gnus-newsgroup-name
6439 gnus-current-headers gnus-newsgroup-last-rmail)))
6443 (concat "Save article in rmail file: (default "
6444 (file-name-nondirectory default-name) ") ")
6445 (file-name-directory default-name)
6447 (gnus-make-directory (file-name-directory filename))
6448 (gnus-eval-in-buffer-window
6453 (gnus-output-to-rmail filename))))
6454 ;; Remember the directory name to save articles.
6455 (setq gnus-newsgroup-last-rmail filename)))
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."
6463 (funcall gnus-mail-save-name gnus-newsgroup-name
6464 gnus-current-headers gnus-newsgroup-last-mail)))
6468 (concat "Save article in Unix mail file: (default "
6469 (file-name-nondirectory default-name) ") ")
6470 (file-name-directory default-name)
6473 (expand-file-name filename
6475 (file-name-directory default-name))))
6476 (gnus-make-directory (file-name-directory filename))
6477 (gnus-eval-in-buffer-window
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)))
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."
6494 (funcall gnus-file-save-name gnus-newsgroup-name
6495 gnus-current-headers gnus-newsgroup-last-file)))
6499 (concat "Save article in file: (default "
6500 (file-name-nondirectory default-name) ") ")
6501 (file-name-directory default-name)
6503 (gnus-make-directory (file-name-directory filename))
6504 (gnus-eval-in-buffer-window
6509 (gnus-output-to-file filename))))
6510 ;; Remember the directory name to save articles.
6511 (setq gnus-newsgroup-last-file filename)))
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
6523 (shell-command-on-region (point-min) (point-max) command nil)))
6524 (setq gnus-last-shell-command command)))
6526 ;; Summary killfile commands
6528 (defun gnus-summary-edit-global-kill ()
6529 "Edit a global KILL file."
6531 (setq gnus-current-kill-article (gnus-summary-article-number))
6532 (gnus-kill-file-edit-file nil) ;Nil stands for global KILL file.
6534 (substitute-command-keys
6535 "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
6537 (defun gnus-summary-edit-local-kill ()
6538 "Edit a local KILL file applied to the current newsgroup."
6540 (setq gnus-current-kill-article (gnus-summary-article-number))
6541 (gnus-kill-file-edit-file gnus-newsgroup-name)
6543 (substitute-command-keys
6544 "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
6549 ;;; Gnus Article Mode
6552 (if gnus-article-mode-map
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))
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:
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
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))
6600 (defun gnus-article-setup-buffer ()
6601 "Initialize Article mode buffer."
6602 (or (get-buffer gnus-article-buffer)
6604 (set-buffer (get-buffer-create gnus-article-buffer))
6605 (gnus-add-current-to-buffer-list)
6606 (gnus-article-mode))
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.
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))
6623 (set-buffer gnus-summary-buffer)
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)))
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))
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)))))
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)
6648 (setq gnus-reffed-article-number (1- gnus-reffed-article-number)))
6649 (setq gnus-current-headers header)
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."
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))
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))
6675 (set-buffer gnus-article-buffer)
6676 (let ((buffer-read-only nil))
6679 (if (gnus-request-article-this-buffer article group)
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.
6688 ;; `gnus-current-article' must be an article number.
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))
6716 (goto-char (point-min))
6717 (if gnus-break-pages
6718 (gnus-narrow-to-page))
6719 (gnus-set-mode-line 'article)
6721 ;; There is no such article.
6722 (if (numberp article)
6723 (gnus-summary-mark-as-read article))
6725 (message "No such article (may be canceled)")
6730 (message "Moved to bookmark.")
6731 (search-forward "\n\n" nil t)
6732 (forward-line bookmark)))
6734 (get-buffer-window gnus-article-buffer) (point))))))))
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))
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))))
6751 (defun gnus-article-show-all-headers ()
6752 "Show all article headers in Article mode buffer."
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)))))
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)))
6766 (defun gnus-article-hide-headers (&optional delete)
6767 "Hide unwanted headers and possibly sort them as well."
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.
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
6783 (while (re-search-forward "^[^ \t]*:" nil t)
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))))
6795 ;; Be sure to get multi-line headers...
6796 (re-search-forward "^[^ \t]*:" nil t)
6799 (cons (buffer-substring beg (point)) want-list))
6800 (delete-region beg (point))
6803 ;; Next we perform the sorting by looking at
6804 ;; `gnus-sorted-header-list'.
6806 (while (and sorted want-list)
6807 (setq want-l want-list)
6809 (not (string-match (car sorted) (car want-l))))
6810 (setq want-l (cdr want-l)))
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.
6819 (insert (car want-list))
6820 (setq want-list (cdr want-list)))
6821 ;; And finally we make the unwanted headers invisible.
6823 (delete-region (point) (point-max))
6824 (set-text-properties (point) (point-max) '(invisible t)))))))
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."
6833 (goto-char (point-max))
6834 (if (re-search-backward "^-- *$" nil t)
6836 (add-text-properties (point) (point-max) '(invisible t))))))
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."
6846 (search-forward "\n\n" nil t)
6848 (if (looking-at ">")
6849 (add-text-properties
6850 (point) (save-excursion (forward-line 1) (point))
6856 (defun gnus-output-to-rmail (file-name)
6857 "Append the current article to an Rmail file named FILE-NAME."
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*")))
6865 (or (get-file-buffer file-name)
6866 (file-exists-p file-name)
6868 (concat "\"" file-name "\" does not exist, create it? "))
6869 (let ((file-buffer (create-file-buffer file-name)))
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")))
6878 (buffer-disable-undo (current-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)))
6885 (append-to-file (point-min) (point-max) file-name)
6886 ;; File has been visited, in 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.
6894 (narrow-to-region (point-max) (point-max))))
6895 (insert-buffer-substring tmpbuf)
6898 (goto-char (point-min))
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))))))
6906 (kill-buffer tmpbuf)
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*")))
6916 (buffer-disable-undo (current-buffer))
6918 (insert-buffer-substring artbuf)
6919 ;; Append newline at end of the buffer as separator, and then
6921 (goto-char (point-max))
6923 (append-to-file (point-min) (point-max) file-name))
6924 (kill-buffer tmpbuf)
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))
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."
6944 (setq arg (if arg (prefix-numeric-value arg) 0))
6946 (forward-page -1) ;Beginning of current page.
6951 (forward-page (1- arg))))
6952 ;; Find the end of the 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)
6963 ;; Find the top of the page.
6965 ;; If we found beginning of buffer, stay there.
6966 ;; If extra text follows page delimiter on same line,
6968 ;; Otherwise, show text starting with following line.
6969 (if (and (eolp) (not (bobp)))
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."
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))
6990 (timezone-make-date-arpa-standard date nil gnus-local-timezone))
6995 ;; Article mode commands
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."
7002 (move-to-window-line -1)
7003 ;; Fixed by enami@ptgd.sony.co.jp (enami tsugutomo)
7006 (and (pos-visible-in-window-p) ;Not continuation line.
7008 ;; Nothing in this page.
7009 (if (or (not gnus-break-pages)
7012 (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer?
7014 (gnus-narrow-to-page 1) ;Go to next page.
7017 ;; More in this page.
7021 ;; Long lines may cause an end-of-buffer error.
7022 (goto-char (point-max))))
7026 (defun gnus-article-prev-page (lines)
7027 "Show previous page of current article.
7028 Argument LINES specifies lines to be scrolled down."
7030 (move-to-window-line 0)
7031 (if (and gnus-break-pages
7033 (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
7035 (gnus-narrow-to-page -1) ;Go to previous page.
7036 (goto-char (point-max))
7038 (scroll-down lines)))
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.
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.
7057 (if (re-search-forward gnus-digest-separator nil t)
7059 (search-backward "\n\n") ;This may be incorrect.
7061 (goto-char (point-max)))
7062 (push-mark) ;Set mark at end of digested message.
7065 ;; Show From: and Subject: fields.
7067 (message "End of message")
7070 (defun gnus-article-prev-digest (nth)
7071 "Move to head of NTH previous digested message."
7072 ;; Stop page breaking in digest mode.
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.
7086 (if (re-search-forward gnus-digest-separator nil t)
7088 (search-backward "\n\n") ;This may be incorrect.
7090 (goto-char (point-max)))
7091 (push-mark) ;Set mark at end of digested message.
7093 ;; Show From: and Subject: fields.
7095 (goto-char (point-min))
7096 (message "Top of message")
7099 (defun gnus-article-refer-article ()
7100 "Read article specified by message-id around point."
7102 (save-window-excursion
7104 (re-search-forward ">" nil t) ;Move point to end of "<....>".
7105 (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
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"))
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."
7119 (save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
7120 (save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
7123 (switch-to-buffer gnus-summary-buffer)
7124 (funcall gnus-mail-reply-method yank address)))))
7126 (defun gnus-article-mail-with-original ()
7127 "Send a reply to the address near point and include the original article."
7129 (gnus-article-mail 'yank))
7131 (defun gnus-article-show-summary ()
7132 "Reconfigure windows to show Summary buffer."
7134 (gnus-configure-windows 'article)
7135 (pop-to-buffer gnus-summary-buffer)
7136 (gnus-summary-goto-subject gnus-current-article))
7138 (defun gnus-article-describe-briefly ()
7139 "Describe Article mode commands briefly."
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")))
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.
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))
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
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))
7165 (aset caesar-translate-table i i)
7167 (setq lower (concat lower lower) upper (upcase lower) i 0)
7169 (aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
7170 (aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
7172 ;; ROT47 for Japanese text.
7173 ;; Thanks to ichikawa@flab.fujitsu.junet.
7175 (let ((t1 (logior ?O 128))
7176 (t2 (logior ?! 128))
7177 (t3 (logior ?~ 128)))
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))))
7184 (message "Building caesar-translate-table... done")))
7185 (let ((from (region-beginning))
7188 (setq str (buffer-substring from to))
7189 (setq len (length str))
7191 (aset str i (aref caesar-translate-table (aref str i)))
7194 (delete-region from to)
7199 ;;; Gnus KILL-File Mode
7202 (if gnus-kill-file-mode-map
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))
7212 (defun gnus-kill-file-mode ()
7213 "Major mode for editing KILL file.
7215 In addition to Emacs-Lisp Mode, the following commands are available:
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.
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
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.
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
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:
7253 (gnus-kill \"Subject\" \"AI\")
7255 If you want to mark articles with `D' instead of `X', you can use
7256 the following expression:
7258 (gnus-kill \"Subject\" \"AI\" \"d\")
7260 In this example it is assumed that the command
7261 `gnus-summary-mark-as-read-forward' is assigned to `d' in Summary Mode.
7263 It is possible to delete unnecessary headers which are marked with
7264 `X' in a KILL file as follows:
7266 (gnus-expunge \"X\")
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.
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."
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))
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)))
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))
7311 (find-file-other-window file))
7313 (gnus-kill-file-mode)
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)))))
7322 (defun gnus-kill-save-kill-buffer ()
7323 (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
7324 (if (get-buffer file)
7326 (set-buffer (get-buffer file))
7328 (kill-buffer (current-buffer))))))
7330 (defun gnus-article-fetch-field (field)
7332 (set-buffer gnus-article-buffer)
7336 (narrow-to-region 1 (save-excursion
7337 (search-forward "\n\n" nil t) (point)))
7340 (mail-fetch-field field)
7343 (defun gnus-kill-file-enter-kill (field regexp)
7345 (gnus-kill-set-kill-buffer)
7346 (insert (format "(gnus-kill \"%s\" \"%s\") ; ttl=5\n"
7349 (defun gnus-kill-file-kill-by-subject ()
7350 "Insert KILL command for current subject."
7352 (gnus-kill-file-enter-kill
7353 "Subject" (regexp-quote (header-subject gnus-current-headers))))
7355 (defun gnus-kill-file-kill-by-author ()
7356 "Insert KILL command for current author."
7358 (gnus-kill-file-enter-kill
7359 "From" (regexp-quote (header-from gnus-current-headers))))
7361 (defun gnus-kill-file-kill-by-thread ()
7362 "Insert KILL command for current thread."
7364 (gnus-kill-file-enter-kill
7365 "References" (concat ".*" (regexp-quote
7366 (header-id gnus-current-headers)))))
7368 (defun gnus-kill-file-kill-by-xref ()
7369 "Insert KILL command for current xref."
7371 (let ((xref (header-xref gnus-current-headers))
7377 (while (string-match " \\([a-zA-Z\.]\\):" xref start)
7378 (if (not (string= (setq group (substring (match-beginning 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
7386 (defun gnus-kill-file-apply-buffer ()
7387 "Apply current buffer to current newsgroup."
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)" )))
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.")))
7399 (defun gnus-kill-file-apply-last-sexp ()
7400 "Apply sexp before point in current buffer to current newsgroup."
7402 (if (and gnus-current-kill-article
7403 (get-buffer gnus-summary-buffer))
7404 ;; Assume newsgroup is selected.
7407 (save-excursion (forward-sexp -1) (point)) (point))))
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.")))
7414 (defun gnus-kill-file-exit ()
7415 "Save a KILL file, then return to the previous 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)))
7431 ;; Basic ideas by emv@math.lsa.umich.edu (Edward Vielmetti)
7433 (defun gnus-batch-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"))
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)
7455 ;; Apply kills to specified newsgroups in command line arguments.
7456 (setq newsrc (copy-sequence gnus-newsrc-assoc))
7458 (setq group (car (car newsrc)))
7459 (setq subscribed (nth 1 (car newsrc)))
7460 (setq newsrc (cdr newsrc))
7462 (not (zerop (car (gnus-gethash group gnus-newsrc-hashtb))))
7464 (string-match yes group) t)
7466 (not (string-match no group))))
7468 (gnus-summary-read-group group nil t)
7469 (if (eq (current-buffer) (get-buffer gnus-summary-buffer))
7470 (gnus-summary-exit t))
7473 ;; Finally, exit Emacs.
7474 (set-buffer gnus-group-buffer)
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))
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")))
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")))
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")))
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")))
7527 (defalias 'gnus-expunge 'gnus-summary-delete-marked-with)
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.
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.
7544 (setq command '(gnus-summary-mark-as-read nil "X")))
7545 (gnus-execute field regexp command nil (not all))
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)
7557 (if (string-equal 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)
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)))))
7583 (defun gnus-execute-1 (function regexp form)
7585 ;; The point of Summary buffer must be saved during execution.
7586 (let ((article (gnus-summary-article-number)))
7590 ;; Compare with header field.
7591 (let ((header (gnus-get-header-by-number article))
7595 (setq value (funcall function header))
7596 ;; Number (Lines:) or symbol must be converted to string.
7598 (setq value (prin1-to-string value)))
7599 (string-match regexp value))
7600 (if (stringp form) ;Keyboard macro.
7601 (execute-kbd-macro 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)
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)
7624 ;;; Gnus Posting Functions
7627 (defvar gnus-organization-file "/usr/lib/news/organization"
7628 "*Local news organization file.")
7630 (defvar gnus-post-news-buffer "*post-news*")
7631 (defvar gnus-winconf-post-news nil)
7633 (autoload 'news-reply-mode "rnewspost")
7635 ;;; Post news commands of Gnus Group Mode and Summary Mode
7637 (defun gnus-group-post-news ()
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)))
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)))
7656 (defun gnus-summary-post-news ()
7659 ;; Save window configuration.
7660 (setq gnus-winconf-post-news (current-window-configuration))
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)))
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."
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))
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))
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))))
7698 (defun gnus-summary-followup-with-original ()
7699 "Compose a followup to an article and include the original article."
7701 (gnus-summary-followup t))
7703 (defun gnus-summary-cancel-article ()
7704 "Cancel an article you posted."
7706 (gnus-summary-select-article t)
7707 (gnus-eval-in-buffer-window gnus-article-buffer
7708 (gnus-cancel-news)))
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."
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)
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)
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")
7740 (insert mail-header-separator))))
7743 ;;; Post a News using NNTP
7746 (fset 'sendnews 'gnus-post-news)
7749 (fset 'postnews 'gnus-post-news)
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."
7755 (if (or (not gnus-novice-user)
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)
7764 (set-buffer gnus-summary-buffer)
7765 (cons (current-buffer) gnus-current-article))))
7767 (if (and gnus-interactive-post
7768 (not gnus-expert-user)
7772 (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
7773 (setq mail-reply-buffer article-buffer)
7774 (setq gnus-post-news-buffer
7776 (gnus-request-post-buffer method header article-buffer)))
7777 (if (eq method 'post)
7779 (delete-other-windows)
7780 (switch-to-buffer post-buf))
7781 (delete-other-windows)
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
7793 (mail-position-on-field "FCC")
7794 (insert gnus-author-copy)))
7795 (goto-char (point-min))
7796 (if (and (eq method 'post) (not header))
7798 (search-forward (concat "\n" mail-header-separator "\n"))
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))))))
7809 (defun gnus-inews-news ()
7810 "Send a news message."
7812 (let* ((case-fold-search nil)
7813 (server-running (gnus-server-opened gnus-select-method))
7814 (reply gnus-article-reply))
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.
7821 (goto-char (point-min))
7822 (run-hooks 'news-inews-hook)
7827 (goto-char (point-min))
7828 (search-forward (concat "\n" mail-header-separator "\n"))
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)
7838 (if (re-search-forward "^[^ \t]" nil 'end)
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]+" ",")
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
7852 (message "Sending via mail...")
7854 (funcall gnus-mail-send-method)
7855 (message "Sending via mail... done"))
7857 (message "No mailer defined. To: and/or Cc: fields ignored.")
7860 ;; Send to NNTP server.
7861 (message "Posting to USENET...")
7862 (if (gnus-inews-article)
7864 (message "Posting to USENET... done")
7866 (get-buffer (car reply))
7867 (buffer-name (car reply)))
7870 (set-buffer gnus-summary-buffer)
7871 (gnus-summary-mark-article-as-replied
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.
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)
7887 (defun gnus-cancel-news ()
7888 "Cancel an article you posted."
7890 (if (yes-or-no-p "Do you really want to cancel this article? ")
7896 ;; Get header info. from original article.
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.
7910 (downcase (mail-strip-quoted-names from))
7911 (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
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))
7918 (insert "Newsgroups: " newsgroups "\n"
7919 "Subject: cancel " message-id "\n"
7920 "Control: cancel " message-id "\n"
7921 mail-header-separator "\n"
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))
7934 ;;; Lowlevel inews interface
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*")))
7941 (goto-char (point-max))
7942 ;; require a newline at the end for inews to append .signature to
7943 (or (= (preceding-char) ?\n)
7945 ;; Prepare article headers. All message body such as signature
7946 ;; must be inserted before Lines: field is prepared.
7948 (goto-char (point-min))
7952 (search-forward (concat "\n" mail-header-separator "\n"))
7955 (gnus-inews-insert-headers)
7959 (buffer-disable-undo (current-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.
7975 (gnus-request-post gnus-current-select-method)
7976 (kill-buffer (current-buffer)))
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."
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")))
8003 (memq 'Message-ID gnus-required-headers)
8005 (if (mail-fetch-field "message-id")
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")))
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
8018 (memq 'Organization gnus-required-headers)
8019 (or (mail-fetch-field "organization")
8020 (let ((begin (point-max))
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")))
8037 (defun gnus-insert-end (&rest args)
8039 (goto-char (point-max))
8040 (apply 'insert args)))
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."
8050 (if gnus-signature-file
8051 (expand-file-name gnus-signature-file nil)))
8053 (goto-char (point-min))
8054 (search-forward "\n\n")
8055 (narrow-to-region (point-min) (point))
8056 (setq distribution (mail-fetch-field "distribution"))
8060 ;; Insert signature.
8061 (if (file-exists-p signature)
8063 (goto-char (point-max))
8065 (insert-file-contents signature)))
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)
8078 (case-fold-search t)) ;Should ignore case.
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)
8087 (cons (buffer-substring
8091 (skip-chars-backward " \t")
8094 (delete-region (match-beginning 0)
8095 (progn (forward-line 1) (point))))
8096 ;; Process FCC operations.
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)
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)))
8123 (defun gnus-inews-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))
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))
8143 ;; User's full name.
8144 (cond ((string-equal full-name "") "")
8145 ((string-equal full-name "&") ;Unix hack.
8146 (concat " (" (user-login-name) ")"))
8148 (concat " (" full-name ")")))
8151 (defun gnus-inews-login-name ()
8152 "Return user login name.
8153 Got from the variable `gnus-user-login-name' and the function
8155 (or gnus-user-login-name (user-login-name)))
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)))
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")
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)))
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)
8190 (substring user-mail-address (1+ (string-match "@" user-mail-address)))))
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) ">"))
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]\\)"
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
8212 (error "Cannot understand current-time-string: %s." date))
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)))
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)))
8228 (gnus-inews-valid-date now zone)
8229 ;; No timezone info.
8230 (gnus-inews-buggy-date now))))
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))
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]\\)"
8252 (concat (substring date (match-beginning 2) (match-end 2)) ;Day
8254 (substring date (match-beginning 1) (match-end 1)) ;Month
8256 (substring date (match-beginning 4) (match-end 4)) ;Year
8258 (substring date (match-beginning 3) (match-end 3))) ;Time
8259 (error "Cannot understand current-time-string: %s." date))
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.
8272 (let* ((private-file (expand-file-name "~/.organization" nil))
8273 (organization (or (getenv "ORGANIZATION")
8274 gnus-local-organization
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")))
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)
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.
8296 (let ((tmpbuf (get-buffer-create " *Gnus organization*")))
8299 (insert-file-contents organization)
8300 (prog1 (buffer-string)
8301 (kill-buffer tmpbuf))
8303 ((string-equal organization private-file) nil) ;No such file
8307 (defun gnus-inews-lines ()
8308 "Count the number of lines and return numeric string."
8312 (goto-char (point-min))
8313 (search-forward "\n\n" nil 'move)
8314 (int-to-string (count-lines (point) (point-max))))))
8318 ;;; Gnus Mail Functions
8321 (autoload 'news-mail-reply "rnewspost")
8322 (autoload 'news-mail-other-window "rnewspost")
8324 ;;; Mail reply commands of Gnus Summary Mode
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."
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)))
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."
8343 (gnus-summary-reply t))
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."
8349 (gnus-summary-select-article)
8350 (switch-to-buffer gnus-article-buffer)
8352 (delete-other-windows)
8353 (bury-buffer gnus-article-buffer)
8354 (funcall gnus-mail-forward-method))
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."
8360 (gnus-summary-select-article)
8361 (switch-to-buffer gnus-article-buffer)
8363 (delete-other-windows)
8364 (bury-buffer gnus-article-buffer)
8365 (funcall gnus-mail-other-window-method))
8367 (defun gnus-mail-reply-using-mail (&optional yank to-address)
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*"))
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)
8383 (not (y-or-n-p "Unsent article being composed; erase it? ")))
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)
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"))
8402 (string-match " *at \\| *@ \\| *(\\| *<" from)))
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")
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"))
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: ")))
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"))
8433 (let ((last (point)))
8434 (run-hooks 'news-reply-header-hook)
8435 (mail-yank-original nil)
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))))))
8445 (defun gnus-mail-yank-original ()
8447 (run-hooks 'news-reply-header-hook)
8448 (mail-yank-original nil))
8450 (defun gnus-mail-send-and-exit ()
8452 (let ((reply gnus-article-reply))
8453 (mail-send-and-exit nil)
8455 (get-buffer (car reply))
8456 (buffer-name (car reply)))
8458 (set-buffer (car reply))
8459 (gnus-summary-mark-article-as-replied
8461 (if gnus-winconf-post-news
8462 (set-window-configuration gnus-winconf-post-news)))
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))
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))
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)
8489 (defun gnus-mail-other-window-using-mail ()
8490 "Compose mail other window using mail."
8491 (news-mail-other-window)
8492 (gnus-overload-functions))
8499 (defvar gnus-dribble-ignore nil)
8501 (defun gnus-dribble-file-name ()
8502 (concat gnus-startup-file "-dribble"))
8504 (defun gnus-dribble-open ()
8507 (setq gnus-dribble-buffer (find-file-noselect (gnus-dribble-file-name))))
8508 (buffer-disable-undo (current-buffer))
8509 (bury-buffer gnus-dribble-buffer)
8511 (goto-char (point-max))))
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))))
8521 (defun gnus-dribble-read-file ()
8522 (let ((dribble-file (gnus-dribble-file-name)))
8524 (set-buffer (setq gnus-dribble-buffer
8526 (file-name-nondirectory dribble-file))))
8527 (gnus-add-current-to-buffer-list)
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))
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? ")
8544 (message "Reading %s..." dribble-file)
8545 (eval-current-buffer)
8546 (message "Reading %s...done" dribble-file)))))))))
8548 (defun gnus-dribble-delete-file ()
8550 (set-buffer gnus-dribble-buffer)
8551 (let ((auto (make-auto-save-file-name)))
8552 (if (file-exists-p auto)
8554 (if (file-exists-p (gnus-dribble-file-name))
8555 (delete-file (gnus-dribble-file-name)))
8557 (set-buffer-modified-p nil))))
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))
8564 (set-buffer gnus-dribble-buffer)
8567 (defun gnus-dribble-clear ()
8569 (if (and gnus-dribble-buffer
8570 (get-buffer gnus-dribble-buffer)
8571 (buffer-name (get-buffer gnus-dribble-buffer)))
8573 (set-buffer gnus-dribble-buffer)
8575 (set-buffer-modified-p nil)
8576 (setq buffer-saved-size (buffer-size))))))
8579 ;;; Server Communication
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."
8586 (if gnus-current-select-method
8587 ;; Stream is already opened.
8589 ;; Open NNTP server.
8590 (if (null gnus-nntp-service) (setq gnus-nntp-server nil))
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)))
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))
8613 (list 'nntp gnus-nntp-server))))))
8615 (setq how (car gnus-select-method))
8616 (setq where (car (cdr gnus-select-method)))
8617 (cond ((eq how 'nnspool)
8619 (message "Looking up local news spool..."))
8622 (message "Looking up private directory..."))
8625 (setq gnus-current-select-method gnus-select-method)
8626 (run-hooks 'gnus-open-server-hook)
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"
8634 gnus-select-method)))
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.
8642 ;; Open NNTP server.
8643 (message "Opening server %s on %s..." (car method) (nth 1 method))
8644 (run-hooks 'gnus-open-server-hook)
8646 (or (gnus-server-opened method)
8647 (gnus-open-server method)))))
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))
8658 (defun gnus-get-function (method function)
8659 (let ((func (intern (format "%s-%s" (car method) function))))
8660 (if (not (fboundp func))
8662 (require (car method))
8663 (if (not (fboundp func))
8664 (error "No such function: %s" func))))
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)))
8671 (defun gnus-close-server (method)
8672 (funcall (gnus-get-function method 'close-server) (nth 1 method)))
8674 (defun gnus-request-list (method)
8675 (funcall (gnus-get-function method 'request-list) (nth 1 method)))
8677 (defun gnus-request-list-newsgroups (method)
8678 (funcall (gnus-get-function method 'request-list-newsgroups) (nth 1 method)))
8680 (defun gnus-server-opened (method)
8681 (funcall (gnus-get-function method 'server-opened) (nth 1 method)))
8683 (defun gnus-status-message (method)
8684 (funcall (gnus-get-function method 'status-message) (nth 1 method)))
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)))
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))))
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)))
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))))
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)))
8711 (if (and gnus-post-method
8712 (memq 'post (member (car gnus-current-select-method)
8713 gnus-valid-select-methods)))
8715 gnus-current-select-method)))
8716 (funcall (gnus-get-function method 'request-post-buffer)
8717 post header artbuf (gnus-group-real-name group) info)))
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)
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))))
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)))
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))))
8743 (defun gnus-find-method-for-group (group)
8744 (let ((info (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
8750 (defun gnus-check-backend-function (func group)
8751 (let ((method (if (stringp group) (car (gnus-find-method-for-group group))
8753 (fboundp (intern (format "%s-%s" method func)))))
8755 (defun gnus-methods-using (method)
8756 (let ((valids gnus-valid-select-methods)
8759 (if (memq method (car valids))
8760 (setq outs (cons (car valids) outs)))
8761 (setq valids (cdr valids)))
8765 ;;; Active & Newsrc File Handling
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.
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))
8788 ;; Gnus internal format of gnus-active-hashtb:
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.)
8796 ;; Gnus internal format of gnus-killed-list and gnus-zombie-list:
8797 ;; ("alt.misc" "alt.test" "alt.general" ...)
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
8806 ;; Clear some variables to re-initialize news information.
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)))
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))))
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."
8833 (if (not gnus-have-read-active-file) (gnus-read-active-file))
8834 (if (not (gnus-check-first-time-used))
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'.
8842 (setq group (symbol-name sym))
8843 (if (or (gnus-gethash group gnus-killed-hashtb)
8844 (gnus-gethash group gnus-newsrc-hashtb))
8846 (if (and gnus-newsrc-options-n-yes
8847 (string-match gnus-newsrc-options-n-yes group))
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)))
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)))))))
8863 (gnus-subscribe-hierarchical-interactive new-newsgroups))
8864 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
8866 (message "%d new newsgroup%s arrived."
8867 groups (if (> groups 1) "s have" " has"))))))
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")))
8874 (message "First time user; subscribing you to default groups")
8875 (let ((groups gnus-default-subscribed-newsgroups)
8879 (setq groups (or groups gnus-backup-default-subscribed-newsgroups))
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)))))
8891 (if (gnus-gethash (car groups) gnus-active-hashtb)
8892 (gnus-group-change-level (car groups) 3 9))
8893 (setq groups (cdr groups)))))))
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'
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
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
8916 (if fromkilled (setq group (nth 1 entry))
8917 (setq group (car (nth 2 entry))))
8919 (if (and (stringp entry)
8922 (setq entry (gnus-gethash entry gnus-newsrc-hashtb)))
8923 (if (and (not oldlevel)
8925 (setq oldlevel (car (cdr (nth 2 entry)))))
8926 (if (stringp previous)
8927 (setq previous (gnus-gethash previous gnus-newsrc-hashtb)))
8930 (format "(gnus-group-change-level %S %S %S %S %S)"
8931 group level oldlevel (car (nth 2 previous)) fromkilled))
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)
8939 (setq gnus-zombie-list (delete group gnus-zombie-list))
8940 (setq gnus-killed-list (delete group gnus-killed-list))))
8944 (gnus-sethash (car (nth 2 entry))
8945 nil gnus-newsrc-hashtb)
8947 (setcdr (gnus-gethash (car (nth 3 entry))
8950 (setcdr (cdr entry) (cdr (cdr (cdr entry))))))))
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.
8957 (setq gnus-zombie-list (cons group gnus-zombie-list))
8958 (setq gnus-killed-list (cons group gnus-killed-list))))
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.
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)
8976 (gnus-sethash group (cons num (if previous (cdr previous)
8980 (setcdr (gnus-gethash (car (car (cdr 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))))))
8988 (defun gnus-kill-newsgroup (newsgroup)
8989 "Obsolete function. Kills a newsgroup."
8990 (gnus-group-change-level (gnus-gethash newsgroup gnus-newsrc-hashtb) 9))
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
8996 (let ((newsrc (cdr gnus-newsrc-assoc))
8997 (dead-lists '(gnus-killed-list gnus-zombie-list))
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.
9003 (setq group (car (car newsrc)))
9004 (if (or (gnus-gethash group gnus-active-hashtb)
9005 (nth 4 (car newsrc))
9008 (format "Delete bogus newsgroup: %s " group)))))
9009 ;; Active newsgroup.
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.
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.
9024 (setq killed (symbol-value (car dead-lists)))
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)))
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")))
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))
9044 (message "Checking new news...")
9046 (setq info (car newsrc))
9047 (setq group (car info))
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
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)))
9061 (gnus-sethash group nil gnus-active-hashtb)
9062 (setcar (gnus-gethash group gnus-newsrc-hashtb) t))))
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
9071 ;; If this is a bogus group, there's not much we can do.
9073 (gnus-get-unread-articles-in-group info active))
9074 (setq newsrc (cdr newsrc)))
9075 (message "Checking new news... done")))
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)
9083 (setq gnus-newsrc-hashtb (gnus-make-hashtable (length alist)))
9085 (setq prev (setq gnus-newsrc-assoc
9086 (cons (list "dummy.group" 0 (cons 0 0)) alist))))
9088 (gnus-sethash (car (car alist)) (cons nil prev) gnus-newsrc-hashtb)
9090 (setq alist (cdr alist)))))
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))
9096 (setq gnus-killed-hashtb
9097 (gnus-make-hashtable
9098 (+ (length gnus-killed-list) (length gnus-zombie-list))))
9100 (setq list (symbol-value (car lists)))
9101 (setq lists (cdr lists))
9103 (gnus-sethash (car list) (car list) gnus-killed-hashtb)
9104 (setq list (cdr list))))))
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))
9114 (setq num (- (1+ (cdr active)) (car active))))
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)))
9123 ;; The read list is a list of ranges. Fix them according to
9124 ;; the active hash table.
9126 (setq lowest (1- (car active)))
9127 (while (and (< (cdr (car srange)) lowest))
9128 (if (and (cdr srange)
9129 (<= (cdr (car srange)) (1+ lowest)))
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.
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)))
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)))))
9150 (defun gnus-activate-newsgroup (group &optional real-group-name)
9152 (if (gnus-request-group group)
9154 (set-buffer nntp-server-buffer)
9156 (if (looking-at "[0-9]+ [0-9]+ \\([0-9]+\\) \\([0-9]+\\)")
9159 (cons (string-to-int (buffer-substring (match-beginning 1)
9162 (buffer-substring (match-beginning 2)
9164 gnus-active-hashtb))))
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))
9179 (unread (sort (copy-sequence unread) (function <)))
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...
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
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
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)))))))
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))))
9226 (if (gnus-request-list gnus-select-method) ; Get active
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."))))
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.
9248 (goto-char (point-min))
9249 (if (or (re-search-forward "\n.\r?$" nil t)
9250 (goto-char (point-max)))
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
9259 (let ((mod-hashtb (make-vector 7 0))
9262 (setq group (let ((obarray gnus-active-hashtb))
9264 (setq max (read cur))
9265 (set group (cons (read cur) max))
9266 ;; Enter moderated groups into a list.
9268 (symbol-name (let ((obarray mod-hashtb)) (read cur)))
9270 (setq gnus-moderated-list
9271 (cons (symbol-name group) gnus-moderated-list)))
9273 ;; And if we do not care about moderation, we use this loop,
9277 ;; group gets set to a symbol interned in gnus-active-hashtb
9279 (setq group (let ((obarray gnus-active-hashtb))
9281 (setq max (read cur))
9282 (set group (cons (read cur) max))
9283 (forward-line 1)))))))
9285 (defun gnus-read-newsrc-file (&optional force)
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))
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")))
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)
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).
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))))
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)
9328 (load ding-file t t t)
9330 (gnus-make-hashtable-from-newsrc-alist)
9331 (if (not (file-newer-than-file-p file ding-file))
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
9337 (gnus-read-old-newsrc-el-file file))))
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)
9343 (let (gnus-killed-assoc gnus-marked-assoc gnus-newsrc-assoc)
9348 (setq newsrc gnus-newsrc-assoc
9349 killed gnus-killed-assoc
9350 marked gnus-marked-assoc)))
9351 (setq gnus-newsrc-assoc nil)
9353 (setq group (car newsrc))
9354 (let ((info (nth 2 (gnus-gethash (car group) gnus-newsrc-hashtb))))
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
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)
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)))
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)
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
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))
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:
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:
9413 ;; options -n all !net.sf-lovers !mod.human-nets -r
9416 ;; A string of lines beginning with a space or tab after the
9417 ;; initial options line will be considered continuation
9420 ;; For now, we only accept it at the beginning of the file.
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)
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)
9438 ;; Gather all "-n" options lines.
9441 (if gnus-newsrc-options
9442 (while (and (string-match "^[ \t]*-n\\([^\n]*\\)$"
9445 (setq start (match-end 0)))
9446 (setq result (concat result
9448 (substring gnus-newsrc-options
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)))
9456 (defun gnus-parse-newsrc-body ()
9457 ;; Point has been positioned after the options lines. We shouldn't
9458 ;; see any more in here.
9460 (let ((subscribe nil)
9462 (line (1+ (count-lines (point-min) (point))))
9466 (skip-chars-forward " \t")
9469 ((= (following-char) ?\n)
9474 (skip-chars-forward "^:!\n")
9475 (if (= (following-char) ?\n)
9476 (error "line %d is unparsable in %s" line (buffer-name)))
9478 (skip-chars-backward " \t")
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)))
9487 (setq subscribe (= (following-char) ?:))
9488 (setq read-list nil)
9490 (forward-char 1) ; after : or !
9491 (skip-chars-forward " \t")
9492 (while (not (= (following-char) ?\n))
9493 (skip-chars-forward " \t")
9496 ((looking-at "\\([0-9]+\\)-\\([0-9]+\\)") ; a range
9501 ;; faster that buffer-substring/string-to-int
9502 (narrow-to-region (point-min) (match-end 1))
9503 (read (current-buffer)))
9505 (narrow-to-region (point-min) (match-end 2))
9506 (forward-char) ; skip over "-"
9508 (read (current-buffer))
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)))
9517 (setq read-list (cons (cons p p) read-list))
9520 ;; bogus chars in ranges
9523 (goto-char (match-end 0))
9524 (skip-chars-forward " \t")
9525 (cond ((= (following-char) ?,)
9528 ((= (following-char) ?\n)
9531 ;; bogus char after range
9533 ;; if we get here, the parse failed
9535 (end-of-line) ; give up on this line
9537 (message "Ignoring bogus line %d for %s in %s"
9538 line newsgroup (buffer-name))
9541 (let ((info (nth 2 (gnus-gethash newsgroup gnus-newsrc-hashtb))))
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
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))
9555 (setq gnus-newsrc-assoc (nreverse gnus-newsrc-assoc))
9556 (gnus-make-hashtable-from-newsrc-alist)
9559 (defun gnus-parse-n-options (options)
9560 "Parse -n NEWSGROUPS options and return a cons of YES and NO regexps."
9563 (yes-or-no nil) ;`!' or not.
9565 ;; Parse each newsgroup description such as "comp.all". Commas
9566 ;; and white spaces can be a newsgroup separator.
9568 (string-match "^[ \t\n,]*\\(!?\\)\\([^- \t\n,][^ \t\n,]*\\)" options)
9570 (substring options (match-beginning 1) (match-end 1)))
9574 (match-beginning 2) (match-end 2))))
9575 (setq options (substring options (match-end 2)))
9576 ;; Rewrite "all" to ".+" not ".*". ".+" requires at least one
9578 (while (string-match "\\(^\\|\\\\[.]\\)all\\(\\\\[.]\\|$\\)" newsgroup)
9580 (concat (substring newsgroup 0 (match-end 1))
9582 (substring newsgroup (match-beginning 2)))))
9584 (cond ((string-equal yes-or-no "!")
9585 (setq no (cons newsgroup no)))
9586 ((string-equal newsgroup ".+")) ;Ignore `all'.
9588 (setq yes (cons newsgroup yes))))
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
9594 ;; Fixes for large regexp problems are from yonezu@nak.math.keio.ac.jp.
9597 (apply (function concat)
9600 (concat newsgroup "\\|"))
9602 (car yes) "\\)\\(\\.\\|$\\)"))
9605 (apply (function concat)
9608 (concat newsgroup "\\|"))
9610 (car no) "\\)\\(\\.\\|$\\)")))
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
9620 (if (= 0 (save-excursion
9621 (set-buffer gnus-dribble-buffer)
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))
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")
9648 (kill-buffer (current-buffer))
9649 (message "Saving %s.eld... done" gnus-current-startup-file)
9650 (gnus-dribble-delete-file)))))
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))
9660 ;; insert lisp expressions.
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))
9671 (setq variables (cdr variables)))))
9673 (defun gnus-gnus-to-newsrc-format ()
9674 (let ((newsrc (cdr gnus-newsrc-assoc))
9677 (set-buffer (create-file-buffer gnus-startup-file))
9678 (buffer-disable-undo (current-buffer))
9680 ;; Bug by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
9681 (if gnus-newsrc-options (insert "options " gnus-newsrc-options "\n"))
9683 (setq group (car newsrc))
9684 (insert (car group) (if (>= (nth 1 group) 6) "!" ":"))
9685 (if (setq ranges (nth 2 group))
9688 (gnus-ranges-to-newsrc-format
9689 (if (atom (car ranges)) (list ranges) ranges))))
9691 (setq newsrc (cdr newsrc)))
9692 (write-region 1 (point-max) gnus-startup-file nil 'nomesg)
9693 (kill-buffer (current-buffer)))))
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.
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 ","))
9708 (insert (int-to-string (car range))
9710 (int-to-string (cdr range)))
9711 (if ranges (insert ","))
9715 (defun gnus-read-descriptions-file ()
9716 (message "Reading descriptions file...")
9717 (if (not (gnus-request-list-newsgroups gnus-select-method))
9719 (message "Couldn't read newsgroups descriptions")
9722 (setq gnus-description-hashtb
9723 (gnus-make-hashtable (length gnus-active-hashtb)))
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)))
9734 (narrow-to-region (point-min) (point))))
9735 (goto-char (point-min))
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))))
9743 (message "Reading descriptions file...done")
9748 ;;; gnus.el ends here