1 ;;; (ding) Gnus: a newsreader for GNU Emacs
2 ;; Copyright (C) 1987,88,89,90,93,94,95 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
44 ;; Customization variables
46 (defvar gnus-select-method
47 (list 'nntp (or (getenv "NNTPSERVER")
48 (if (and gnus-default-nntp-server
49 (not (string= gnus-default-nntp-server "")))
50 gnus-default-nntp-server)
53 "Default method for selecting a newsgroup.
54 This variable should be a list, where the first element is how the
55 news is to be fetched, the second is the address, and the optional
56 third element is the \"port number\", if nntp is used.
58 For instance, if you want to get your news via NNTP from
59 \"flab.flab.edu\" on port 23, you could say:
61 (setq gnus-select-method '(nntp \"flab.flab.edu\" 23))
63 If you want to use your local spool, say:
65 (setq gnus-select-method (list 'nnspool (system-name)))
67 If you use this variable, you must set `gnus-nntp-server' to nil.")
69 ;; Added by Sudish Joseph <joseph@cis.ohio-state.edu>.
70 (defvar gnus-post-method nil
71 "Preferred method for posting USENET news.
72 If this variable is nil, GNUS will use the current method to decide
73 which method to use when posting. If it is non-nil, it will override
74 the current method. This method will not be used in mail groups and
75 the like, only in \"real\" newsgroups.
77 The value must be a valid method as discussed in the documentation of
78 `gnus-select-method'.")
80 (defvar gnus-default-nntp-server nil
81 "Specify a default NNTP server.
82 This variable should be defined in paths.el.")
84 (defvar gnus-secondary-servers nil
85 "List of NNTP servers that the user can choose between interactively.
86 The list should contain lists, where each list contains the name of
87 the server. To make Gnus query you for a server, you have to give
88 `gnus' a non-numeric prefix - `C-u M-x gnus', in short.")
90 (defvar gnus-nntp-server nil
91 "*The name of the host running the NNTP server.
92 This variable is semi-obsolete. Use the `gnus-select-method'
95 (defvar gnus-nntp-service "nntp"
96 "NNTP service name (\"nntp\" or 119).
97 This is an obsolete variable, which is scarcely used. If you use an
98 nntp server for your newsgroup and want to change the port number
99 used to 899, you would say something along these lines:
101 (setq gnus-select-method '(nntp \"my.nntp.server\" 899))")
103 (defvar gnus-startup-file "~/.newsrc"
104 "Your `.newsrc' file. Use `.newsrc-SERVER' instead if it exists.")
106 (defvar gnus-signature-file "~/.signature"
107 "Your `.signature' file.")
109 (defvar gnus-init-file "~/.gnus"
110 "Your Gnus elisp startup file.
111 If a file with the .el or .elc suffixes exist, they will be read
114 (defvar gnus-default-subscribed-newsgroups nil
115 "This variable lists what newsgroups should be susbcribed the first time Gnus is used.
116 It should be a list of strings.
117 If it is `t', Gnus will not do anything special the first time it is
118 started; it'll just use the normal newsgroups subscription methods.")
120 (defconst gnus-backup-default-subscribed-newsgroups
121 '("news.announce.newusers" "news.groups.questions")
122 "Default default new newsgroups the first time Gnus is run.")
124 (defvar gnus-post-prepare-function nil
125 "Function that is run after a post buffer has been prepared.
126 It is called with the name of the newsgroup that is posted to. It
127 might be use, for instance, for inserting signatures based on the
128 newsgroup name. (In that case, `gnus-signature-file' and
129 `mail-signature' should both be set to nil).")
131 (defvar gnus-use-cross-reference t
132 "Non-nil means that cross referenced articles will be marked as read.
133 If nil, ignore cross references. If t, mark articles as read in
136 (defvar gnus-use-followup-to 'use
137 "Specifies what to do with Followup-To field.
138 If nil, ignore the field. If it is t, use its value, but ignore
139 `poster'. If it is neither nil nor t, always use the value.")
141 (defvar gnus-followup-to-function nil
142 "A variable that contains a function that returns a followup address.
143 The function will be called in the buffer of the article that is being
144 followed up. The buffer will be narrowed to the headers of the
145 article. To pick header fields, one might use `mail-fetch-field'. The
146 function will be called with the name of the current newsgroup as the
149 Here's an example `gnus-followup-to-function':
151 (setq gnus-followup-to-function
153 (cond ((string= group \"mail.list\")
154 (or (mail-fetch-field \"sender\")
155 (mail-fetch-field \"from\")))
157 (or (mail-fetch-field \"reply-to\")
158 (mail-fetch-field \"from\"))))))")
160 (defvar gnus-reply-to-function nil
161 "A variable that contains a function that returns a reply address.
162 See the `gnus-followup-to-function' variable for an explanation of how
163 this variable is used.")
165 (defvar gnus-large-newsgroup 200
166 "The number of articles which indicates a large newsgroup.
167 If the number of articles in a newsgroup is greater than the value,
168 confirmation is required for selecting the newsgroup.")
170 (defvar gnus-author-copy (getenv "AUTHORCOPY")
171 "Name of the file the article will be saved before it is posted using the FCC: field.
172 Initialized from the AUTHORCOPY environment variable.
174 Articles are saved using a function specified by the the variable
175 `gnus-author-copy-saver' (`rmail-output' is default) if a file name is
176 given. Instead, if the first character of the name is `|', the
177 contents of the article is piped out to the named program. It is
178 possible to save an article in an MH folder as follows:
180 \(setq gnus-author-copy \"|/usr/local/lib/mh/rcvstore +Article\")")
182 (defvar gnus-author-copy-saver (function rmail-output)
183 "A function called with a file name to save an author copy to.
184 The default function is `rmail-output' which saves in Unix mailbox format.")
186 (defvar gnus-use-long-file-name (not (memq system-type '(usg-unix-v xenix)))
187 "Non-nil means that the default name of a file to save articles in is the newsgroup name.
188 If it's nil, the directory form of the newsgroup name is used instead.")
190 (defvar gnus-article-save-directory (getenv "SAVEDIR")
191 "Name of the directory articles will be saved in (default \"~/News\").
192 Initialized from the SAVEDIR environment variable.")
194 (defvar gnus-kill-files-directory (getenv "SAVEDIR")
195 "Name of the directory where kill files will be stored (default \"~/News\").
196 Initialized from the SAVEDIR environment variable.")
198 (defvar gnus-kill-expiry-days 7
199 "*Number of days before unused kill file entries are expired.")
201 (defvar gnus-default-article-saver (function gnus-summary-save-in-rmail)
202 "A function to save articles in your favorite format.
203 The function must be interactively callable (in other words, it must
204 be an Emacs command).
206 Gnus provides the following functions:
208 * gnus-summary-save-in-rmail (Rmail format)
209 * gnus-summary-save-in-mail (Unix mail format)
210 * gnus-summary-save-in-folder (MH folder)
211 * gnus-summary-save-in-file (article format).")
213 (defvar gnus-rmail-save-name (function gnus-plain-save-name)
214 "A function generating a file name to save articles in Rmail format.
215 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
217 (defvar gnus-mail-save-name (function gnus-plain-save-name)
218 "A function generating a file name to save articles in Unix mail format.
219 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
221 (defvar gnus-folder-save-name (function gnus-folder-save-name)
222 "A function generating a file name to save articles in MH folder.
223 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER.")
225 (defvar gnus-file-save-name (function gnus-numeric-save-name)
226 "A function generating a file name to save articles in article format.
227 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
229 (defvar gnus-kill-file-name "KILL"
230 "Suffix of the kill files.")
232 (defvar gnus-visual t
233 "*If non-nil, will do various highlighting.
234 If nil, no mouse highlight (or any other) will be performed. This
235 might speed up Gnus some when generating large Newsgroup and Summary
238 (defvar gnus-novice-user t
239 "*Non-nil means that you are a usenet novice.
240 If non-nil, verbose messages may be displayed and confirmations may be
243 (defvar gnus-expert-user nil
244 "*Non-nil means that you will never be asked for confirmation about anything.
245 And that means *anything*.")
247 (defvar gnus-keep-same-level nil
248 "Non-nil means that the next newsgroup after the current will be on the same level.
249 When you type, for instance, `n' after reading the last article in the
250 current newsgroup, you will go to the next newsgroup. If this variable
251 is nil, the next newsgroup will be the next from the Newsgroup
252 buffer. If this variable is non-nil, Gnus will either put you in the
253 next newsgroup with the same level, or, if no such newsgroup is
254 available, the next newsgroup with the lowest possible level higher
255 than the current level.")
257 (defvar gnus-gather-loose-threads t
258 "Non-nil means sub-threads from a common thread will be gathered.
259 If the root of a thread has expired or been read in a previous
260 session, the information necessary to build a complete thread has been
261 lost. Instead of having many small sub-threads from this original thread
262 scattered all over the Summary buffer, Gnus will gather them. If the
263 `gnus-summary-make-false-root' variable is non-nil, Gnus will also
264 present them as one thread with a new root.")
266 (defvar gnus-summary-make-false-root 'adopt
267 "nil means that Gnus won't print dummy roots of threads in the summary buffer.
268 If `gnus-gather-loose-threads' is non-nil, Gnus will try to gather all
269 loose sub-threads from an original thread into one large thread. If
270 this variable is nil, these sub-threads will not get a common root,
271 but will just be presented after one another. If this variable is
272 `dummy', Gnus will create a dummy root that will have all the
273 sub-threads as children.
274 If this variable is `adopt', Gnus will make one of the \"children\"
275 the parent and mark all the step-children as such.
276 If this variable is `empty', the \"children\" are printed with empty
279 (defvar gnus-check-new-newsgroups t
280 "Non-nil means that Gnus will add new newsgroups at startup.
281 If this variable is nil, then you have to tell Gnus explicitly to
282 check for new newsgroups with \\<gnus-group-mode-map>\\[gnus-find-new-newsgroups].")
284 (defvar gnus-check-bogus-newsgroups nil
285 "Non-nil means that Gnus will check and delete bogus newsgroup at startup.
286 If this variable is nil, then you have to tell Gnus explicitly to
287 check for bogus newsgroups with \\<gnus-group-mode-map>\\[gnus-group-check-bogus-groups].")
289 (defvar gnus-read-active-file t
290 "Non-nil means that Gnus will read the entire active file at startup.
291 If this variable is nil, Gnus will only read parts of the active file.")
293 (defvar gnus-activate-foreign-newsgroups nil
294 "If nil, Gnus will not check foreign newsgroups at startup.
295 If it is non-nil, it should be a number between one and nine. Foreign
296 newsgroups that have a level lower or equal to this number will be
297 activated on startup. For instance, if you want to active all
298 subscribed newsgroups, but not the rest, you'd set this variable to 5.
300 If you subscribe to lots of newsgroups from different servers, startup
301 might take a while. By setting this variable to nil, you'll save time,
302 but you won't be told how many unread articles there are in the
305 (defvar gnus-save-newsrc-file t
306 "Non-nil means that Gnus will save a .newsrc file.
307 Gnus always saves its own startup file, which is called \".newsrc.el\".
308 The file called \".newsrc\" is in a format that can be readily
309 understood by other newsreaders. If you don't plan on using other
310 newsreaders, set this variable to nil to save some time on exit.")
312 (defvar gnus-save-killed-list t
313 "If non-nil, save the list of killed groups to the startup file.
314 This will save both time (when starting and quitting) and space (on
315 disk), but it will also mean that Gnus has no record of what
316 newsgroups are new or old, so the automatic new newsgroups
317 subscription methods become meaningless. You should always set
318 `gnus-check-new-newsgroups' to nil if you set this variable to nil.")
320 (defvar gnus-interactive-catchup t
321 "Require your confirmation when catching up a newsgroup if non-nil.")
323 (defvar gnus-interactive-post t
324 "Newsgroup and subject will be asked for if non-nil.")
326 (defvar gnus-interactive-exit t
327 "Require your confirmation when exiting Gnus if non-nil.")
329 (defvar gnus-kill-killed t
330 "If non-nil, Gnus will apply kill files to already \"killed\" articles.
331 If it is nil, Gnus will never apply kill files to articles that have
332 already been through the kill process, which might very well save lots
335 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
336 (defvar gnus-summary-same-subject ""
337 "String indicating that the current article has the same subject as the previous.")
339 (defvar gnus-summary-default-interest nil
340 "Default article interest level.
341 If this variable is nil, interest levels will not be used.")
343 (defvar gnus-user-login-name nil
344 "The login name of the user.
345 Got from the function `user-login-name' if undefined.")
347 (defvar gnus-user-full-name nil
348 "The full name of the user.
349 Got from the NAME environment variable if undefined.")
351 (defvar gnus-show-mime nil
352 "*Show MIME message if non-nil.")
354 (defvar gnus-show-threads t
355 "*Show conversation threads in Summary Mode if non-nil.")
357 (defvar gnus-thread-hide-subtree nil
358 "Non-nil means hide thread subtrees initially.
359 If non-nil, you have to run the command `gnus-summary-show-thread' by
360 hand or by using `gnus-select-article-hook' to show hidden threads.")
362 (defvar gnus-thread-hide-killed t
363 "Non-nil means hide killed thread subtrees automatically.")
365 (defvar gnus-thread-ignore-subject nil
366 "Don't take care of subject differences, but only references if non-nil.
367 If it is non-nil, some commands work with subjects do not work properly.")
369 (defvar gnus-thread-indent-level 4
370 "Indentation of thread subtrees.")
372 ;; jwz: nuke newsgroups whose name is all digits - that means that
373 ;; some loser has let articles get into the root of the news spool,
374 ;; which is toxic. Lines beginning with whitespace also tend to be
376 (defvar gnus-ignored-newsgroups
377 (purecopy (mapconcat 'identity
378 '("^to\\." ; not "real" groups
379 "^[0-9. \t]+ " ; all digits in name
380 "[][\"#'();\\]" ; bogus characters
383 "A regexp to match uninteresting newsgroups in the active file.
384 Any lines in the active file matching this regular expression are
385 removed from the newsgroup list before anything else is done to it,
386 thus making them effectively non-existant.")
388 (defvar gnus-ignored-headers
389 "^Path:\\|^Posting-Version:\\|^Article-I.D.:\\|^Expires:\\|^Date-Received:\\|^References:\\|^Control:\\|^Xref:\\|^Lines:\\|^Posted:\\|^Relay-Version:\\|^Message-ID:\\|^Nf-ID:\\|^Nf-From:\\|^Approved:\\|^Sender:\\|^Received:\\|^Mail-from:"
390 "All header lines that match this regexp will be hidden.
391 Also see `gnus-visible-headers'.")
393 (defvar gnus-visible-headers "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:"
394 "All header lines that do not match this regexp will be hidden.
395 Also see `gnus-ignored-headers'.")
397 (defvar gnus-sorted-header-list
398 '("^From:" "^Subject:" "^Summary:" "^Keywords:" "^Newsgroups:"
399 "^Date:" "^Organization:")
400 "This variable is a list of regular expressions.
401 If it is non-nil, header lines that match the regular expressions will
402 be placed first in the Article buffer in the sequence specified by
405 (defvar gnus-required-headers
406 '(From Date Newsgroups Subject Message-ID Organization Lines X-Newsreader)
407 ;; changed by jwz because it's not so nice to do "Lines: 0" by default.
408 ;; and to remove Path, since it's incorrect for Gnus to try
409 ;; and generate that - it is the responsibility of inews or nntpd.
410 "All required fields for articles you post.
411 RFC977 and RFC1036 require From, Date, Newsgroups, Subject, Message-ID
412 and Path fields. Organization, Lines and X-Newsreader are optional.
413 If you want Gnus not to insert some field, remove it from this
416 (defvar gnus-show-all-headers nil
417 "*Show all headers of an article if non-nil.")
419 (defvar gnus-save-all-headers t
420 "*Save all headers of an article if non-nil.")
422 (defvar gnus-inhibit-startup-message nil
423 "The startup message will not be displayed if this function is non-nil.")
425 (defvar gnus-auto-extend-newsgroup t
426 "Extend visible articles to forward and backward if non-nil.")
428 (defvar gnus-auto-select-first t
429 "Select the first unread article automagically if non-nil.
430 If you want to prevent automatic selection of the first unread article
431 in some newsgroups, set the variable to nil in `gnus-select-group-hook'
432 or `gnus-apply-kill-hook'.")
434 (defvar gnus-auto-select-next t
435 "Select the next newsgroup automagically if non-nil.
436 If the value is t and the next newsgroup is empty, Gnus will exit
437 Summary mode and go back to Group mode. If the value is neither nil
438 nor t, Gnus will select the following unread newsgroup. Especially, if
439 the value is the symbol `quietly', the next unread newsgroup will be
440 selected without any confirmations.")
442 (defvar gnus-auto-select-same nil
443 "Select the next article with the same subject automagically if non-nil.")
445 (defvar gnus-auto-center-summary t
446 "*Always center the current summary in Gnus Summary window if non-nil.")
448 (defvar gnus-auto-mail-to-author nil
449 "Insert `To: author' of the article when following up if non-nil.
450 Mail is sent using the function specified by the variable
451 `gnus-mail-send-method'.")
453 (defvar gnus-break-pages t
454 "*Break an article into pages if non-nil.
455 Page delimiter is specified by the variable `gnus-page-delimiter'.")
457 (defvar gnus-page-delimiter "^\^L"
458 "Regexp describing line-beginnings that separate pages of news article.")
460 (defvar gnus-digest-show-summary t
461 "Show a summary of undigestified messages if non-nil.")
463 (defvar gnus-digest-separator "^Subject:[ \t]"
464 "Regexp that separates messages in a digest article.")
466 (defvar gnus-use-full-window t
467 "*Non-nil means to take up the entire screen of Emacs.")
469 (defvar gnus-window-configuration
473 "Specify window configurations for each action.
474 The format of the variable is either a list of (ACTION (G S A)), where
475 G, S, and A are the relative height of Group, Summary, and Article
476 windows, respectively, or a list of (ACTION FUNCTION), where FUNCTION
477 is a function that will be called with ACTION as an argument. ACTION
478 can be `summary', `newsgroups', or `article'.")
480 (defvar gnus-show-mime-method (function metamail-buffer)
481 "Function to process a MIME message.
482 The function is expected to process current buffer as a MIME message.")
484 (defvar gnus-mail-reply-method
485 (function gnus-mail-reply-using-mail)
486 "Function to compose reply mail.
487 The function `gnus-mail-reply-using-mail' uses usual sendmail mail
488 program. The function `gnus-mail-reply-using-mhe' uses the MH-E mail
489 program. You can use yet another program by customizing this variable.")
491 (defvar gnus-mail-forward-method
492 (function gnus-mail-forward-using-mail)
493 "Function to forward current message to another user.
494 The function `gnus-mail-reply-using-mail' uses usual sendmail mail
495 program. You can use yet another program by customizing this variable.")
497 (defvar gnus-mail-other-window-method
498 (function gnus-mail-other-window-using-mail)
499 "Function to compose mail in other window.
500 The function `gnus-mail-other-window-using-mail' uses the usual sendmail
501 mail program. The function `gnus-mail-other-window-using-mhe' uses the MH-E
502 mail program. You can use yet another program by customizing this variable.")
504 (defvar gnus-mail-send-method send-mail-function
505 "Function to mail a message too which is being posted as an article.
506 The message must have To: or Cc: field. The default is copied from
507 the variable `send-mail-function'.")
509 (defvar gnus-subscribe-newsgroup-method
510 (function gnus-subscribe-zombies)
511 "Function called with a newsgroup name when new newsgroup is found.
512 The function `gnus-subscribe-randomly' inserts a new newsgroup a the
513 beginning of newsgroups. The function `gnus-subscribe-alphabetically'
514 inserts it in strict alphabetic order. The function
515 `gnus-subscribe-hierarchically' inserts it in hierarchical newsgroup
516 order. The function `gnus-subscribe-interactively' asks for your decision.")
518 ;; Suggested by a bug report by Hallvard B Furuseth
519 ;; <h.b.furuseth@usit.uio.no>.
520 (defvar gnus-subscribe-options-newsgroup-method
521 (function gnus-subscribe-alphabetically)
522 "This function is called to subscribe newsgroups mentioned on \"options -n\" lines.
523 If, for instance, you want to subscribe to all newsgroups in the
524 \"no\" and \"alt\" hierarchies, you'd put the following in your
527 options -n no.all alt.all
529 Gnus will the subscribe all new newsgroups in these hierarchies with
530 the subscription method in this variable.")
532 ;; Mark variables suggested by Thomas Michanek
533 ;; <Thomas.Michanek@telelogic.se>.
534 (defvar gnus-unread-mark " "
535 "Mark used for unread articles.")
537 (defvar gnus-read-mark "D"
538 "Mark used for read articles.")
540 (defvar gnus-ticked-mark "-"
541 "Mark used for ticked articles.")
543 (defvar gnus-dormant-mark "+"
544 "Mark used for dormant articles.")
546 (defvar gnus-killed-mark "K"
547 "Mark used for killed articles.")
549 (defvar gnus-kill-file-mark "X"
550 "Mark used for articles killed by kill files.")
552 (defvar gnus-catchup-mark "C"
553 "Mark used for articles that are caught up.")
555 (defvar gnus-group-mode-hook nil
556 "A hook for Gnus Group Mode.")
558 (defvar gnus-summary-mode-hook nil
559 "A hook for Gnus Summary Mode.")
561 (defvar gnus-article-mode-hook nil
562 "A hook for Gnus Article Mode.")
564 (defvar gnus-kill-file-mode-hook nil
565 "A hook for Gnus KILL File Mode.")
567 (defvar gnus-open-server-hook nil
568 "A hook called just before opening connection to news server.")
570 (defvar gnus-startup-hook nil
571 "A hook called at startup time.
572 This hook is called after Gnus is connected to the NNTP server. So, it
573 is possible to change the behavior of Gnus according to the selected
576 (defvar gnus-group-prepare-hook nil
577 "A hook called after the newsgroup list is created in the Newsgroup buffer.
578 If you want to modify the Newsgroup buffer, you can use this hook.")
580 (defvar gnus-summary-prepare-hook nil
581 "A hook called after summary list is created in the Summary buffer.
582 If you want to modify the Summary buffer, you can use this hook.")
584 (defvar gnus-article-prepare-hook nil
585 "A hook called after an article is prepared in the Article buffer.
586 If you want to run a special decoding program like nkf, use this hook.")
588 (defvar gnus-article-display-hook nil
589 "A hook called after the article is displayed in the Article buffer.
590 The hook is designed to change the contents of the Article
591 buffer. Typical functions that this hook may contain are
592 `gnus-article-hide-headers' (hide selected headers),
593 `gnus-article-hide-signature' (hide signature) and
594 `gnus-article-treat-overstrike' (turn \"^H_\" into bold characters).")
595 (add-hook 'gnus-article-display-hook 'gnus-article-hide-headers-if-wanted)
597 (defvar gnus-select-group-hook nil
598 "A hook called when a newsgroup is selected.
599 If you want to sort Summary buffer by date and then by subject, you
600 can use the following hook:
602 (setq gnus-select-group-hook
605 ;; First of all, sort by date.
606 (gnus-keysort-headers
607 (function string-lessp)
609 (gnus-sortable-date (header-date a))))
610 ;; Then sort by subject string ignoring `Re:'.
611 ;; If case-fold-search is non-nil, case of letters is ignored.
612 (gnus-keysort-headers
613 (function string-lessp)
616 (downcase (gnus-simplify-subject (header-subject a) t))
617 (gnus-simplify-subject (header-subject a) t)))))))
619 If you'd like to simplify subjects like the
620 `gnus-summary-next-same-subject' command does, you can use the
623 (setq gnus-select-group-hook
626 (mapcar (lambda (header)
629 (gnus-simplify-subject
630 (header-subject header) 're-only)))
631 gnus-newsgroup-headers))))
634 (defvar gnus-select-article-hook
635 '(gnus-summary-show-thread)
636 "A hook called when an article is selected.
637 The default hook shows conversation thread subtrees of the selected
638 article automatically using `gnus-summary-show-thread'.
640 If you'd like to run RMAIL on a digest article automagically, you can
641 use the following hook:
643 \(setq gnus-select-article-hook
646 (gnus-summary-show-thread)
647 (cond ((string-equal \"comp.sys.sun\" gnus-newsgroup-name)
648 (gnus-summary-rmail-digest))
649 ((and (string-equal \"comp.text\" gnus-newsgroup-name)
650 (string-match \"^TeXhax Digest\"
651 (header-subject gnus-current-headers)))
652 (gnus-summary-rmail-digest)
655 (defvar gnus-select-digest-hook
658 ;; Reply-To: is required by `undigestify-rmail-message'.
659 (or (mail-position-on-field "Reply-to" t)
661 (mail-position-on-field "Reply-to")
662 (insert (gnus-fetch-field "From"))))))
663 "A hook called when reading digest messages using Rmail.
664 This hook can be used to modify incomplete digest articles as follows
665 \(this is the default):
667 \(setq gnus-select-digest-hook
670 ;; Reply-To: is required by `undigestify-rmail-message'.
671 (or (mail-position-on-field \"Reply-to\" t)
673 (mail-position-on-field \"Reply-to\")
674 (insert (gnus-fetch-field \"From\")))))))")
676 (defvar gnus-rmail-digest-hook nil
677 "A hook called when reading digest messages using Rmail.
678 This hook is intended to customize Rmail mode for reading digest articles.")
680 (defvar gnus-apply-kill-hook '(gnus-apply-kill-file)
681 "A hook called when a newsgroup is selected and summary list is prepared.
682 This hook is intended to apply a KILL file to the selected newsgroup.
683 The function `gnus-apply-kill-file' is called by default.
685 Since a general KILL file is too heavy to use only for a few
686 newsgroups, I recommend you to use a lighter hook function. For
687 example, if you'd like to apply a KILL file to articles which contains
688 a string `rmgroup' in subject in newsgroup `control', you can use the
691 \(setq gnus-apply-kill-hook
694 (cond ((string-match \"control\" gnus-newsgroup-name)
695 (gnus-kill \"Subject\" \"rmgroup\")
696 (gnus-expunge \"X\"))))))")
698 (defvar gnus-visual-mark-article-hook 'gnus-visual-highlight-selected-summary
699 "Hook run after selecting an article in the Summary buffer.
700 It is meant to be used for highlighting the article in some way. It is
701 not run if `gnus-visual' is nil.")
703 (defvar gnus-prepare-article-hook (list (function gnus-inews-insert-signature))
704 "A hook called after preparing body, but before preparing header fields.
705 The default hook (`gnus-inews-insert-signature') inserts a signature
706 file specified by the variable `gnus-signature-file'.")
708 (defvar gnus-inews-article-hook (list (function gnus-inews-do-fcc))
709 "A hook called before finally posting an article.
710 The default hook (`gnus-inews-do-fcc') does FCC processing (save article
713 (defvar gnus-exit-group-hook nil
714 "A hook called when exiting (not quitting) Summary mode.
715 If your machine is so slow that exiting from Summary mode takes very
716 long time, set the variable `gnus-use-cross-reference' to nil. This
717 inhibits marking articles as read using cross-reference information.")
719 (defvar gnus-suspend-gnus-hook nil
720 "A hook called when suspending (not exiting) Gnus.")
722 (defvar gnus-exit-gnus-hook (list 'nntp-request-close)
723 "A hook called when exiting Gnus.")
725 (defvar gnus-save-newsrc-hook nil
726 "A hook called when saving the newsrc file.
727 This hook is called before saving the `.newsrc' file.")
729 (defvar gnus-auto-expirable-newsgroups nil
730 "All newsgroups that match this regexp will have all read articles automatically marked as expirable.")
732 (defvar gnus-subscribe-hierarchical-interactive nil
733 "If non-nil, Gnus will offer to subscribe hierarchically.
734 When a new hierarchy appears, Gnus will ask the user:
736 'alt.binaries': Do you want to subscribe to this hierarchy? ([d]ys):
738 If the user pressed `d', Gnus will descend the hierarchy, `y' will
739 subscribe to all newsgroups in the hierarchy and `s' will skip this
740 hierarchy in its entirety.")
742 (defvar gnus-group-line-format "%M%S%5y: %G %z\n"
743 "Format of Newsgroups lines.
744 It works along the same lines as a normal formatting string,
745 with some simple extrensions.
747 %M Only marked articles (character, \"*\" or \" \")
748 %S Whether the group is subscribed (character, \"U\", \"K\", \"Z\" or \" \")
749 %L Level of subscribedness (integer, 1-9)
750 %N Number of unread articles (integer)
751 %I Number of dormant articles (integer)
752 %i Number of ticked and dormant (integer)
753 %T Number of ticked articles (integer)
754 %R Number of read articles (integer)
755 %t Total number of articles (integer)
756 %y Number of unread, unticked articles (integer)
757 %G Group name (string)
758 %D Newsgroup description (string)
759 %s Select method (string)
760 %o Moderated group (char, \"m\")
761 %O Moderated group (string, \"(m)\" or \"\")
762 %n Select from where (string)
763 %z A string that look like `<%s:%n>' if a foreign select method is used
765 Note that this format specification is not always respected. For
766 reasons of efficiency, when listing killed groups, this specification
767 is ignored altogether. If the spec is changed considerably, your
768 output may end up looking strange when listing both alive and killed
771 If you use %o or %O, reading the active file will be slower and quite
772 a bit of extra memory will be used. %D will also worsen performance.
773 Also note that if you change the format specification to include any
774 of these specs, you must probably re-start Gnus to see them go into
777 (defvar gnus-summary-line-format "%U%R%X%i %I%[%4L: %-20,20n%] %s\n"
778 "The format specification of the lines in the Summary buffer.
779 The first specification must always be \"%U%R%X\", at least in this
782 It works along the same lines as a normal formatting string,
783 with some simple extensions.
785 %N Article number, left padded with spaces (integer)
787 %s Subject if it is at the root of a thread, and \"\" otherwise (string)
788 %n Name of the poster (string)
789 %A Address of the poster (string)
790 %L Number of lines in the article (integer)
791 %D Date of the article (string)
792 %I Indentation based on thread level (a string of spaces)
793 %T A string with two possible values: 80 spaces if the article
794 is on thread level two or larger and 0 spaces on level one
795 %C This is the current article (character, \"+\" or \" \")
796 %U Status of this article (character, \"D\", \"K\", \"-\" or \" \")
797 %[ Opening bracket (character, \"[\" or \"=\")
798 %] Closing bracket (character, \"]\" or \"=\")
799 %> Spaces of length thread-level (string)
800 %< Spaces of length (- 20 thread-level) (string)
801 %i Article interest (integer, 0-9)
804 (defconst gnus-summary-dummy-line-format "* : : %S\n"
805 "The format specification for the dummy roots in the Summary buffer.
806 It works along the same lines as a normal formatting string,
807 with some simple extensions.
811 (defvar gnus-summary-mode-line-format "(ding) %G/%A %Z"
812 "The format specification for the Summary mode line.")
814 (defvar gnus-article-mode-line-format "(ding) %G/%A %S"
815 "The format specification for the Article mode line.")
817 (defconst gnus-group-mode-line-format "(ding) List of Newsgroups {%M:%S}"
818 "The format specification for the Newsgroup mode line.")
822 ;; Site dependent variables. You have to define these variables in
823 ;; site-init.el, default.el or your .emacs.
825 (defvar gnus-local-timezone nil
827 This value is used only if `current-time-zone' does not work in your Emacs.
828 It specifies the GMT offset, i.e. a decimal integer
829 of the form +-HHMM giving the hours and minutes ahead of (i.e. east of) GMT.
830 For example, +0900 should be used in Japan, since it is 9 hours ahead of GMT.
832 For backwards compatibility, it may also be a string like \"JST\",
833 but strings are obsolescent: you should use numeric offsets instead.")
835 (defvar gnus-local-domain nil
836 "Local domain name without a host name like: \"stars.flab.Fujitsu.CO.JP\"
837 The `DOMAINNAME' environment variable is used instead if defined. If
838 the function (system-name) returns the full internet name, there is no
839 need to define the name.")
841 (defvar gnus-local-organization nil
842 "Local organization like: \"Fujitsu Laboratories Ltd., Kawasaki, Japan.\"
843 The `ORGANIZATION' environment variable is used instead if defined.")
845 (defvar gnus-use-generic-from nil
846 "If nil, prepend local host name to the defined domain in the From:
847 field; if stringp, use this; if non-nil, strip of the local host name.")
849 (defvar gnus-use-generic-path nil
850 "If nil, use the NNTP server name in the Path: field; if stringp,
851 use this; if non-nil, use no host name (user name only)")
853 (defvar gnus-valid-select-methods
854 '(("nntp" post) ("nnspool" post) ("nnvirtual" none)
855 ("nnmail" mail respool) ("nnml" mail respool)
856 ("nnmh" mail respool))
857 "A list of valid select methods.
858 Each element in this list should be a list. The first element of these
859 lists should be a string with the name of the select method. The
860 other elements may be be the category of this method (ie. `post',
861 `mail', `none' or whatever) or other properties that this method has
862 (like being respoolable).
863 If you implement a new select method, all you should have to change is
864 this variable. I think.")
866 (defvar gnus-updated-mode-lines '(group article summary)
867 "This variable is a list of buffers that should keep their mode lines updated.
868 The list may contain the symbols `group', `article' and `summary'. If
869 the corresponding symbol is present, Gnus will keep that mode line
870 updated with information that may be pertinent.
871 If this variable is nil, screen refresh may be quicker.")
873 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
874 (defvar gnus-mouse-face 'highlight
875 "Face used for mouse highlighting in Gnus.
876 No mouse highlights will be done if `gnus-visual' is nil.")
878 (defvar gnus-visual-summary-update-hook
879 (list 'gnus-visual-summary-highlight-line)
880 "A hook called when a summary line is changed.
881 The cursor will be positioned at the summary line.
883 The default hook `gnus-visual-summary-highlight-line' will highlight the line
884 according to the `gnus-visual-summary-highlight' variable.")
887 ;; Internal variables
889 ;; Avoid highlighting in kill files.
890 (defvar gnus-summary-inhibit-highlight nil)
892 (defvar caesar-translate-table nil)
894 (defvar gnus-dribble-buffer nil)
896 (defvar gnus-article-reply nil)
897 (defvar gnus-article-check-size nil)
899 (defvar gnus-newsgroup-dependencies nil)
901 (defconst gnus-group-edit-buffer "*Gnus edit newsgroup*")
903 (defvar gnus-default-subscribe-level 2
904 "Default subscription level.")
906 (defvar gnus-default-unsubscribe-level 6
907 "Default unsubscription level.")
909 (defvar gnus-default-kill-level 9
910 "Default kill level.")
912 (defconst gnus-group-line-format-alist
913 (list (list ?M 'marked ?c)
914 (list ?S 'subscribed ?c)
917 (list ?I 'number-of-dormant ?d)
918 (list ?T 'number-of-ticked ?d)
919 (list ?R 'number-of-read ?s)
920 (list ?t 'number-total ?d)
921 (list ?y 'number-of-unread-unticked ?s)
922 (list ?i 'number-of-ticked-and-dormant ?d)
924 (list ?D 'newsgroup-description ?s)
925 (list ?o 'moderated ?c)
926 (list ?O 'moderated-string ?s)
927 (list ?s 'news-server ?s)
928 (list ?n 'news-method ?s)
929 (list ?z 'news-method-string ?s)))
931 (defconst gnus-summary-line-format-alist
932 (list (list ?N 'number ?d)
933 (list ?S 'subject ?s)
934 (list ?s 'subject-or-nil ?s)
936 (list ?A 'address ?s)
938 (list ?x (macroexpand '(header-xref header)) ?s)
939 (list ?D (macroexpand '(header-date header)) ?s)
940 (list ?M (macroexpand '(header-id header)) ?s)
941 (list ?r (macroexpand '(header-references header)) ?s)
943 (list ?I 'indentation ?s)
944 (list ?T '(thread-space (if (< level 1) "" (make-string (frame-width) ? )))
946 (list ?C '(if current ?+ ? ) ?c)
947 (list ?R 'replied ?c)
948 (list ?X 'expirable ?c)
949 (list ?\[ 'opening-bracket ?c)
950 (list ?\] 'closing-bracket ?c)
951 (list ?\> '(make-string level ? ) ?s)
952 (list ?\< '(make-string (max 0 (- 20 level)) ? ) ?s)
953 (list ?i 'interest ?s)
954 (list ?U 'unread ?c))
955 "An alist of format specifications that can appear in summary lines,
956 and what variables they correspond with, along with the type of the
957 variable (string, integer, character, etc).")
959 (defconst gnus-summary-dummy-line-format-alist
960 (list (list ?S 'subject ?s)
961 (list ?N 'number ?d)))
963 (defconst gnus-summary-mode-line-format-alist
964 (list (list ?G 'group-name ?s)
965 (list ?A 'article-number ?d)
966 (list ?Z 'unread-and-unselected ?s)
967 (list ?V 'gnus-version ?s)
969 (list ?S 'subject ?s)
970 (list ?u 'unselected ?d)))
972 (defconst gnus-group-mode-line-format-alist
973 (list (list ?S 'news-server ?s)
974 (list ?M 'news-method ?s)))
976 (defvar gnus-have-read-active-file nil)
978 (defconst gnus-foreign-group-prefix "foreign.")
980 (defconst gnus-maintainer "Lars Magne Ingebrigtsen <larsi@ifi.uio.no>"
981 "The mail address of the Gnus maintainer.")
983 (defconst gnus-version "(ding) Gnus v0.10"
984 "Version numbers of this version of Gnus.")
986 (defvar gnus-info-nodes
987 '((gnus-group-mode "(gnus)Newsgroup Commands")
988 (gnus-summary-mode "(gnus)Summary Commands")
989 (gnus-article-mode "(gnus)Article Commands")
990 (gnus-kill-file-mode "(gnus)Kill File"))
991 "Assoc list of major modes and related Info nodes.")
993 (defvar gnus-group-buffer "*Newsgroup*")
994 (defvar gnus-summary-buffer "*Summary*")
995 (defvar gnus-article-buffer "*Article*")
996 (defvar gnus-digest-buffer "Gnus Digest")
997 (defvar gnus-digest-summary-buffer "Gnus Digest-summary")
999 (defvar gnus-buffer-list nil
1000 "Gnus buffers that should be killed when exiting.")
1002 (defvar gnus-variable-list
1003 '(gnus-newsrc-options
1004 gnus-newsrc-options-n-yes gnus-newsrc-options-n-no
1005 gnus-newsrc-assoc gnus-killed-list gnus-zombie-list)
1006 "Gnus variables saved in the quick startup file.")
1008 (defvar gnus-overload-functions
1009 '((news-inews gnus-inews-news "rnewspost")
1010 (caesar-region gnus-caesar-region "rnews"))
1011 "Functions overloaded by gnus.
1012 It is a list of `(original overload &optional file)'.")
1014 (defvar gnus-newsrc-options nil
1015 "Options line in the .newsrc file.")
1017 (defvar gnus-newsrc-options-n-yes nil
1018 "Regexp representing subscribed newsgroups.")
1020 (defvar gnus-newsrc-options-n-no nil
1021 "Regexp representing unsubscribed newsgroups.")
1023 (defvar gnus-newsrc-assoc nil
1024 "Assoc list of read articles.
1025 gnus-newsrc-hashtb should be kept so that both hold the same information.")
1027 (defvar gnus-newsrc-hashtb nil
1028 "Hashtable of gnus-newsrc-assoc.")
1030 (defvar gnus-killed-list nil
1031 "List of killed newsgroups.")
1033 (defvar gnus-killed-hashtb nil
1034 "Hash table equivalent of gnus-killed-list.")
1036 (defvar gnus-zombie-list nil
1037 "List of almost dead newsgroups.")
1039 (defvar gnus-description-hashtb nil
1040 "Descriptions of newsgroups (from the file 'newsgroups').")
1042 (defvar gnus-list-of-killed-groups nil
1043 "List of newsgroups that have recently been killed by the user.")
1045 (defvar gnus-xref-hashtb nil
1046 "Hash table of cross-posted articles.")
1048 (defvar gnus-active-hashtb nil
1049 "Hashtable of active articles.")
1051 (defvar gnus-moderated-list nil
1052 "List of moderated newsgroups.")
1054 (defvar gnus-current-startup-file nil
1055 "Startup file for the current host.")
1057 (defvar gnus-last-search-regexp nil
1058 "Default regexp for article search command.")
1060 (defvar gnus-last-shell-command nil
1061 "Default shell command on article.")
1063 (defvar gnus-current-select-method nil
1064 "The current method for selecting a newsgroup.")
1066 (defvar gnus-have-all-newsgroups nil)
1068 (defvar gnus-article-internal-prepare-hook nil)
1070 (defvar gnus-newsgroup-name nil)
1071 (defvar gnus-newsgroup-begin nil)
1072 (defvar gnus-newsgroup-end nil)
1073 (defvar gnus-newsgroup-last-rmail nil)
1074 (defvar gnus-newsgroup-last-mail nil)
1075 (defvar gnus-newsgroup-last-folder nil)
1076 (defvar gnus-newsgroup-last-file nil)
1077 (defvar gnus-newsgroup-auto-expire nil
1078 "If non-nil, all read articles will be marked as expirable.")
1080 (defvar gnus-newsgroup-selected-overlay nil)
1082 (defvar gnus-newsgroup-unreads nil
1083 "List of unread articles in the current newsgroup.")
1085 (defvar gnus-newsgroup-unselected nil
1086 "List of unselected unread articles in the current newsgroup.")
1088 (defvar gnus-newsgroup-marked nil
1089 "List of ticked articles in the current newsgroup (a subset of unread art).")
1091 (defvar gnus-newsgroup-killed nil
1092 "List of ranges of articles that have been through the kill process.")
1094 (defvar gnus-newsgroup-replied nil
1095 "List of articles that have been replied to in the current newsgroup.")
1097 (defvar gnus-newsgroup-expirable nil
1098 "List of articles in the current newsgroup that can be expired.")
1100 (defvar gnus-newsgroup-processable nil
1101 "List of articles in the current newsgroup that can be processed.")
1103 (defvar gnus-newsgroup-bookmarks nil
1104 "List of articles in the current newsgroup that have bookmarks.")
1106 (defvar gnus-newsgroup-dormant nil
1107 "List of dormant articles in the current newsgroup.")
1109 (defvar gnus-newsgroup-headers nil
1110 "List of article headers in the current newsgroup.")
1111 (defvar gnus-newsgroup-headers-hashtb-by-number nil)
1113 (defvar gnus-current-article nil)
1114 (defvar gnus-article-current nil)
1115 (defvar gnus-current-headers nil)
1116 (defvar gnus-have-all-headers nil "Must be either T or NIL.")
1117 (defvar gnus-last-article nil)
1118 (defvar gnus-current-kill-article nil)
1119 (defvar gnus-newsgroup-dormant-subjects nil)
1120 (defvar gnus-newsgroup-expunged-lines nil)
1122 ;; Save window configuration.
1123 (defvar gnus-winconf-kill-file nil)
1125 (defconst gnus-group-mode-map nil)
1126 (defvar gnus-summary-mode-map nil)
1127 (defvar gnus-article-mode-map nil)
1128 (defvar gnus-kill-file-mode-map nil)
1131 (defvar gnus-summary-line-format-spec nil)
1132 (defvar gnus-summary-dummy-line-format-spec nil)
1133 (defvar gnus-group-line-format-spec nil)
1134 (defvar gnus-summary-mode-line-format-spec nil)
1135 (defvar gnus-article-mode-line-format-spec nil)
1136 (defvar gnus-group-mode-line-format-spec nil)
1138 (defvar gnus-reffed-article-number nil)
1140 (defvar rmail-default-file (expand-file-name "~/XMBOX"))
1141 (defvar rmail-default-rmail-file (expand-file-name "~/XNEWS"))
1143 (defconst gnus-summary-local-variables
1144 '(gnus-newsgroup-name gnus-newsgroup-begin gnus-newsgroup-end
1145 gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
1146 gnus-newsgroup-last-folder gnus-newsgroup-last-file
1147 gnus-newsgroup-auto-expire gnus-newsgroup-unreads
1148 gnus-newsgroup-unselected gnus-newsgroup-marked
1149 gnus-newsgroup-replied gnus-newsgroup-expirable
1150 gnus-newsgroup-processable gnus-newsgroup-killed
1151 gnus-newsgroup-bookmarks gnus-newsgroup-dormant
1152 gnus-newsgroup-dormant-subjects gnus-newsgroup-expunged-lines
1153 gnus-newsgroup-headers gnus-newsgroup-headers-hashtb-by-number
1154 gnus-current-article gnus-current-headers gnus-have-all-headers
1155 gnus-last-article gnus-article-internal-prepare-hook
1156 gnus-newsgroup-selected-overlay)
1157 "Variables that are buffer-local to the Summary buffers.")
1159 (defvar gnus-mark-article-hook
1162 (or (memq gnus-current-article gnus-newsgroup-marked)
1163 (memq gnus-current-article gnus-newsgroup-dormant)
1164 (gnus-summary-mark-as-read gnus-current-article))))
1165 "A hook called when an article is selected at the first time.
1166 The hook is intended to mark an article as read (or unread)
1167 automatically when it is selected.
1169 If you'd like to tick articles instead, use the following hook:
1171 \(setq gnus-mark-article-hook
1174 (gnus-summary-tick-article gnus-current-article))))")
1176 ;; Define some autoload functions Gnus may use.
1178 (autoload 'metamail-buffer "metamail")
1179 (autoload 'Info-goto-node "info")
1181 (autoload 'timezone-make-date-arpa-standard "timezone")
1182 (autoload 'timezone-fix-time "timezone")
1183 (autoload 'timezone-make-sortable-date "timezone")
1184 (autoload 'timezone-make-time-string "timezone")
1186 (autoload 'rmail-output "rmailout"
1187 "Append this message to Unix mail file named FILE-NAME." t)
1188 (autoload 'mail-position-on-field "sendmail")
1189 (autoload 'mail-setup "sendmail")
1191 (autoload 'gnus-mail-reply-using-mhe "gnus-mh")
1192 (autoload 'gnus-mail-forward-using-mhe "gnus-mh")
1193 (autoload 'gnus-mail-other-window-using-mhe "gnus-mh")
1194 (autoload 'gnus-summary-save-in-folder "gnus-mh")
1195 (autoload 'gnus-Folder-save-name "gnus-mh")
1196 (autoload 'gnus-folder-save-name "gnus-mh")
1198 (autoload 'gnus-group-make-menu-bar "gnus-visual")
1199 (autoload 'gnus-summary-make-menu-bar "gnus-visual")
1200 (autoload 'gnus-visual-highlight-selected-summary "gnus-visual")
1201 (autoload 'gnus-visual-summary-highlight-line "gnus-visual")
1204 (put 'gnus-group-mode 'mode-class 'special)
1205 (put 'gnus-summary-mode 'mode-class 'special)
1206 (put 'gnus-article-mode 'mode-class 'special)
1208 (autoload 'gnus-uu-ctl-map "gnus-uu" nil nil 'keymap)
1211 ;; Fix by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
1212 (defun gnus-summary-position-cursor () nil)
1213 (defun gnus-group-position-cursor () nil)
1214 (fset 'gnus-summary-position-cursor 'gnus-goto-colon)
1215 (fset 'gnus-group-position-cursor 'gnus-goto-colon)
1217 (defmacro gnus-eval-in-buffer-window (buffer &rest forms)
1218 "Pop to BUFFER, evaluate FORMS, and then returns to original window."
1219 (` (let ((GnusStartBufferWindow (selected-window)))
1222 (pop-to-buffer (, buffer))
1224 (select-window GnusStartBufferWindow)))))
1226 (defun gnus-make-hashtable (&optional hashsize)
1227 "Make a hash table (default and minimum size is 255).
1228 Optional argument HASHSIZE specifies the table size."
1229 (make-vector (if hashsize
1230 (max (gnus-create-hash-size hashsize) 255)
1233 (defmacro gnus-gethash (string hashtable)
1234 "Get hash value of STRING in HASHTABLE."
1235 ;;(` (symbol-value (abbrev-symbol (, string) (, hashtable))))
1236 ;;(` (abbrev-expansion (, string) (, hashtable)))
1237 (` (symbol-value (intern-soft (, string) (, hashtable)))))
1239 (defmacro gnus-sethash (string value hashtable)
1240 "Set hash value. Arguments are STRING, VALUE, and HASHTABLE."
1241 ;; We cannot use define-abbrev since it only accepts string as value.
1242 ; (set (intern string hashtable) value))
1243 (` (set (intern (, string) (, hashtable)) (, value))))
1245 (defsubst gnus-buffer-substring (beg end)
1246 (buffer-substring (match-beginning beg) (match-end end)))
1248 (defsubst gnus-simplify-subject-re (subject)
1249 "Remove \"Re:\" from subject lines."
1250 (let ((case-fold-search t))
1251 (if (string-match "^re: *" subject)
1252 (substring subject (match-end 0))
1257 ;;; Gnus Utility Functions
1260 (defun gnus-extract-address-components (from)
1262 (if (string-match "([^)]+)" from)
1263 (setq name (substring from (1+ (match-beginning 0))
1264 (1- (match-end 0)))))
1265 (if (string-match "\\b[^@ \t<>]+@[^@ \t<>]+\\b" from)
1266 (setq address (substring from (match-beginning 0) (match-end 0))))
1267 (if (and (not name) address)
1268 (if (string-match (concat "<" address ">") from)
1269 (setq name (substring from 0 (1- (match-beginning 0))))))
1270 (list (or name from) (or address from))))
1272 (defun gnus-fetch-field (field)
1273 "Return the value of the header FIELD of current article."
1276 (gnus-narrow-to-headers)
1277 (mail-fetch-field field))))
1279 (defun gnus-goto-colon ()
1281 (search-forward ":" (save-excursion (end-of-line) (point)) t))
1283 (defun gnus-narrow-to-headers ()
1287 (if (search-forward "\n\n")
1288 (narrow-to-region 1 (1- (point))))))
1290 ;; Get a number that is suitable for hashing; bigger than MIN
1291 (defun gnus-create-hash-size (min)
1297 (defun gnus-update-format-specifications ()
1298 (setq gnus-summary-line-format-spec
1299 (gnus-parse-format gnus-summary-line-format
1300 gnus-summary-line-format-alist))
1301 (setq gnus-summary-dummy-line-format-spec
1302 (gnus-parse-format gnus-summary-dummy-line-format
1303 gnus-summary-dummy-line-format-alist))
1304 (if (and (memq 'newsgroup-description
1305 (cdr (cdr (setq gnus-group-line-format-spec
1307 gnus-group-line-format
1308 gnus-group-line-format-alist)))))
1309 (not gnus-description-hashtb))
1310 (gnus-read-descriptions-file))
1311 (setq gnus-summary-mode-line-format-spec
1312 (gnus-parse-format gnus-summary-mode-line-format
1313 gnus-summary-mode-line-format-alist))
1314 (setq gnus-article-mode-line-format-spec
1315 (gnus-parse-format gnus-article-mode-line-format
1316 gnus-summary-mode-line-format-alist))
1317 (setq gnus-group-mode-line-format-spec
1318 (gnus-parse-format gnus-group-mode-line-format
1319 gnus-group-mode-line-format-alist)))
1321 (defun gnus-format-max-width (var length)
1323 (if (> (length (setq result (eval var))) length)
1324 (format "%s" (substring result 0 length))
1325 (format "%s" result))))
1327 (defun gnus-parse-format (format spec-alist)
1328 ;; This function parses the FORMAT string with the help of the
1329 ;; SPEC-ALIST and returns a list that can be eval'ed to return the
1330 ;; string. The list will consist of the symbol `format', a format
1331 ;; specification string, and a list of forms depending on the
1334 spec flist fstring b newspec max-width elem beg)
1336 (set-buffer (get-buffer-create "*gnus work*"))
1337 (buffer-disable-undo (current-buffer))
1338 (gnus-add-current-to-buffer-list)
1342 (while (re-search-forward "%[-0-9]*\\(,[0-9]*\\)*\\(.\\)" nil t)
1343 (setq spec (string-to-char (buffer-substring (match-beginning 2)
1345 ;; First check if there are any specs that look anything like
1346 ;; "%12,12A", ie. with a "max width specification". These have
1347 ;; to be treated specially.
1348 (if (setq beg (match-beginning 1))
1351 (buffer-substring (1+ (match-beginning 1)) (match-end 1))))
1353 (setq beg (match-beginning 2)))
1354 ;; Find the specification from `spec-alist'.
1355 (if (not (setq elem (cdr (assq spec spec-alist))))
1356 (setq elem '("*" ?s)))
1357 (if (not (= max-width 0))
1359 (setq flist (cons (list 'gnus-format-max-width
1360 (car elem) max-width) flist))
1362 (setq flist (cons (car elem) flist))
1363 (setq newspec (car (cdr elem))))
1364 ;; Remove the old specification (and possibly a ",12" string).
1365 (delete-region beg (match-end 2))
1366 ;; Insert the new specification.
1369 (setq fstring (buffer-substring 1 (point-max)))
1370 (kill-buffer (current-buffer)))
1371 (cons 'format (cons fstring (nreverse flist)))))
1373 ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
1374 (defun gnus-read-init-file ()
1375 (if (and gnus-init-file
1376 (file-exists-p gnus-init-file))
1377 (load gnus-init-file nil t)))
1379 ;; Article file names when saving.
1381 (defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
1382 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1383 If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group/num.
1384 Otherwise, it is like ~/News/news/group/num."
1387 (concat (if gnus-use-long-file-name
1388 (gnus-capitalize-newsgroup newsgroup)
1389 (gnus-newsgroup-directory-form newsgroup))
1390 "/" (int-to-string (header-number headers)))
1391 (or gnus-article-save-directory "~/News"))))
1393 (string-equal (file-name-directory default)
1394 (file-name-directory last-file))
1395 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1397 (or last-file default))))
1399 (defun gnus-numeric-save-name (newsgroup headers &optional last-file)
1400 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1401 If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group/num.
1402 Otherwise, it is like ~/News/news/group/num."
1405 (concat (if gnus-use-long-file-name
1407 (gnus-newsgroup-directory-form newsgroup))
1408 "/" (int-to-string (header-number headers)))
1409 (or gnus-article-save-directory "~/News"))))
1411 (string-equal (file-name-directory default)
1412 (file-name-directory last-file))
1413 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
1415 (or last-file default))))
1417 (defun gnus-Plain-save-name (newsgroup headers &optional last-file)
1418 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1419 If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group.
1420 Otherwise, it is like ~/News/news/group/news."
1423 (if gnus-use-long-file-name
1424 (gnus-capitalize-newsgroup newsgroup)
1425 (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
1426 (or gnus-article-save-directory "~/News"))))
1428 (defun gnus-plain-save-name (newsgroup headers &optional last-file)
1429 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
1430 If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group.
1431 Otherwise, it is like ~/News/news/group/news."
1434 (if gnus-use-long-file-name
1436 (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
1437 (or gnus-article-save-directory "~/News"))))
1439 ;; For subscribing new newsgroup
1441 (defun gnus-subscribe-hierarchical-interactive (groups)
1442 (let ((groups (sort groups 'string<))
1443 prefixes prefix start rest ans group starts)
1445 (setq prefixes (list "^"))
1446 (while (and groups prefixes)
1447 (while (not (string-match (car prefixes) (car groups)))
1448 (setq prefixes (cdr prefixes)))
1449 (setq prefix (car prefixes))
1450 (setq start (1- (length prefix)))
1451 (if (and (string-match "[^\\.]\\." (car groups) start)
1454 (concat "^" (substring (car groups) 0 (match-end 0))))
1455 (string-match prefix (car (cdr groups))))
1457 (setq prefixes (cons prefix prefixes))
1458 (message "Descend hierarchy %s'? ([y]nsq): "
1459 (substring prefix 1 (1- (length prefix))))
1460 (setq ans (read-char))
1463 (string-match prefix
1464 (setq group (car groups))))
1465 (setq gnus-killed-list
1466 (cons group gnus-killed-list))
1467 (gnus-sethash group group gnus-killed-hashtb)
1468 (setq groups (cdr groups)))
1469 (setq starts (cdr starts)))
1472 (string-match prefix
1473 (setq group (car groups))))
1474 (gnus-sethash group group gnus-killed-hashtb)
1475 (funcall gnus-subscribe-newsgroup-method
1477 (setq groups (cdr groups)))
1478 (setq starts (cdr starts)))
1481 (setq group (car groups))
1482 (setq gnus-killed-list (cons group gnus-killed-list))
1483 (gnus-sethash group group gnus-killed-hashtb)
1484 (setq groups (cdr groups))))
1486 (message "Subscribe '%s'? ([n]yq)" (car groups))
1487 (setq ans (read-char))
1489 (funcall gnus-subscribe-newsgroup-method (car groups))
1490 (gnus-sethash group group gnus-killed-hashtb))
1493 (setq group (car groups))
1494 (setq gnus-killed-list (cons group gnus-killed-list))
1495 (gnus-sethash group group gnus-killed-hashtb)
1496 (setq groups (cdr groups))))
1498 (setq gnus-killed-list (cons group gnus-killed-list))
1499 (gnus-sethash group group gnus-killed-hashtb)))
1500 (setq groups (cdr groups)))))))
1502 (defun gnus-subscribe-randomly (newsgroup)
1503 "Subscribe new NEWSGROUP by making it the first newsgroup."
1504 (gnus-subscribe-newsgroup newsgroup))
1506 (defun gnus-subscribe-alphabetically (newgroup)
1507 "Subscribe new NEWSGROUP and insert it in alphabetical order."
1508 ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
1509 (let ((groups (cdr gnus-newsrc-assoc))
1511 (while (and (not before) groups)
1512 (if (string< newgroup (car (car groups)))
1513 (setq before (car (car groups)))
1514 (setq groups (cdr groups))))
1515 (gnus-subscribe-newsgroup newgroup before)))
1517 (defun gnus-subscribe-hierarchically (newgroup)
1518 "Subscribe new NEWSGROUP and insert it in hierarchical newsgroup order."
1519 ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
1521 (set-buffer (find-file-noselect gnus-current-startup-file))
1522 (let ((groupkey newgroup)
1524 (while (and (not before) groupkey)
1525 (goto-char (point-min))
1527 (concat "^\\(" (regexp-quote groupkey) ".*\\)[!:]")))
1528 (while (and (re-search-forward groupkey-re nil t)
1530 (setq before (buffer-substring
1531 (match-beginning 1) (match-end 1)))
1532 (string< before newgroup)))))
1533 ;; Remove tail of newsgroup name (eg. a.b.c -> a.b)
1535 (if (string-match "^\\(.*\\)\\.[^.]+$" groupkey)
1536 (substring groupkey (match-beginning 1) (match-end 1)))))
1537 (gnus-subscribe-newsgroup newgroup before))))
1539 (defun gnus-subscribe-interactively (newsgroup)
1540 "Subscribe new NEWSGROUP interactively.
1541 It is inserted in hierarchical newsgroup order if subscribed. If not,
1543 (if (y-or-n-p (format "Subscribe new newsgroup: %s " newsgroup))
1544 (gnus-subscribe-hierarchically newsgroup)
1545 (setq gnus-killed-list (cons newsgroup gnus-killed-list))))
1547 (defun gnus-subscribe-zombies (newsgroup)
1548 "Make new NEWSGROUP a zombie group."
1549 (setq gnus-zombie-list (cons newsgroup gnus-zombie-list)))
1551 (defun gnus-subscribe-newsgroup (newsgroup &optional next)
1552 "Subscribe new NEWSGROUP.
1553 If NEXT is non-nil, it is inserted before NEXT. Otherwise it is made
1554 the first newsgroup."
1555 ;; We subscribe the group by changing its level to 3.
1556 (gnus-group-change-level
1558 (if next (gnus-gethash next gnus-newsrc-hashtb)
1559 (gnus-gethash (car (nth 1 gnus-newsrc-assoc)) gnus-newsrc-hashtb)))
1560 (message "Subscribe newsgroup: %s" newsgroup))
1564 (defun gnus-newsgroup-directory-form (newsgroup)
1565 "Make hierarchical directory name from NEWSGROUP name."
1566 (let ((newsgroup (substring newsgroup 0)) ;Copy string.
1567 (len (length newsgroup))
1569 ;; Replace all occurrences of `.' with `/'.
1571 (if (= (aref newsgroup idx) ?.)
1572 (aset newsgroup idx ?/))
1573 (setq idx (1+ idx)))
1577 (defun gnus-make-directory (directory)
1578 "Make DIRECTORY recursively."
1579 (let ((directory (expand-file-name directory default-directory)))
1580 (or (file-exists-p directory)
1581 (gnus-make-directory-1 "" directory))
1584 (defun gnus-make-directory-1 (head tail)
1585 (cond ((string-match "^/\\([^/]+\\)" tail)
1586 ;; ange-ftp interferes with calling match-* after
1587 ;; calling file-name-as-directory.
1588 (let ((beg (match-beginning 1))
1589 (end (match-end 1)))
1590 (setq head (concat (file-name-as-directory head)
1591 (substring tail beg end)))
1592 (or (file-exists-p head)
1593 (call-process "mkdir" nil nil nil head))
1594 (gnus-make-directory-1 head (substring tail end))))
1595 ((string-equal tail "") t)
1598 (defun gnus-capitalize-newsgroup (newsgroup)
1599 "Capitalize NEWSGROUP name with treating '.' and '-' as part of words."
1600 ;; Suggested by "Jonathan I. Kamens" <jik@pit-manager.MIT.EDU>.
1601 (let ((current-syntax-table (syntax-table)))
1604 (set-syntax-table (copy-syntax-table current-syntax-table))
1605 (modify-syntax-entry ?- "w")
1606 (modify-syntax-entry ?. "w")
1607 (capitalize newsgroup))
1608 (set-syntax-table current-syntax-table))))
1612 (defun gnus-simplify-subject (subject &optional re-only)
1613 "Remove `Re:' and words in parentheses.
1614 If optional argument RE-ONLY is non-nil, strip `Re:' only."
1615 (let ((case-fold-search t)) ;Ignore case.
1616 ;; Remove `Re:' and `Re^N:'.
1617 (if (string-match "^re:[ \t]*" subject)
1618 (setq subject (substring subject (match-end 0))))
1619 ;; Remove words in parentheses from end.
1621 (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1622 (setq subject (substring subject 0 (match-beginning 0)))))
1623 ;; Return subject string.
1627 (defun gnus-add-current-to-buffer-list ()
1628 (setq gnus-buffer-list (cons (current-buffer) gnus-buffer-list)))
1630 ;; Functions accessing headers.
1631 ;; Functions are more convenient than macros in some case.
1633 (defun gnus-header-number (header)
1634 "Return article number in HEADER."
1635 (header-number header))
1637 (defun gnus-header-subject (header)
1638 "Return subject string in HEADER."
1639 (header-subject header))
1641 (defun gnus-header-from (header)
1642 "Return author string in HEADER."
1643 (header-from header))
1645 (defun gnus-header-xref (header)
1646 "Return xref string in HEADER."
1647 (header-xref header))
1649 (defun gnus-header-lines (header)
1650 "Return lines in HEADER."
1651 (header-lines header))
1653 (defun gnus-header-date (header)
1654 "Return date in HEADER."
1655 (header-date header))
1657 (defun gnus-header-id (header)
1658 "Return Id in HEADER."
1661 (defun gnus-header-references (header)
1662 "Return references in HEADER."
1663 (header-references header))
1665 (defun gnus-clear-system ()
1666 "Clear all variables and buffers."
1667 ;; Clear Gnus variables.
1668 (let ((variables gnus-variable-list))
1670 (set (car variables) nil)
1671 (setq variables (cdr variables))))
1672 ;; Clear other internal variables.
1673 (setq gnus-list-of-killed-groups nil
1674 gnus-have-read-active-file nil
1675 gnus-newsrc-assoc nil
1676 gnus-newsrc-hashtb nil
1677 gnus-killed-list nil
1678 gnus-zombie-list nil
1679 gnus-killed-hashtb nil
1680 gnus-active-hashtb nil
1681 gnus-moderated-list nil
1682 gnus-description-hashtb nil
1683 gnus-newsgroup-headers nil
1684 gnus-newsgroup-headers-hashtb-by-number nil
1685 gnus-current-select-method nil)
1686 ;; Kill the startup file.
1687 (and gnus-current-startup-file
1688 (get-file-buffer gnus-current-startup-file)
1689 (kill-buffer (get-file-buffer gnus-current-startup-file)))
1690 (setq gnus-current-startup-file nil)
1691 (gnus-dribble-clear)
1692 ;; Kill Gnus buffers.
1693 (while gnus-buffer-list
1694 (if (and (get-buffer (car gnus-buffer-list))
1695 (buffer-name (get-buffer (car gnus-buffer-list))))
1696 (kill-buffer (car gnus-buffer-list)))
1697 (setq gnus-buffer-list (cdr gnus-buffer-list))))
1699 (defun gnus-configure-windows (action &optional force)
1700 "Configure Gnus windows according to the next ACTION.
1701 The ACTION is either a symbol, such as `summary', or a
1702 configuration list such as `(1 1 2)'. If ACTION is not a list,
1703 configuration list is got from the variable gnus-window-configuration.
1704 If FORCE is non-nil, the updating will be done whether it is necessary
1707 (if (listp action) action
1708 (if (listp gnus-window-configuration)
1709 (car (cdr (assq action gnus-window-configuration)))
1710 gnus-window-configuration)))
1711 (grpwin (get-buffer-window gnus-group-buffer))
1712 (subwin (get-buffer-window gnus-summary-buffer))
1713 (artwin (get-buffer-window gnus-article-buffer))
1719 (if (and (symbolp windows) (fboundp windows))
1720 (funcall windows action)
1721 (if (and (not force)
1722 (or (null windows) ;No configuration is specified.
1723 (and (eq (null grpwin)
1724 (zerop (nth 0 windows)))
1726 (zerop (nth 1 windows)))
1728 (zerop (nth 2 windows))))))
1729 ;; No need to change window configuration.
1731 (select-window (or grpwin subwin artwin (selected-window)))
1732 ;; First of all, compute the height of each window.
1733 (cond (gnus-use-full-window
1734 ;; Take up the entire screen.
1735 (delete-other-windows)
1736 (setq height (window-height (selected-window))))
1738 (setq height (+ (if grpwin (window-height grpwin) 0)
1739 (if subwin (window-height subwin) 0)
1740 (if artwin (window-height artwin) 0)))))
1741 ;; The Newsgroup buffer exits always. So, use it to extend the
1742 ;; Group window so as to get enough window space.
1743 (switch-to-buffer gnus-group-buffer 'norecord)
1744 (and (get-buffer gnus-summary-buffer)
1745 (delete-windows-on gnus-summary-buffer))
1746 (and (get-buffer gnus-article-buffer)
1747 (delete-windows-on gnus-article-buffer))
1748 ;; Compute expected window height.
1749 (setq winsum (apply (function +) windows))
1750 (if (not (zerop (nth 0 windows)))
1751 (setq grpheight (max window-min-height
1752 (/ (* height (nth 0 windows)) winsum))))
1753 (if (not (zerop (nth 1 windows)))
1754 (setq subheight (max window-min-height
1755 (/ (* height (nth 1 windows)) winsum))))
1756 (if (not (zerop (nth 2 windows)))
1757 (setq artheight (max window-min-height
1758 (/ (* height (nth 2 windows)) winsum))))
1759 (setq height (+ grpheight subheight artheight))
1760 (enlarge-window (max 0 (- height (window-height (selected-window)))))
1761 ;; Then split the window.
1762 (and (not (zerop artheight))
1763 (or (not (zerop grpheight))
1764 (not (zerop subheight)))
1765 (split-window-vertically (+ grpheight subheight)))
1766 (and (not (zerop grpheight))
1767 (not (zerop subheight))
1768 (split-window-vertically grpheight))
1769 ;; Then select buffers in each window.
1770 (and (not (zerop grpheight))
1772 (switch-to-buffer gnus-group-buffer 'norecord)
1774 (and (not (zerop subheight))
1776 (switch-to-buffer gnus-summary-buffer 'norecord)
1778 (and (not (zerop artheight))
1780 ;; If Article buffer does not exist, it will be created
1782 (gnus-article-setup-buffer)
1783 (switch-to-buffer gnus-article-buffer 'norecord)))))
1786 (defun gnus-window-configuration-split (action)
1787 (switch-to-buffer gnus-group-buffer t)
1788 (delete-other-windows)
1789 (split-window-horizontally)
1790 (cond ((or (eq action 'newsgroup) (eq action 'summary))
1791 (if (and (get-buffer gnus-summary-buffer)
1792 (buffer-name gnus-summary-buffer))
1793 (switch-to-buffer-other-window gnus-summary-buffer)))
1794 ((eq action 'article)
1795 (switch-to-buffer gnus-summary-buffer t)
1797 (gnus-article-setup-buffer)
1798 (switch-to-buffer gnus-article-buffer t))))
1800 (defun gnus-version ()
1801 "Version numbers of this version of Gnus."
1803 (let ((methods gnus-valid-select-methods)
1806 ;; Go through all the legal select methods and add their version
1807 ;; numbers to the total version string. Only the backends that are
1808 ;; currently in use will have their message numbers taken into
1811 (setq meth (intern (concat (car (car methods)) "-version")))
1813 (stringp (symbol-value meth))
1814 (setq mess (concat mess "; " (symbol-value meth))))
1815 (setq methods (cdr methods)))
1818 (defun gnus-info-find-node ()
1819 "Find Info documentation of Gnus."
1821 ;; Enlarge info window if needed.
1822 (cond ((eq major-mode 'gnus-group-mode)
1823 (gnus-configure-windows '(1 0 0)) ;Take all windows.
1824 (pop-to-buffer gnus-group-buffer))
1825 ((eq major-mode 'gnus-summary-mode)
1826 (gnus-configure-windows '(0 1 0)) ;Take all windows.
1827 (pop-to-buffer gnus-summary-buffer)))
1828 (Info-goto-node (car (cdr (assq major-mode gnus-info-nodes)))))
1831 "Send a bug report to the Gnus maintainers."
1833 (pop-to-buffer "*Gnus Bug*")
1835 (mail-setup gnus-maintainer "[Gnus Bug Report] " nil nil nil nil)
1836 (goto-char (point-min))
1837 (search-forward mail-header-separator)
1839 (insert (format "%s\n%s\n\n" (gnus-version) (emacs-version)))
1842 (defun gnus-overload-functions (&optional overloads)
1843 "Overload functions specified by optional argument OVERLOADS.
1844 If nothing is specified, use the variable gnus-overload-functions."
1846 (overloads (or overloads gnus-overload-functions)))
1848 (setq defs (car overloads))
1849 (setq overloads (cdr overloads))
1850 ;; Load file before overloading function if necessary. Make
1851 ;; sure we cannot use `require' always.
1852 (and (not (fboundp (car defs)))
1853 (car (cdr (cdr defs)))
1854 (load (car (cdr (cdr defs))) nil 'nomessage))
1855 (fset (car defs) (car (cdr defs)))
1858 ;; List and range functions
1860 (defun gnus-last-element (list)
1861 "Return last element of LIST."
1863 (setq list (cdr list)))
1866 (defun gnus-set-difference (list1 list2)
1867 "Return a list of elements of LIST1 that do not appear in LIST2."
1868 (let ((list1 (copy-sequence list1)))
1870 (setq list1 (delq (car list2) list1))
1871 (setq list2 (cdr list2)))
1875 (defun gnus-intersection (list1 list2)
1878 (if (memq (car list2) list1)
1879 (setq result (cons (car list2) result)))
1880 (setq list2 (cdr list2)))
1884 (defun gnus-compress-sequence (numbers &optional always-list)
1885 "Convert list of numbers to a list of ranges or a single range.
1886 If ALWAYS-LIST is non-nil, this function will always release a list of
1888 (let* ((first (car numbers))
1889 (last (car numbers))
1894 (cond ((= last (car numbers)) nil) ;Omit duplicated number
1895 ((= (1+ last) (car numbers)) ;Still in sequence
1896 (setq last (car numbers)))
1897 (t ;End of one sequence
1898 (setq result (cons (cons first last) result))
1899 (setq first (car numbers))
1900 (setq last (car numbers))))
1901 (setq numbers (cdr numbers)))
1902 (if (and (not always-list) (null result))
1904 (nreverse (cons (cons first last) result))))))
1906 (defun gnus-uncompress-sequence (ranges)
1907 "Expand a list of ranges into a list of numbers.
1908 RANGES is either a single range on the form `(num . num)' or a list of
1910 (let (first last result)
1913 (if (atom (car ranges))
1915 (setq first (car ranges))
1916 (setq last (cdr ranges))
1917 (while (<= first last)
1918 (setq result (cons first result))
1919 (setq first (1+ first))))
1921 (setq first (car (car ranges)))
1922 (setq last (cdr (car ranges)))
1923 (while (<= first last)
1924 (setq result (cons first result))
1925 (setq first (1+ first)))
1926 (setq ranges (cdr ranges))))
1927 (nreverse result))))
1929 (defun gnus-add-to-range (ranges list)
1930 "Return a list of ranges that has all articles from both RANGES and LIST.
1931 Note: LIST has to be sorted over `<'."
1932 (let* ((ranges (if (and ranges (atom (car ranges))) (list ranges) ranges))
1934 range nranges first last)
1936 (gnus-compress-sequence list t)
1937 (while (and ranges list)
1938 (setq range (car ranges))
1939 (while (and list (<= (car list) (cdr range)))
1940 (setq list (cdr list)))
1941 (while (and list (= (1- (car list)) (cdr range)))
1942 (setcdr range (car list))
1943 (setq list (cdr list)))
1944 (if (and list (and (> (car list) (cdr range)) (cdr ranges)
1945 (< (car list) (car (car (cdr ranges))))))
1946 (setcdr ranges (cons (cons (car list) (car list)) (cdr ranges))))
1947 (setq ranges (cdr ranges)))
1948 (if (and list (not ranges))
1949 (setq inrange (nconc inrange (gnus-compress-sequence list t))))
1950 (setq ranges inrange)
1952 (if (and (cdr ranges) (>= (1+ (cdr (car ranges)))
1953 (car (car (cdr ranges)))))
1955 (setcdr (car ranges) (cdr (car (cdr ranges))))
1956 (setcdr ranges (cdr (cdr ranges))))
1957 (setq ranges (cdr ranges))))
1958 (if (not (cdr inrange))
1962 (defun gnus-remove-from-range (ranges list)
1963 "Return a list of ranges that has all articles from LIST removed from RANGES.
1964 Note: LIST has to be sorted over `<'."
1965 ;; !!! This function shouldn't look like this, but I've got a headache.
1966 (gnus-compress-sequence
1967 (gnus-set-difference
1968 (gnus-uncompress-sequence ranges) list)))
1970 (defun gnus-member-of-range (number ranges)
1972 (while (and ranges not-stop)
1973 (if (and (>= number (car (car ranges)))
1974 (<= number (cdr (car ranges))))
1975 (setq not-stop nil))
1976 (setq ranges (cdr ranges)))
1984 (if gnus-group-mode-map
1986 (setq gnus-group-mode-map (make-keymap))
1987 (suppress-keymap gnus-group-mode-map)
1988 (define-key gnus-group-mode-map " " 'gnus-group-read-group)
1989 (define-key gnus-group-mode-map "=" 'gnus-group-select-group)
1990 (define-key gnus-group-mode-map "j" 'gnus-group-jump-to-group)
1991 (define-key gnus-group-mode-map "n" 'gnus-group-next-unread-group)
1992 (define-key gnus-group-mode-map "p" 'gnus-group-prev-unread-group)
1993 (define-key gnus-group-mode-map [del] 'gnus-group-prev-unread-group)
1994 (define-key gnus-group-mode-map "N" 'gnus-group-next-group)
1995 (define-key gnus-group-mode-map "P" 'gnus-group-prev-group)
1996 (define-key gnus-group-mode-map "\M-n" 'gnus-group-next-unread-group-same-level)
1997 (define-key gnus-group-mode-map "\M-p" 'gnus-group-prev-unread-group-same-level)
1998 (define-key gnus-group-mode-map "\r" 'gnus-group-select-group)
1999 (define-key gnus-group-mode-map "u" 'gnus-group-unsubscribe-current-group)
2000 (define-key gnus-group-mode-map "U" 'gnus-group-unsubscribe-group)
2001 (define-key gnus-group-mode-map "c" 'gnus-group-catchup-current)
2002 (define-key gnus-group-mode-map "C" 'gnus-group-catchup-current-all)
2003 (define-key gnus-group-mode-map "l" 'gnus-group-list-groups)
2004 (define-key gnus-group-mode-map "L" 'gnus-group-list-all-groups)
2005 (define-key gnus-group-mode-map "m" 'gnus-group-mail)
2006 (define-key gnus-group-mode-map "g" 'gnus-group-get-new-news)
2007 (define-key gnus-group-mode-map "\M-g" 'gnus-group-get-new-news-this-group)
2008 (define-key gnus-group-mode-map "R" 'gnus-group-restart)
2009 (define-key gnus-group-mode-map "r" 'gnus-group-read-init-file)
2010 (define-key gnus-group-mode-map "B" 'gnus-group-browse-foreign-server)
2011 (define-key gnus-group-mode-map "b" 'gnus-group-check-bogus-groups)
2012 (define-key gnus-group-mode-map "F" 'gnus-find-new-newsgroups)
2013 (define-key gnus-group-mode-map "\C-c\C-d" 'gnus-group-describe-group)
2014 (define-key gnus-group-mode-map "\M-d" 'gnus-group-describe-all-groups)
2015 (define-key gnus-group-mode-map "\C-c\C-a" 'gnus-group-apropos)
2016 (define-key gnus-group-mode-map "\C-c\M-C-a" 'gnus-group-description-apropos)
2017 (define-key gnus-group-mode-map "a" 'gnus-group-post-news)
2018 (define-key gnus-group-mode-map "\M-a" 'gnus-group-add-newsgroup)
2019 (define-key gnus-group-mode-map "\M-e" 'gnus-group-edit-newsgroup)
2020 (define-key gnus-group-mode-map "\ek" 'gnus-group-edit-local-kill)
2021 (define-key gnus-group-mode-map "\eK" 'gnus-group-edit-global-kill)
2022 (define-key gnus-group-mode-map "\C-k" 'gnus-group-kill-group)
2023 (define-key gnus-group-mode-map "\C-y" 'gnus-group-yank-group)
2024 (define-key gnus-group-mode-map "\C-w" 'gnus-group-kill-region)
2025 (define-key gnus-group-mode-map "\M-z" 'gnus-group-kill-all-zombies)
2026 (define-key gnus-group-mode-map "\C-x\C-t" 'gnus-group-transpose-groups)
2027 (define-key gnus-group-mode-map "\C-c\C-l" 'gnus-group-list-killed)
2028 (define-key gnus-group-mode-map "\C-c\C-k" 'gnus-group-list-killed)
2029 (define-key gnus-group-mode-map "\C-c\C-z" 'gnus-group-list-zombies)
2030 (define-key gnus-group-mode-map "\C-c\C-x" 'gnus-group-expire-articles)
2031 (define-key gnus-group-mode-map "\C-c\M-\C-x" 'gnus-group-expire-all-groups)
2032 (define-key gnus-group-mode-map "V" 'gnus-version)
2033 (define-key gnus-group-mode-map "S" 'gnus-group-set-current-level)
2034 (define-key gnus-group-mode-map "s" 'gnus-group-save-newsrc)
2035 (define-key gnus-group-mode-map "z" 'gnus-group-suspend)
2036 (define-key gnus-group-mode-map "Z" 'gnus-group-clear-dribble)
2037 (define-key gnus-group-mode-map "q" 'gnus-group-exit)
2038 (define-key gnus-group-mode-map "Q" 'gnus-group-quit)
2039 (define-key gnus-group-mode-map "?" 'gnus-group-describe-briefly)
2040 (define-key gnus-group-mode-map "\C-c\C-i" 'gnus-info-find-node)
2041 (define-key gnus-group-mode-map [mouse-2] 'gnus-mouse-pick-group)
2042 (gnus-group-make-menu-bar))
2044 (defun gnus-group-mode ()
2045 "Major mode for reading news.
2046 All normal editing commands are switched off.
2047 The following commands are available:
2049 \\<gnus-group-mode-map>
2050 \\[gnus-group-read-group]\t Choose the current group
2051 \\[gnus-group-select-group]\t Select the current group without selecting the first article
2052 \\[gnus-group-jump-to-group]\t Go to some group
2053 \\[gnus-group-next-unread-group]\t Go to the next unread group
2054 \\[gnus-group-prev-unread-group]\t Go to the previous unread group
2055 \\[gnus-group-next-group]\t Go to the next group
2056 \\[gnus-group-prev-group]\t Go to the previous group
2057 \\[gnus-group-next-unread-group-same-level]\t Go to the next unread group on the same level
2058 \\[gnus-group-prev-unread-group-same-level]\t Go to the previous unread group un the same level
2059 \\[gnus-group-unsubscribe-current-group]\t (Un)subscribe to the current group
2060 \\[gnus-group-unsubscribe-group]\t (Un)subscribe to some group
2061 \\[gnus-group-catchup-current]\t Mark all unread articles in the current group as read
2062 \\[gnus-group-catchup-current-all]\t Mark all alrticles in the current group as read
2063 \\[gnus-group-list-groups]\t List groups that have unread articles
2064 \\[gnus-group-list-all-groups]\t List all groups
2065 \\[gnus-group-mail]\t Compose a mail
2066 \\[gnus-group-get-new-news]\t Look for new news
2067 \\[gnus-group-get-new-news-this-group]\t Look for new news for the current group
2068 \\[gnus-group-restart]\t Restart Gnus
2069 \\[gnus-group-save-newsrc]\t Save the startup file(s)
2070 \\[gnus-group-browse-foreign-server]\t Browse a foreign (NNTP) server
2071 \\[gnus-group-check-bogus-groups]\t Check for and delete bogus newsgroups
2072 \\[gnus-find-new-newsgroups]\t Find new newsgroups
2073 \\[gnus-group-describe-group]\t Describe the current newsgroup
2074 \\[gnus-group-describe-all-groups]\t Describe all newsgroups
2075 \\[gnus-group-post-news]\t Post an article to some newsgroup
2076 \\[gnus-group-add-newsgroup]\t Add a newsgroup entry
2077 \\[gnus-group-edit-newsgroup]\t Edit a newsgroup entry
2078 \\[gnus-group-edit-local-kill]\t Edit a local kill file
2079 \\[gnus-group-edit-global-kill]\t Edit the global kill file
2080 \\[gnus-group-kill-group]\t Kill the current newsgroup
2081 \\[gnus-group-yank-group]\t Yank a previously killed newsgroup
2082 \\[gnus-group-kill-region]\t Kill all newsgroups between point and mark
2083 \\[gnus-group-kill-all-zombies]\t Kill all zombie newsgroups
2084 \\[gnus-group-transpose-groups]\t Transpose two newsgroups
2085 \\[gnus-group-list-killed]\t List all killed newsgroups
2086 \\[gnus-group-list-zombies]\t List all zombie newsgroups
2087 \\[gnus-group-expire-articles]\t Expire the expirable articles in the current newsgroup
2088 \\[gnus-group-expire-all-groups]\t Expire expirable articles in all newsgroups
2089 \\[gnus-version]\t Display the current Gnus version
2090 \\[gnus-group-set-current-level]\t Set the level of the current newsgroup
2091 \\[gnus-group-suspend]\t Suspend Gnus
2092 \\[gnus-group-clear-dribble]\t Clear the dribble buffer
2093 \\[gnus-group-exit]\t Stop reading news
2094 \\[gnus-group-quit]\t Stop reading news without saving the startup files
2095 \\[gnus-group-describe-briefly]\t Give a brief description of the current mode
2096 \\[gnus-info-find-node]\t Find the info pages for Gnus
2099 (kill-all-local-variables)
2100 (setq mode-line-modified "--- ")
2101 (setq major-mode 'gnus-group-mode)
2102 (setq mode-name "Newsgroup")
2103 (gnus-group-set-mode-line)
2104 (setq mode-line-process nil)
2105 (use-local-map gnus-group-mode-map)
2106 (buffer-disable-undo (current-buffer))
2107 (setq truncate-lines t)
2108 (setq buffer-read-only t)
2109 (run-hooks 'gnus-group-mode-hook))
2111 (defun gnus-mouse-pick-group (e)
2114 (gnus-group-read-group nil))
2117 (defun gnus-no-server (&optional arg)
2119 If ARG is non-nil and a positive number, Gnus will use that as the
2120 startup level. If ARG is non-nil and not a positive number, Gnus will
2121 prompt the user for the name of an NNTP server to use.
2122 As opposed to `gnus', this command will not connect to the local server."
2126 (defalias '\(ding\) 'gnus)
2129 (defun gnus (&optional arg dont-connect)
2131 If ARG is non-nil and a positive number, Gnus will use that as the
2132 startup level. If ARG is non-nil and not a positive number, Gnus will
2133 prompt the user for the name of an NNTP server to use."
2136 (gnus-read-init-file)
2137 (if (and gnus-signature-file mail-signature)
2138 (setq gnus-signature-file nil))
2139 (let ((level (and arg (numberp arg) (> arg 0) arg)))
2142 (switch-to-buffer (get-buffer-create gnus-group-buffer))
2143 (gnus-add-current-to-buffer-list)
2145 (or dont-connect (gnus-start-news-server (and arg (not level)))))
2146 (if (and (not dont-connect)
2147 (not (gnus-server-opened gnus-select-method)))
2149 ;; NNTP server is successfully open.
2150 (gnus-update-format-specifications)
2151 (let ((buffer-read-only nil))
2153 (if (not gnus-inhibit-startup-message)
2155 (gnus-group-startup-message)
2157 (run-hooks 'gnus-startup-hook)
2158 (gnus-setup-news nil level)
2160 (or (not gnus-novice-user)
2162 (gnus-group-describe-briefly)) ;Show brief help message.
2163 (gnus-group-list-groups (or level 5))))))
2165 (defun gnus-group-startup-message (&optional x y)
2166 "Insert startup message in current buffer."
2167 ;; Insert the message.
2183 ;; And then hack it.
2184 ;; 18 is the longest line.
2185 (indent-rigidly (point-min) (point-max)
2186 (/ (max (- (window-width) (or x 28)) 0) 2))
2187 (goto-char (point-min))
2188 ;; +4 is fuzzy factor.
2189 (insert-char ?\n (/ (max (- (window-height) (or y 12)) 0) 2)))
2191 (defun gnus-group-list-groups (level &optional unread)
2192 "List newsgroups with level LEVEL or lower that have unread alticles.
2193 Default is 5, which lists all subscribed groups.
2194 If argument UNREAD is non-nil, groups with no unread articles are also listed."
2196 (setq level (or level 5))
2197 (let ((case-fold-search nil)
2198 (group (gnus-group-group-name)))
2199 (set-buffer gnus-group-buffer) ;May call from out of Group buffer
2200 (gnus-group-prepare level unread)
2201 (if (zerop (buffer-size))
2202 ;; Suggested by Andrew Eskilsson <pi92ae@lelle.pt.hk-r.se>.
2203 (message "No news is horrible news")
2204 (goto-char (point-min))
2207 ;; Find the right group to put point on. If the current group
2208 ;; has disapeared in the new listing, try to find the next
2209 ;; one. If no next one can be found, just leave point at the
2210 ;; first newsgroup in the buffer.
2211 (if (not (re-search-forward (gnus-group-make-regexp group) nil t))
2212 (let ((newsrc (nthcdr 3 (gnus-gethash group gnus-newsrc-hashtb))))
2214 (not (re-search-forward
2215 (gnus-group-make-regexp (car (car newsrc)))
2217 (setq newsrc (cdr newsrc))))))
2218 ;; Adjust cursor point.
2219 (gnus-group-position-cursor))))
2221 (defun gnus-group-prepare (level &optional all lowest)
2222 "List all newsgroups with unread articles of level LEVEL or lower.
2223 If ALL is non-nil, list groups that have no unread articles.
2224 If LOWEST is non-nil, list all newsgroups of level LOWEST or higher."
2225 (set-buffer (get-buffer-create gnus-group-buffer))
2226 (gnus-add-current-to-buffer-list)
2227 (let ((buffer-read-only nil)
2228 (newsrc (cdr gnus-newsrc-assoc))
2229 (zombie gnus-zombie-list)
2230 (killed gnus-killed-list)
2231 info clevel unread active group)
2236 ;; List alive newsgroups.
2238 (setq info (car newsrc)
2241 unread (car (gnus-gethash group gnus-newsrc-hashtb)))
2242 (if (and unread ; This group might be bogus
2243 (or all (eq unread t)
2246 (length (cdr (assq 'dormant (nth 3 info)))))))
2247 (and (<= (setq clevel (car (cdr info))) level))
2249 (gnus-group-insert-group-line
2250 nil group (car (cdr info)) (nth 3 info) unread
2253 ;; List zombies and killed lists somehwat faster, which was
2254 ;; suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>. It does
2255 ;; this by ignoring the group format specification altogether.
2256 (let ((lists (list 'gnus-zombie-list 'gnus-killed-list))
2259 (if (or (and (eq (car lists) 'gnus-zombie-list)
2260 (progn (setq mark ?Z)
2261 (and (>= level 8) (<= lowest 8))))
2262 (and (eq (car lists) 'gnus-killed-list)
2263 (progn (setq mark ?K)
2264 (and (>= level 9) (<= lowest 9)))))
2266 (setq newsrc (set (car lists)
2267 (sort (symbol-value (car lists))
2268 (function string<))))
2270 (setq group (car newsrc)
2271 newsrc (cdr newsrc))
2272 (insert (format " %c *: %s" mark group))
2274 (insert (format " %s %d\n" group
2275 (if (= mark ?Z) 8 9)))
2276 (set-text-properties beg (1- (point))
2278 (setq lists (cdr lists))))
2280 (gnus-group-set-mode-line)
2281 (setq gnus-have-all-newsgroups all)
2282 (run-hooks 'gnus-group-prepare-hook)))
2284 (defun gnus-group-real-name (group)
2285 "Find the real name of a foreign newsgroup."
2286 (if (string-match (concat "^" gnus-foreign-group-prefix) group)
2287 (substring group (match-end 0))
2290 (defun gnus-group-set-info (info)
2291 (let ((entry (gnus-gethash (car info) gnus-newsrc-hashtb)))
2294 (setcar (nthcdr 2 entry) info)
2295 (if (and (not (eq (car entry) t))
2296 (gnus-gethash (car info) gnus-active-hashtb))
2297 (setcar entry (length (gnus-list-of-unread-articles
2299 (error "No such group: %s" (car info)))))
2301 (defun gnus-group-update-group-line ()
2302 "This function updates the current line in the newsgroup buffer and
2303 moves the point to the colon."
2304 (let ((group (gnus-group-group-name))
2305 (buffer-read-only nil))
2306 (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
2309 (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2312 (delete-region (point) (save-excursion (forward-line 1) (point)))
2313 (gnus-group-insert-group-line-info group)
2315 (gnus-group-position-cursor)))
2317 (defun gnus-group-insert-group-line-info (group)
2318 (let ((entry (gnus-gethash group gnus-newsrc-hashtb))
2322 (setq info (nth 2 entry))
2323 (gnus-group-insert-group-line
2324 nil group (nth 1 info) (nth 3 info) (car entry) (nth 4 info)))
2325 (setq active (gnus-gethash group gnus-active-hashtb))
2326 (gnus-group-insert-group-line
2327 nil group (if (member group gnus-zombie-list) 8 9)
2328 nil (- (1+ (cdr active)) (car active)) nil))))
2330 (defun gnus-group-insert-group-line (gformat group level marked number method)
2331 (let* ((gformat (or gformat gnus-group-line-format-spec))
2332 (active (gnus-gethash group gnus-active-hashtb))
2333 (number-total (if active (1+ (- (cdr active) (car active)))))
2334 (number-of-dormant (length (cdr (assq 'dormant marked))))
2335 (number-of-ticked (length (cdr (assq 'tick marked))))
2336 (number-of-ticked-and-dormant
2337 (+ number-of-ticked number-of-dormant))
2338 (number-of-unread-unticked
2339 (if (numberp number) (- number number-of-ticked number-of-dormant)
2342 (if (numberp number)
2343 (- number-total number)
2345 (subscribed (cond ((< level 6) ? )
2349 (buffer-read-only nil)
2350 (newsgroup-description
2351 (if gnus-description-hashtb
2352 (or (gnus-gethash group gnus-description-hashtb) "")
2354 (moderated (if (member group gnus-moderated-list) ?m ? ))
2355 (moderated-string (if (eq moderated ?m) "(m)" ""))
2356 (news-server (or (car (cdr method)) ""))
2357 (news-method (or (car method) ""))
2359 (if method (format "(%s:%s)" (car method) (car (cdr method))) ""))
2360 (number (if (eq number t) "*" number))
2363 (not (zerop number))
2364 (>= (+ (length (cdr (assq 'tick marked)))
2365 (length (cdr (assq 'dormant marked)))) number)
2366 (> (length (cdr (assq 'tick marked))) 0))
2371 (let ((group (if method (gnus-group-real-name group) group)))
2372 ;; Insert the visible text.
2373 (insert-before-markers (eval gformat)))
2375 (if (and gnus-visual gnus-mouse-face)
2376 (overlay-put (make-overlay b (point)) 'mouse-face gnus-mouse-face))
2377 ;; Insert the invisible info on the end of the line.
2378 (set-text-properties
2381 ;; The info is GROUP UNREAD MARKED LEVEL.
2383 " %s%c%c%d" group (if (or (stringp number) (> number 0)) ?+ ? )
2385 (point) '(invisible t))
2388 (defun gnus-group-update-group (group &optional visible-only)
2389 "Update newsgroup info of GROUP.
2390 If VISIBLE-ONLY is non-nil, the group won't be displayed if it isn't already."
2391 (let ((buffer-read-only nil)
2392 (case-fold-search nil)
2393 (regexp (gnus-group-make-regexp group))
2395 (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
2398 (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
2400 ;; Buffer may be narrowed.
2403 ;; Search a line to modify. If the buffer is large, the search
2404 ;; takes long time. In most cases, current point is on the line
2405 ;; we are looking for. So, first of all, check current line.
2406 ;; And then if current point is in the first half, search from
2407 ;; the beginning. Otherwise, search from the end.
2410 (looking-at regexp)))
2411 ((and (> (/ (buffer-size) 2) (point)) ;In the first half.
2413 (goto-char (point-min))
2414 (re-search-forward regexp nil t))))
2416 (goto-char (point-max))
2417 (re-search-backward regexp nil t))))
2418 ;; GROUP is listed in current buffer. So, delete old line.
2422 (delete-region (point) (progn (forward-line 1) (point))))
2423 ;; No such line in the buffer, find out where it's supposed to
2424 ;; go, and insert it there (or at the end of the buffer).
2425 ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
2427 (let ((entry (cdr (gnus-gethash group gnus-newsrc-hashtb))))
2428 (goto-char (point-min))
2430 (not (re-search-forward (gnus-group-make-regexp
2431 (car (car entry))) nil t)))
2432 (setq entry (cdr entry)))
2434 (goto-char (point-max))))))
2435 (if (or visible (not visible-only))
2437 (gnus-group-insert-group-line-info group)
2438 (forward-line -1) ; Move point back to the inserted line.
2440 (gnus-group-set-mode-line))
2442 (defun gnus-group-set-mode-line ()
2443 (if (memq 'group gnus-updated-mode-lines)
2444 (let* ((gformat (or gnus-group-mode-line-format-spec
2445 (setq gnus-group-mode-line-format-spec
2447 gnus-group-mode-line-format
2448 gnus-group-mode-line-format-alist))))
2449 (news-server (car (cdr gnus-select-method)))
2450 (news-method (car gnus-select-method))
2451 (mode-string (eval gformat))
2453 (if (> (length mode-string) max-len)
2454 (setq mode-string (substring mode-string 0 (- max-len 4))))
2455 (setq mode-line-buffer-identification mode-string)
2456 (set-buffer-modified-p t))))
2458 (defun gnus-group-group-name ()
2459 "Get the name of the newsgroup on the current line."
2461 (let ((buffer-read-only nil))
2463 (if (re-search-forward " \\([^ ]*\\)...$" nil t)
2465 (set-text-properties (match-beginning 1) (match-end 1) nil)
2466 (buffer-substring (match-beginning 1) (match-end 1))
2467 (set-text-properties (match-beginning 1) (match-end 1)
2468 '(invisible t)))))))
2470 (defun gnus-group-group-level ()
2471 "Get the level of the newsgroup on the current line."
2475 (let ((c (following-char)))
2476 (if (and (>= c ?1) (<= c ?9))
2479 (defun gnus-group-make-regexp (newsgroup)
2480 "Return regexp that will match the line that NEWSGROUP is on."
2481 (concat " " (regexp-quote newsgroup) "...$"))
2483 (defun gnus-group-search-forward (&optional backward all level)
2484 "Find the next newsgroup with unread articles.
2485 If BACKWARD is non-nil, find the previous newsgroup instead.
2486 If ALL is non-nil, just find any newsgroup.
2487 If LEVEL is non-nil, find group with level LEVEL, or higher if no such
2490 (let ((regexp (if all "...$" "\\+.[1-5]$")))
2495 (re-search-backward regexp nil t))
2497 (re-search-forward regexp nil t))
2498 (gnus-group-position-cursor)))
2499 (let ((beg (point)))
2500 (while (and (< level 10)
2502 (let ((regexp (format "%s.%d$" (if all "." "\\+") level)))
2507 (re-search-backward regexp nil t))
2509 (re-search-forward regexp nil t)))))
2510 (setq level (1+ level)))
2513 ;; Gnus Group mode command
2515 (defun gnus-group-read-group (all &optional no-article)
2516 "Read news in this newsgroup.
2517 If argument ALL is non-nil, already read articles become readable.
2518 If optional argument NO-ARTICLE is non-nil, no article body is displayed."
2520 (let ((group (gnus-group-group-name))
2523 (error "No group on current line"))
2524 ;; This group might be a dead group. In that case we have to get
2525 ;; the number of unread articles from `gnus-active-hashtb'.
2526 (if (>= (gnus-group-group-level) 8)
2527 (setq number (- (1+ (cdr (setq active (gnus-gethash
2528 group gnus-active-hashtb))))
2530 (setq number (car (gnus-gethash group gnus-newsrc-hashtb))))
2531 (gnus-summary-read-group
2532 group (or all (and (numberp number) (zerop number))) no-article)))
2534 (defun gnus-group-select-group (all)
2535 "Select this newsgroup.
2536 No article is selected automatically.
2537 If argument ALL is non-nil, already read articles become readable."
2539 (gnus-group-read-group all t))
2541 (defun gnus-group-jump-to-group (group)
2542 "Jump to newsgroup GROUP."
2545 (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
2546 (let ((case-fold-search nil))
2547 (goto-char (point-min))
2548 ;; Either go to the line in the group buffer...
2549 (or (re-search-forward (gnus-group-make-regexp group) nil t)
2550 ;; ... or insert the line.
2551 (gnus-group-update-group group))
2552 ;; Adjust cursor point.
2553 (gnus-group-position-cursor)))
2555 (defun gnus-group-next-group (n)
2556 "Go to next N'th newsgroup.
2557 If N is negative, search backward instead.
2558 Returns the difference between N and the number of skips actually
2561 (gnus-group-next-unread-group n t))
2563 (defun gnus-group-next-unread-group (n &optional all level)
2564 "Go to next N'th unread newsgroup.
2565 If N is negative, search backward instead.
2566 If ALL is non-nil, choose any newsgroup, unread or not.
2567 If LEVEL is non-nil, choose the next group with level LEVEL, or, if no
2568 such group can be found, the next group with a level higher than
2570 Returns the difference between N and the number of skips actually
2573 (let ((backward (< n 0))
2576 (gnus-group-search-forward backward all level))
2578 (if (/= 0 n) (message "No more%s newsgroups%s" (if all "" " unread")
2579 (if level " on this level or higher" "")))
2582 (defun gnus-group-prev-group (n)
2583 "Go to previous N'th newsgroup.
2584 Returns the difference between N and the number of skips actually
2587 (gnus-group-next-unread-group (- n) t))
2589 (defun gnus-group-prev-unread-group (n)
2590 "Go to previous N'th unread newsgroup.
2591 Returns the difference between N and the number of skips actually
2594 (gnus-group-next-unread-group (- n)))
2596 (defun gnus-group-next-unread-group-same-level (n)
2597 "Go to next N'th unread newsgroup on the same level.
2598 If N is negative, search backward instead.
2599 Returns the difference between N and the number of skips actually
2602 (gnus-group-next-unread-group n t (gnus-group-group-level))
2603 (gnus-group-position-cursor))
2605 (defun gnus-group-prev-unread-group-same-level (n)
2606 "Go to next N'th unread newsgroup on the same level.
2607 Returns the difference between N and the number of skips actually
2610 (gnus-group-next-unread-group (- n) t (gnus-group-group-level))
2611 (gnus-group-position-cursor))
2613 (defun gnus-group-add-newsgroup (&optional name how where)
2614 "Add a new newsgroup."
2616 (let ((methods gnus-valid-select-methods)
2619 (setq name (read-string "Newsgroup name: ")))
2620 (setq nname (concat gnus-foreign-group-prefix name))
2621 (while (gnus-gethash nname gnus-newsrc-hashtb)
2622 (setq name (read-string "Name already in use. Newsgroup name: "))
2623 (setq nname (concat gnus-foreign-group-prefix name)))
2625 (setq how (completing-read (format "%s method: " name) methods nil t)))
2627 (setq where (read-string
2628 (format "Get %s by method %s from: " name how))))
2629 (gnus-group-change-level
2630 (list t nname 3 nil nil (list (intern how) where))
2631 3 9 (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)
2633 (gnus-group-insert-group-line-info nname)))
2635 (defun gnus-group-edit-newsgroup ()
2637 (let ((group (gnus-group-group-name))
2639 (if group (setq info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
2640 (error "No group on current line"))
2641 (switch-to-buffer (get-buffer-create gnus-group-edit-buffer))
2642 (gnus-add-current-to-buffer-list)
2645 (insert ";; Type `C-c C-c' after you have edited the newsgroup entry.\n\n")
2646 (insert (format "(gnus-group-set-info\n '%S)\n" info))
2647 (local-set-key "\C-c\C-c" 'gnus-group-edit-newsgroup-done)))
2649 (defun gnus-group-edit-newsgroup-done ()
2651 (set-buffer (get-buffer-create gnus-group-edit-buffer))
2652 (eval-current-buffer)
2653 (kill-buffer (current-buffer))
2654 (set-buffer gnus-group-buffer)
2655 (gnus-group-update-group (gnus-group-group-name))
2656 (gnus-group-position-cursor))
2658 (defun gnus-group-make-mail-groups (method)
2659 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
2665 (gnus-methods-using 'mail) nil t "nnmail"))))
2666 (let ((groups nnmail-split-methods)
2669 (setq group (concat gnus-foreign-group-prefix (car (car groups))))
2670 (if (not (gnus-gethash group gnus-newsrc-hashtb))
2672 (gnus-group-change-level
2673 (list t group 1 nil nil (list method ""))
2674 1 9 (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)
2676 (gnus-group-insert-group-line-info group)))
2677 (setq groups (cdr groups)))))
2679 (defun gnus-group-catchup-current (n &optional all)
2680 "Mark all articles not marked as unread in current newsgroup as read.
2681 If prefix argument N is numeric, the ARG next newsgroups will be
2682 caught up. If ALL is non-nil, marked articles will also be marked as
2683 read. Cross references (Xref: field) of articles are ignored.
2684 The difference between N and actual number of newsgroups that were
2685 caught up is returned."
2687 (if (or (not gnus-interactive-catchup) ;Without confirmation?
2691 "Do you really want to mark all articles as read? "
2692 "Mark all unread articles as read? ")))
2698 (gnus-group-catchup (gnus-group-group-name) all)
2699 (gnus-group-update-group-line)
2701 (= 0 (gnus-group-next-unread-group 1))))))
2704 (defun gnus-group-catchup-current-all (n)
2705 "Mark all articles in current newsgroup as read.
2706 Cross references (Xref: field) of articles are ignored."
2708 (gnus-group-catchup-current n 'all))
2710 (defun gnus-group-catchup (group &optional all)
2711 "Mark all articles in GROUP as read.
2712 If ALL is non-nil, all articles are marked as read.
2713 The return value is the number of articles that were marked as read,
2714 or nil if no action could be taken."
2715 (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
2718 ;; Do the updating only if the newsgroup isn't killed
2721 (setq ticked (if all nil (cdr (assq 'tick (nth 3 (nth 2 entry))))))
2722 (gnus-update-read-articles group ticked nil ticked)))
2725 (defun gnus-group-expire-articles (newsgroup)
2726 "Expire all expirable articles in the current newsgroup."
2727 (interactive (list (gnus-group-group-name)))
2728 (if (not newsgroup) (error "No current newsgroup"))
2730 (assq 'expire (nth 3 (nth 2 (gnus-gethash newsgroup
2731 gnus-newsrc-hashtb))))))
2733 (gnus-check-backend-function 'request-expire-articles newsgroup)
2735 (gnus-request-expire-articles (cdr expirable) newsgroup)))))
2737 (defun gnus-group-expire-all-groups ()
2738 "Expire all expirable articles in all newsgroups."
2740 (let ((newsrc (cdr gnus-newsrc-assoc)))
2742 (gnus-group-expire-articles (car (car newsrc)))
2743 (setq newsrc (cdr newsrc)))))
2745 (defun gnus-group-set-current-level (n)
2746 "Set the level of the current group to the numeric prefix."
2748 (setq n (or n (string-to-int
2751 '(("1") ("2") ("3") ("4") ("5") ("6") ("7") ("8") ("9"))
2753 (let ((group (gnus-group-group-name)))
2754 (if (not group) (error "No newsgroup on current line.")
2755 (if (and (numberp n) (>= n 1) (<= n 9))
2757 (gnus-group-change-level group n (gnus-group-group-level))
2758 (gnus-group-update-group-line))
2759 (error "Illegal level: %s" n)))))
2761 (defun gnus-group-unsubscribe-current-group (arg)
2762 "Toggle subscribe from/to unsubscribe current group."
2764 (let ((group (gnus-group-group-name)))
2768 (setq arg (if (<= (gnus-group-group-level) 5) 7 3)))
2769 (gnus-group-unsubscribe-group group arg)
2770 (gnus-group-next-group 1))
2771 (message "No newsgroup on current line"))))
2773 (defun gnus-group-unsubscribe-group (group &optional level)
2774 "Toggle subscribe from/to unsubscribe GROUP.
2775 New newsgroup is added to .newsrc automatically."
2777 (list (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
2778 (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
2780 ;; Toggle subscription flag.
2781 (gnus-group-change-level
2782 newsrc (if level level (if (< (nth 1 (nth 2 newsrc)) 6) 7 4)))
2783 (gnus-group-update-group group))
2784 ((and (stringp group)
2785 (gnus-gethash group gnus-active-hashtb))
2786 ;; Add new newsgroup.
2787 (gnus-group-change-level
2790 (if (member group gnus-zombie-list) 8 9)
2791 (or (and (gnus-group-group-name)
2792 (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb))
2793 (gnus-gethash (car (car gnus-newsrc-assoc))
2794 gnus-newsrc-hashtb)))
2795 (gnus-group-update-group group))
2796 (t (error "No such newsgroup: %s" group)))
2797 (gnus-group-position-cursor)))
2799 (defun gnus-group-transpose-groups (arg)
2800 "Exchange current newsgroup and previous newsgroup.
2801 With argument ARG, takes previous newsgroup and moves it past ARG newsgroup."
2803 ;; BUG: last newsgroup and the last but one cannot be transposed
2804 ;; since gnus-group-search-forward does not move forward beyond the
2805 ;; last. If we instead use forward-line, no problem, but I don't
2806 ;; want to use it for later extension.
2808 (gnus-group-search-forward t t)
2809 (gnus-group-kill-group 1)
2810 (gnus-group-search-forward nil t)
2811 (gnus-group-yank-group)
2812 (gnus-group-search-forward nil t)
2816 (defun gnus-group-kill-all-zombies ()
2817 "Kill all zombie newsgroups."
2819 (setq gnus-killed-list (nconc gnus-zombie-list gnus-killed-list))
2820 (setq gnus-zombie-list nil)
2821 (gnus-group-prepare 5)
2822 (goto-char (point-min))
2823 (gnus-group-position-cursor))
2825 (defun gnus-group-kill-region (begin end)
2826 "Kill newsgroups in current region (excluding current point).
2827 The killed newsgroups can be yanked by using \\[gnus-group-yank-group]."
2830 ;; Exclude a line where current point is on.
2844 (beginning-of-line) ;Important when LINES < 1
2845 (gnus-group-kill-group lines)))
2847 (defun gnus-group-kill-group (n)
2848 "Kill newsgroup on current line, repeated prefix argument N times.
2849 The killed newsgroups can be yanked by using \\[gnus-group-yank-group].
2850 However, only groups that were alive can be yanked; already killed
2851 groups or zombie groups can't be yanked.
2852 The return value is the name of the (last) newsgroup that was killed."
2854 (let ((buffer-read-only nil)
2856 (while (>= (setq n (1- n)) 0)
2857 (setq group (gnus-group-group-name))
2859 (signal 'end-of-buffer nil))
2860 (setq level (gnus-group-group-level))
2862 (delete-region (point)
2863 (progn (forward-line 1) (point)))
2864 (if (setq entry (gnus-gethash group gnus-newsrc-hashtb))
2865 (setq gnus-list-of-killed-groups
2866 (cons (cons (car entry) (nth 2 entry))
2867 gnus-list-of-killed-groups)))
2868 (gnus-group-change-level
2869 (if entry entry group) 9
2870 (if entry nil level)))
2873 (gnus-group-position-cursor)
2876 (defun gnus-group-yank-group (&optional arg)
2877 "Yank the last newsgroups killed with \\[gnus-group-kill-group],
2878 inserting it before the current newsgroup. The numeric ARG specifies
2879 how many newsgroups are to be yanked. The name of the (last)
2880 newsgroup yanked is returned."
2882 (if (not arg) (setq arg 1))
2883 (let (info group prev)
2884 (while (>= (setq arg (1- arg)) 0)
2885 (if (not (setq info (car gnus-list-of-killed-groups)))
2886 (error "No more newsgroups to yank"))
2887 (setq group (nth 2 info))
2888 ;; Find which newsgroup to insert this one before - search
2889 ;; backward until something suitable is found. If there are no
2890 ;; other newsgroups in this buffer, just make this newsgroup the
2892 (while (and (not (setq prev (gnus-group-group-name)))
2893 (= 0 (forward-line -1))))
2895 (setq prev (car (car gnus-newsrc-assoc))))
2896 (gnus-group-change-level
2898 (gnus-gethash prev gnus-newsrc-hashtb)
2900 (gnus-group-insert-group-line-info (nth 1 info))
2901 (setq gnus-list-of-killed-groups
2902 (cdr gnus-list-of-killed-groups)))
2904 (gnus-group-position-cursor)
2907 (defun gnus-group-list-all-groups (arg)
2908 "List all newsgroups with level ARG or lower.
2909 Default is 7, which lists all subscribed and unsubscribed groups."
2911 (setq arg (or arg 7))
2912 (gnus-group-list-groups arg t))
2914 (defun gnus-group-list-killed ()
2915 "List all killed newsgroups in the Newsgroup buffer."
2917 (gnus-group-prepare 9 t 9)
2918 (goto-char (point-min))
2919 (gnus-group-position-cursor))
2921 (defun gnus-group-list-zombies ()
2922 "List all zombie newsgroups in the Newsgroup buffer."
2924 (gnus-group-prepare 8 t 8)
2925 (goto-char (point-min))
2926 (gnus-group-position-cursor))
2928 (defun gnus-group-get-new-news (&optional arg)
2929 "Get newly arrived articles.
2930 If ARG is non-nil, it should be a number between one and nine to
2931 specify which levels you are interested in re-scanning."
2933 (if (and gnus-read-active-file (not arg))
2934 (gnus-read-active-file))
2936 (let ((gnus-read-active-file nil))
2937 (gnus-get-unread-articles arg))
2938 (gnus-get-unread-articles 7))
2939 (gnus-group-list-groups 5 gnus-have-all-newsgroups))
2941 (defun gnus-group-get-new-news-this-group (n)
2942 "Check for newly arrived news in the current group (and the N-1 next groups).
2943 The difference between N and the number of newsgroup checked is returned.
2944 If N is negative, this group and the N-1 previous groups will be checked."
2946 (let ((way (if (< n 0) -1 1))
2948 (w-p (window-start))
2952 (and (setq group (gnus-group-group-name))
2953 (gnus-activate-newsgroup
2954 group (gnus-group-real-name group))
2956 (gnus-get-unread-articles-in-group
2957 (nth 2 (gnus-gethash group gnus-newsrc-hashtb))
2958 (gnus-gethash group gnus-active-hashtb))
2959 (gnus-group-update-group-line)))
2961 (= 0 (gnus-group-next-group way)))
2963 (if (/= 0 n) (message "No more newsgroups"))
2964 ;; !!! I don't know why the buffer scrolls forward when updating
2965 ;; the first line in the Group buffer, but it does. So we set the
2966 ;; window start forcibly.
2967 (set-window-start (get-buffer-window (current-buffer)) w-p)
2970 (defun gnus-group-describe-group (&optional group)
2971 "Display a description of the current newsgroup."
2973 (let ((group (or group (gnus-group-group-name))))
2975 (message "No group on current line")
2976 (and (or gnus-description-hashtb
2977 (gnus-read-descriptions-file))
2979 (or (gnus-gethash group gnus-description-hashtb)
2980 "No description available"))))))
2982 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
2983 (defun gnus-group-describe-all-groups ()
2984 "Pop up a buffer with descriptons of all newsgroups."
2986 (if (not (or gnus-description-hashtb
2987 (gnus-read-descriptions-file)))
2988 (error "Couldn't request descriptions file"))
2989 (let ((buffer-read-only nil)
2994 (insert (format " *: %-20s %s" (symbol-name group)
2995 (symbol-value group)))
2997 (insert (format " %s 6\n" group))
2998 (set-text-properties beg (1- (point)) '(invisible t)))
2999 gnus-description-hashtb)
3000 (goto-char (point-min))
3001 (gnus-group-position-cursor)))
3003 ;; Suggested by by Daniel Quinlan <quinlan@best.com>.
3004 (defun gnus-group-apropos (regexp &optional search-description)
3005 "List all newsgroups that have names that match a regexp."
3006 (interactive "sGnus apropos (regexp): ")
3008 (obuf (current-buffer))
3010 ;; Go through all newsgroups that are known to Gnus.
3013 (and (string-match regexp (symbol-name group))
3014 (setq groups (cons (symbol-name group) groups))))
3016 ;; Go through all descriptions that are known to Gnus.
3017 (if search-description
3020 (and (string-match regexp (symbol-value group))
3021 (gnus-gethash (symbol-name group) gnus-active-hashtb)
3022 (setq groups (cons (symbol-name group) groups))))
3023 gnus-description-hashtb))
3025 (message "No groups matched \"%s\"." regexp)
3026 ;; Print out all the groups.
3028 (pop-to-buffer (get-buffer-create "*Gnus Help*"))
3029 (buffer-disable-undo (current-buffer))
3031 (setq groups (sort groups 'string<))
3033 ;; Groups may be entered twice into the list of groups.
3034 (if (not (string= (car groups) prev))
3036 (insert (setq prev (car groups)) "\n")
3037 (if (and gnus-description-hashtb
3038 (setq des (gnus-gethash (car groups)
3039 gnus-description-hashtb)))
3040 (insert " " des "\n"))))
3041 (setq groups (cdr groups)))
3043 (pop-to-buffer obuf)))
3045 (defun gnus-group-description-apropos (regexp)
3046 "List all newsgroups that have names or desccriptions that match a regexp."
3047 (interactive "sGnus description apropos (regexp): ")
3048 (if (not (or gnus-description-hashtb
3049 (gnus-read-descriptions-file)))
3050 (error "Couldn't request descriptions file"))
3051 (gnus-group-apropos regexp t))
3053 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
3054 (defun gnus-group-save-newsrc ()
3055 "Save the Gnus startup files."
3057 (gnus-save-newsrc-file))
3059 (defun gnus-group-restart (&optional arg)
3060 "Force Gnus to read the .newsrc file."
3062 (gnus-save-newsrc-file)
3063 (gnus-setup-news 'force)
3064 (gnus-group-list-groups (or arg 5) gnus-have-all-newsgroups))
3066 (defun gnus-group-read-init-file ()
3067 "Read the Gnus elisp init file."
3069 (gnus-read-init-file))
3071 (defun gnus-group-check-bogus-groups ()
3072 "Check bogus newsgroups."
3074 (gnus-check-bogus-newsgroups (not gnus-expert-user)) ;Require confirmation.
3075 (gnus-group-list-groups 5 gnus-have-all-newsgroups))
3077 (defun gnus-group-mail ()
3078 "Start composing a mail."
3082 (defun gnus-group-edit-global-kill ()
3083 "Edit a global KILL file."
3085 (setq gnus-current-kill-article nil) ;No articles selected.
3086 (gnus-kill-file-edit-file nil) ;Nil stands for global KILL file.
3088 (substitute-command-keys
3089 "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
3091 (defun gnus-group-edit-local-kill ()
3092 "Edit a local KILL file."
3094 (setq gnus-current-kill-article nil) ;No articles selected.
3095 (gnus-kill-file-edit-file (gnus-group-group-name))
3097 (substitute-command-keys
3098 "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
3100 (defun gnus-group-force-update ()
3101 "Update `.newsrc' file."
3103 (gnus-save-newsrc-file))
3105 (defun gnus-group-suspend ()
3106 "Suspend the current Gnus session.
3107 In fact, cleanup buffers except for Group Mode buffer.
3108 The hook gnus-suspend-gnus-hook is called before actually suspending."
3110 (run-hooks 'gnus-suspend-gnus-hook)
3111 ;; Kill Gnus buffers except for Group Mode buffer.
3112 (let ((group-buf (get-buffer gnus-group-buffer)))
3113 (while gnus-buffer-list
3114 (and (not (eq (car gnus-buffer-list) group-buf))
3115 (get-buffer (car gnus-buffer-list))
3116 (buffer-name (get-buffer (car gnus-buffer-list)))
3117 (kill-buffer (car gnus-buffer-list)))
3118 (setq gnus-buffer-list (cdr gnus-buffer-list)))
3119 (setq gnus-buffer-list (list group-buf))
3120 (bury-buffer group-buf)
3121 (delete-windows-on group-buf t)))
3123 (defun gnus-group-clear-dribble ()
3124 "Clear all information from the dribble buffer."
3126 (gnus-dribble-clear))
3128 (defun gnus-group-exit ()
3129 "Quit reading news after updating .newsrc.eld and .newsrc.
3130 The hook `gnus-exit-gnus-hook' is called before actually exiting."
3132 (if (or noninteractive ;For gnus-batch-kill
3133 (zerop (buffer-size)) ;No news is good news.
3134 (not (gnus-server-opened gnus-select-method)) ;NNTP connection closed
3135 (not gnus-interactive-exit) ;Without confirmation
3137 (y-or-n-p "Are you sure you want to quit reading news? "))
3139 (message "") ;Erase "Yes or No" question.
3140 (run-hooks 'gnus-exit-gnus-hook)
3141 (gnus-save-newsrc-file)
3142 (gnus-clear-system))))
3144 (defun gnus-group-quit ()
3145 "Quit reading news without updating .newsrc.eld or .newsrc.
3146 The hook `gnus-exit-gnus-hook' is called before actually exiting."
3148 (if (or noninteractive ;For gnus-batch-kill
3149 (zerop (buffer-size))
3150 (not (gnus-server-opened gnus-select-method))
3153 (format "Quit reading news without saving %s? "
3154 (file-name-nondirectory gnus-current-startup-file))))
3156 (message "") ;Erase "Yes or No" question.
3157 (run-hooks 'gnus-exit-gnus-hook)
3159 (gnus-clear-system))))
3161 (defun gnus-group-describe-briefly ()
3162 "Give a one line description of the Group mode commands."
3165 (substitute-command-keys "\\[gnus-group-read-group]:Select \\[gnus-group-next-unread-group]:Forward \\[gnus-group-prev-unread-group]:Backward \\[gnus-group-exit]:Exit \\[gnus-info-find-node]:Run Info \\[gnus-group-describe-briefly]:This help")))
3167 (defun gnus-group-browse-foreign-server (method)
3168 "Browse a foreign news server.
3169 If called interactively, this function will ask for a select method
3170 (nntp, nnspool, etc.) and a server address (eg. nntp.some.where).
3171 If not, METHOD should be a list where the first element is the method
3172 and the second element is the address."
3174 (list (list (completing-read "Select method: "
3175 gnus-valid-select-methods
3177 ;; Suggested by mapjph@bath.ac.uk.
3180 (mapcar (lambda (server) (list server))
3181 gnus-secondary-servers)))))
3182 (gnus-browse-foreign-server method))
3186 ;;; Browse Server Mode
3189 (defvar gnus-browse-server-mode-hook nil)
3190 (defvar gnus-browse-server-mode-map nil)
3192 (if gnus-browse-server-mode-map
3194 (setq gnus-browse-server-mode-map (make-keymap))
3195 (suppress-keymap gnus-browse-server-mode-map)
3196 (define-key gnus-browse-server-mode-map " " 'gnus-browse-read-group)
3197 (define-key gnus-browse-server-mode-map "=" 'gnus-browse-read-group)
3198 (define-key gnus-browse-server-mode-map "n" 'gnus-group-next-group)
3199 (define-key gnus-browse-server-mode-map "p" 'gnus-group-prev-group)
3200 (define-key gnus-browse-server-mode-map [del] 'gnus-group-prev-group)
3201 (define-key gnus-browse-server-mode-map "N" 'gnus-group-next-group)
3202 (define-key gnus-browse-server-mode-map "P" 'gnus-group-prev-group)
3203 (define-key gnus-browse-server-mode-map "\M-n" 'gnus-group-next-group)
3204 (define-key gnus-browse-server-mode-map "\M-p" 'gnus-group-prev-group)
3205 (define-key gnus-browse-server-mode-map [down] 'gnus-group-next-group)
3206 (define-key gnus-browse-server-mode-map [up] 'gnus-group-prev-group)
3207 (define-key gnus-browse-server-mode-map "\r" 'gnus-group-next-group)
3208 (define-key gnus-browse-server-mode-map "u" 'gnus-browse-unsubscribe-current-group)
3209 (define-key gnus-browse-server-mode-map "q" 'gnus-browse-exit)
3210 (define-key gnus-browse-server-mode-map "Q" 'gnus-browse-exit)
3211 (define-key gnus-browse-server-mode-map "\C-c\C-c" 'gnus-browse-quit)
3212 (define-key gnus-browse-server-mode-map "?" 'gnus-browse-describe-briefly)
3213 (define-key gnus-browse-server-mode-map "\C-c\C-i" 'gnus-info-find-node)
3216 (defvar gnus-browse-current-method nil)
3218 (defun gnus-browse-foreign-server (method)
3219 (setq gnus-browse-current-method method)
3220 (let ((gnus-select-method method)
3222 (message "Connecting to %s..." (nth 1 method))
3223 (if (not (gnus-request-list method))
3224 (error "Unable to contact server: " (gnus-status-message method)))
3225 (set-buffer (get-buffer-create "*Gnus Browse Server*"))
3226 (gnus-add-current-to-buffer-list)
3227 (buffer-disable-undo (current-buffer))
3228 (let ((buffer-read-only nil))
3230 (gnus-browse-server-mode)
3231 (setq mode-line-buffer-identification
3233 "(ding) Browse Server {%s:%s}" (car method) (car (cdr method))))
3235 (set-buffer nntp-server-buffer)
3236 (let ((cur (current-buffer)))
3238 (while (re-search-forward
3239 "\\(^[^ \t]+\\)[ \t]+[0-9]+[ \t]+[0-9]+" nil t)
3240 (goto-char (match-end 1))
3241 (setq groups (cons (cons (buffer-substring (match-beginning 1)
3243 (- (read cur) (read cur)))
3245 (setq groups (sort groups
3247 (string< (car l1) (car l2)))))
3248 (let ((buffer-read-only nil))
3250 (setq group (car groups))
3252 (format "K%7d: %s\n" (cdr group) (car group)))
3253 (setq groups (cdr groups))))
3254 (switch-to-buffer (current-buffer))
3256 (gnus-group-position-cursor)))
3258 (defun gnus-browse-server-mode ()
3259 "Major mode for reading network news."
3261 (kill-all-local-variables)
3262 (setq mode-line-modified "--- ")
3263 (setq major-mode 'gnus-browse-server-mode)
3264 (setq mode-name "Browse Server")
3265 (setq mode-line-process nil)
3266 (use-local-map gnus-browse-server-mode-map)
3267 (buffer-disable-undo (current-buffer))
3268 (setq truncate-lines t)
3269 (setq buffer-read-only t)
3270 (run-hooks 'gnus-browse-server-mode-hook))
3272 (defun gnus-browse-read-group ()
3273 "Not implemented, and will probably never be."
3275 (error "You can't read while browsing"))
3277 (defun gnus-browse-unsubscribe-current-group (arg)
3278 "(Un)subscribe to the next ARG groups."
3280 (let ((ward (if (< arg 0) -1 1))
3282 (while (and (> arg 0)
3283 (gnus-browse-unsubscribe-group)
3284 (= (gnus-group-next-group ward) 0))
3285 (setq arg (1- arg)))
3286 (gnus-group-position-cursor)
3287 (if (/= 0 arg) (message "No more newsgroups" ))
3290 (defun gnus-browse-unsubscribe-group ()
3292 (buffer-read-only nil)
3296 (if (= (following-char) ?K) (setq sub t))
3297 (re-search-forward ": \\(.*\\)$" nil t)
3299 (concat gnus-foreign-group-prefix
3300 (buffer-substring (match-beginning 1) (match-end 1))))
3305 (gnus-group-change-level
3306 (list t group 3 nil nil gnus-browse-current-method) 3 9
3307 (gnus-gethash (car (nth 1 gnus-newsrc-assoc)) gnus-newsrc-hashtb)
3310 (gnus-group-change-level group 9 3)
3314 (defun gnus-browse-exit ()
3315 "Quit browsing and return to the Newsgroup buffer."
3317 (if (eq major-mode 'gnus-browse-server-mode)
3318 (kill-buffer (current-buffer)))
3319 (switch-to-buffer gnus-group-buffer)
3320 (gnus-group-list-groups 5))
3322 (defun gnus-browse-describe-briefly ()
3323 "Give a one line description of the Group mode commands."
3326 (substitute-command-keys "\\[gnus-group-next-group]:Forward \\[gnus-group-prev-group]:Backward \\[gnus-browse-exit]:Exit \\[gnus-info-find-node]:Run Info \\[gnus-browse-describe-briefly]:This help")))
3330 ;;; Gnus Summary Mode
3333 (defmacro gnus-summary-add (key func)
3334 (` (define-key gnus-summary-mode-map (, key) (, func))))
3336 (defvar gnus-summary-raise-map nil)
3337 (define-prefix-command 'gnus-summary-raise-map)
3339 (defvar gnus-summary-lower-map nil)
3340 (define-prefix-command 'gnus-summary-lower-map)
3342 (if gnus-summary-mode-map
3344 (setq gnus-summary-mode-map (make-keymap))
3345 (suppress-keymap gnus-summary-mode-map)
3346 (gnus-summary-add "\C-c\C-i" gnus-summary-raise-map)
3347 (gnus-summary-add "\C-c\C-k" gnus-summary-lower-map)
3348 (gnus-summary-add "\C-c\C-v" 'gnus-uu-ctl-map)
3349 (gnus-summary-add "@" 'gnus-summary-kill-below)
3350 (gnus-summary-add "\C-c " 'gnus-summary-clear-above)
3351 (gnus-summary-add "\C-c-" 'gnus-summary-tick-above)
3352 (gnus-summary-add "#" 'gnus-summary-mark-as-processable)
3353 (gnus-summary-add "\M-#" 'gnus-summary-unmark-as-processable)
3354 (gnus-summary-add "\C-c\M-#" 'gnus-summary-unmark-all-processable)
3355 (gnus-summary-add " " 'gnus-summary-next-page)
3356 (gnus-summary-add "\177" 'gnus-summary-prev-page)
3357 (gnus-summary-add "\r" 'gnus-summary-scroll-up)
3358 (gnus-summary-add "n" 'gnus-summary-next-unread-article)
3359 (gnus-summary-add "p" 'gnus-summary-prev-unread-article)
3360 (gnus-summary-add "N" 'gnus-summary-next-article)
3361 (gnus-summary-add "P" 'gnus-summary-prev-article)
3362 (gnus-summary-add "\M-\C-n" 'gnus-summary-next-same-subject)
3363 (gnus-summary-add "\M-\C-p" 'gnus-summary-prev-same-subject)
3364 (gnus-summary-add "\C-c\C-n" 'gnus-summary-next-digest)
3365 (gnus-summary-add "\C-c\C-p" 'gnus-summary-prev-digest)
3366 (gnus-summary-add "\M-n" 'gnus-summary-next-unread-subject)
3367 (gnus-summary-add "\M-p" 'gnus-summary-prev-unread-subject)
3368 (gnus-summary-add "." 'gnus-summary-first-unread-article)
3369 (gnus-summary-add "s" 'gnus-summary-isearch-article)
3370 (gnus-summary-add "\M-s" 'gnus-summary-search-article-forward)
3371 (gnus-summary-add "\M-r" 'gnus-summary-search-article-backward)
3372 (gnus-summary-add "<" 'gnus-summary-beginning-of-article)
3373 (gnus-summary-add ">" 'gnus-summary-end-of-article)
3374 (gnus-summary-add "j" 'gnus-summary-goto-subject)
3375 (gnus-summary-add "l" 'gnus-summary-goto-last-article)
3376 (gnus-summary-add "^" 'gnus-summary-refer-parent-article)
3377 (gnus-summary-add "\M-^" 'gnus-summary-refer-article)
3378 (gnus-summary-add "u" 'gnus-summary-tick-article-forward)
3379 (gnus-summary-add "-" 'gnus-summary-tick-article-forward)
3380 (gnus-summary-add "U" 'gnus-summary-tick-article-backward)
3381 (gnus-summary-add "d" 'gnus-summary-mark-as-read-forward)
3382 (gnus-summary-add "D" 'gnus-summary-mark-as-read-backward)
3383 (gnus-summary-add "\M-u" 'gnus-summary-clear-mark-forward)
3384 (gnus-summary-add "\M-U" 'gnus-summary-clear-mark-backward)
3385 (gnus-summary-add "k" 'gnus-summary-kill-same-subject-and-select)
3386 (gnus-summary-add "\C-k" 'gnus-summary-kill-same-subject)
3387 (gnus-summary-add "\M-\C-t" 'gnus-summary-toggle-threads)
3388 (gnus-summary-add "\M-\C-s" 'gnus-summary-show-thread)
3389 (gnus-summary-add "\M-\C-h" 'gnus-summary-hide-thread)
3390 (gnus-summary-add "\M-\C-f" 'gnus-summary-next-thread)
3391 (gnus-summary-add "\M-\C-b" 'gnus-summary-prev-thread)
3392 (gnus-summary-add "\M-\C-u" 'gnus-summary-up-thread)
3393 (gnus-summary-add "\M-\C-d" 'gnus-summary-down-thread)
3394 (gnus-summary-add "\M-\C-k" 'gnus-summary-kill-thread)
3395 (gnus-summary-add "&" 'gnus-summary-execute-command)
3396 (gnus-summary-add "c" 'gnus-summary-catchup-and-exit)
3397 (gnus-summary-add "\C-t" 'gnus-summary-toggle-truncation)
3398 (gnus-summary-add "\M-d" 'gnus-summary-delete-marked-as-read)
3399 (gnus-summary-add "\C-c\M-\C-d" 'gnus-summary-delete-marked-with)
3400 (gnus-summary-add "x" 'gnus-summary-mark-as-expirable)
3401 (gnus-summary-add "X" 'gnus-summary-unmark-as-expirable)
3402 (gnus-summary-add "\M-\C-x" 'gnus-summary-expire-articles)
3403 (gnus-summary-add [M-DEL] 'gnus-summary-delete-article)
3404 (gnus-summary-add "b" 'gnus-summary-set-bookmark)
3405 (gnus-summary-add "B" 'gnus-summary-remove-bookmark)
3406 (gnus-summary-add "+" 'gnus-summary-mark-as-dormant)
3407 (gnus-summary-add "\M-+" 'gnus-summary-show-all-dormant)
3408 (gnus-summary-add "\C-c\M-\C-s" 'gnus-summary-show-all-expunged)
3409 (gnus-summary-add "\C-c\C-sn" 'gnus-summary-sort-by-number)
3410 (gnus-summary-add "\C-c\C-sa" 'gnus-summary-sort-by-author)
3411 (gnus-summary-add "\C-c\C-ss" 'gnus-summary-sort-by-subject)
3412 (gnus-summary-add "\C-c\C-sd" 'gnus-summary-sort-by-date)
3413 (gnus-summary-add "\C-c\C-s\C-n" 'gnus-summary-sort-by-number)
3414 (gnus-summary-add "\C-c\C-s\C-a" 'gnus-summary-sort-by-author)
3415 (gnus-summary-add "\C-c\C-s\C-s" 'gnus-summary-sort-by-subject)
3416 (gnus-summary-add "\C-c\C-s\C-d" 'gnus-summary-sort-by-date)
3417 (gnus-summary-add "=" 'gnus-summary-expand-window)
3418 (gnus-summary-add "\C-x\C-s" 'gnus-summary-reselect-current-group)
3419 (gnus-summary-add "\M-g" 'gnus-summary-rescan-group)
3420 (gnus-summary-add "w" 'gnus-summary-stop-page-breaking)
3421 (gnus-summary-add "\C-c\C-r" 'gnus-summary-caesar-message)
3422 (gnus-summary-add "g" 'gnus-summary-show-article)
3423 (gnus-summary-add "t" 'gnus-summary-toggle-header)
3424 (gnus-summary-add "\M-t" 'gnus-summary-toggle-mime)
3425 (gnus-summary-add "\C-d" 'gnus-summary-rmail-digest)
3426 (gnus-summary-add "a" 'gnus-summary-post-news)
3427 (gnus-summary-add "f" 'gnus-summary-followup)
3428 (gnus-summary-add "F" 'gnus-summary-followup-with-original)
3429 (gnus-summary-add "C" 'gnus-summary-cancel-article)
3430 (gnus-summary-add "S" 'gnus-summary-supersede-article)
3431 (gnus-summary-add "r" 'gnus-summary-reply)
3432 (gnus-summary-add "R" 'gnus-summary-reply-with-original)
3433 (gnus-summary-add "\C-c\C-f" 'gnus-summary-mail-forward)
3434 (gnus-summary-add "m" 'gnus-summary-mail-other-window)
3435 (gnus-summary-add "o" 'gnus-summary-save-article)
3436 (gnus-summary-add "\C-o" 'gnus-summary-save-article-rmail)
3437 (gnus-summary-add "|" 'gnus-summary-pipe-output)
3438 (gnus-summary-add "\M-m" 'gnus-summary-move-article)
3439 (gnus-summary-add "\M-\C-m" 'gnus-summary-respool-article)
3440 (gnus-summary-add "\M-k" 'gnus-summary-edit-local-kill)
3441 (gnus-summary-add "\M-K" 'gnus-summary-edit-global-kill)
3442 (gnus-summary-add "V" 'gnus-version)
3443 (gnus-summary-add "\C-c\C-d" 'gnus-summary-describe-group)
3444 (gnus-summary-add "q" 'gnus-summary-exit)
3445 (gnus-summary-add "Q" 'gnus-summary-quit)
3446 (gnus-summary-add "?" 'gnus-summary-describe-briefly)
3447 ;;(gnus-summary-add "\C-c\C-i" 'gnus-info-find-node)
3448 (gnus-summary-add [mouse-2] 'gnus-mouse-pick-article)
3449 (gnus-summary-add "\C-c\C-x" 'gnus-kill-file-set-expunge-below)
3450 (gnus-summary-add "\C-c\C-m" 'gnus-kill-file-set-mark-below)
3451 (define-key gnus-summary-raise-map "\C-s"
3452 'gnus-summary-temporarily-raise-by-subject)
3453 (define-key gnus-summary-raise-map "\C-a"
3454 'gnus-summary-temporarily-raise-by-author)
3455 (define-key gnus-summary-raise-map "\C-t"
3456 'gnus-summary-temporarily-raise-by-thread)
3457 (define-key gnus-summary-raise-map "\C-x"
3458 'gnus-summary-temporarily-raise-by-xref)
3459 (define-key gnus-summary-raise-map "s" 'gnus-summary-raise-by-subject)
3460 (define-key gnus-summary-raise-map "a" 'gnus-summary-raise-by-author)
3461 (define-key gnus-summary-raise-map "x" 'gnus-summary-raise-by-xref)
3462 (define-key gnus-summary-raise-map "f" 'gnus-summary-raise-followups-to-author)
3463 (define-key gnus-summary-lower-map "\C-s" 'gnus-summary-temporarily-lower-by-subject)
3464 (define-key gnus-summary-lower-map "\C-a" 'gnus-summary-temporarily-lower-by-author)
3465 (define-key gnus-summary-lower-map "\C-t" 'gnus-summary-temporarily-lower-by-thread)
3466 (define-key gnus-summary-lower-map "\C-x" 'gnus-summary-temporarily-lower-by-xref)
3467 (define-key gnus-summary-lower-map "s" 'gnus-summary-lower-by-subject)
3468 (define-key gnus-summary-lower-map "a" 'gnus-summary-lower-by-author)
3469 (define-key gnus-summary-lower-map "x" 'gnus-summary-lower-by-xref)
3470 (define-key gnus-summary-lower-map "f" 'gnus-summary-lower-followups-to-author)
3471 (gnus-summary-add "(" 'gnus-summary-lower-interest)
3472 (gnus-summary-add ")" 'gnus-summary-raise-interest)
3473 (gnus-summary-add "I" 'gnus-summary-set-interest)
3474 (gnus-summary-make-menu-bar))
3477 (defun gnus-summary-mode ()
3478 "Major mode for reading articles in this newsgroup.
3479 All normal editing commands are switched off.
3480 The following commands are available:
3482 \\<gnus-summary-mode-map>
3483 \\[gnus-summary-next-page]\t Scroll the article buffer a page forwards
3484 \\[gnus-summary-prev-page]\t Scroll the article buffer a page backwards
3485 \\[gnus-summary-scroll-up]\t Scroll the article buffer one line forwards
3486 \\[gnus-summary-next-unread-article]\t Go to the next unread article
3487 \\[gnus-summary-prev-unread-article]\t Go to the previous unread article
3488 \\[gnus-summary-next-article]\t Go to the next article
3489 \\[gnus-summary-prev-article]\t Go to the previous article
3490 \\[gnus-summary-next-same-subject]\t Go to the next summary line with the same subject
3491 \\[gnus-summary-prev-same-subject]\t Go to the previous summary line with the same subject
3492 \\[gnus-summary-next-digest]\t Go to the next digest
3493 \\[gnus-summary-prev-digest]\t Go to the previous digest
3494 \\[gnus-summary-next-subject]\t Go to the next summary line
3495 \\[gnus-summary-prev-subject]\t Go to the previous summary line
3496 \\[gnus-summary-next-unread-subject]\t Go to the next unread summary line
3497 \\[gnus-summary-prev-unread-subject]\t Go to the previous unread summary line
3498 \\[gnus-summary-first-unread-article]\t Go to the first unread article
3499 \\[gnus-summary-goto-subject]\t Go to some subject
3500 \\[gnus-summary-goto-last-article]\t Go to the previous article
3502 \\[gnus-summary-beginning-of-article]\t Go to the beginning of the article
3503 \\[gnus-summary-end-of-article]\t Go to the end of the article
3505 \\[gnus-summary-refer-parent-article]\t Get the parent of the current article from the server
3506 \\[gnus-summary-refer-article]\t Request some article by Message-ID from the server
3508 \\[gnus-summary-isearch-article]\t Do an interactive search on the current article
3509 \\[gnus-summary-search-article-forward]\t Search all articles forward for a regular expression
3510 \\[gnus-summary-search-article-backward]\t Search all articles backward for a regular expression
3512 \\[gnus-summary-tick-article-forward]\t Tick current article and move forward
3513 \\[gnus-summary-tick-article-backward]\t Tick current article and move backward
3514 \\[gnus-summary-mark-as-read-forward]\t Mark the current article as read and move forward
3515 \\[gnus-summary-mark-as-read-backward]\t Mark the current article as read and move backward
3516 \\[gnus-summary-clear-mark-forward]\t Clear tick and read marks and move forward
3517 \\[gnus-summary-clear-mark-backward]\t Clear tick and read marks and move backward
3518 \\[gnus-summary-mark-as-processable]\t Set the process mark on the current article
3519 \\[gnus-summary-unmark-as-processable]\t Remove the process mark from the current article
3520 \\[gnus-summary-unmark-all-processable]\t Remove the process mark from all articles
3522 \\[gnus-summary-kill-same-subject-and-select]\t Kill all articles with the current subject and select the next article
3523 \\[gnus-summary-kill-same-subject]\t Kill all articles with the current subject
3525 \\[gnus-summary-toggle-threads]\t Toggle thread display
3526 \\[gnus-summary-show-thread]\t Show the current thread
3527 \\[gnus-summary-hide-thread]\t Hide the current thread
3528 \\[gnus-summary-next-thread]\t Go to the next thread
3529 \\[gnus-summary-prev-thread]\t Go to the previous thread
3530 \\[gnus-summary-up-thread]\t Go up the current thread
3531 \\[gnus-summary-down-thread]\t Descend the current thread
3532 \\[gnus-summary-kill-thread]\t Kill the current thread
3533 \\[gnus-summary-mark-as-expirable]\t Mark the current artivles as expirable
3534 \\[gnus-summary-unmark-as-expirable]\t Remove the expirable mark from the current article
3535 \\[gnus-summary-delete-marked-as-read]\t Delete all articles that are marked as read
3536 \\[gnus-summary-delete-marked-with]\t Delete all articles that have some mark
3538 \\[gnus-summary-execute-command]\t Execute a command
3539 \\[gnus-summary-catchup-and-exit]\t Mark all unread articles as read and exit
3540 \\[gnus-summary-toggle-truncation]\t Toggle truncation of summary lines
3541 \\[gnus-summary-expand-window]\t Expand the summary window
3543 \\[gnus-summary-sort-by-number]\t Sort the Summary buffer by article number
3544 \\[gnus-summary-sort-by-author]\t Sort the Summary buffer by author
3545 \\[gnus-summary-sort-by-subject]\t Sort the Summary buffer by subject
3546 \\[gnus-summary-sort-by-date]\t Sort the Summary buffer by date
3548 \\[gnus-summary-reselect-current-group]\t Exit and reselect the current group
3549 \\[gnus-summary-rescan-group]\t Exit, get new articles and reselect the group
3550 \\[gnus-summary-stop-page-breaking]\t Stop page breaking of the current article
3551 \\[gnus-summary-caesar-message]\t Caesar rotate (rot13) the current article
3552 \\[gnus-summary-show-article]\t Reselect the current article
3553 \\[gnus-summary-toggle-header]\t Toggle header display
3554 \\[gnus-summary-toggle-mime]\t Toggle whether to use MIME
3555 \\[gnus-summary-rmail-digest]\t Use rmail digest
3556 \\[gnus-summary-post-news]\t Post an article to the current group
3557 \\[gnus-summary-followup]\t Post a followup to the current article
3558 \\[gnus-summary-followup-with-original]\t Post a followup and include the original article
3559 \\[gnus-summary-cancel-article]\t Cancel the current article
3560 \\[gnus-summary-supersede-article]\t Supersede the current article
3561 \\[gnus-summary-reply]\t Mail a reply to the author of the current article
3562 \\[gnus-summary-reply-with-original]\t Mail a reply and include the current article
3563 \\[gnus-summary-mail-forward]\t Forward the current article
3564 \\[gnus-summary-mail-other-window]\t Mail in the other window
3565 \\[gnus-summary-save-article]\t Save the current article
3566 \\[gnus-summary-save-article-rmail]\t Save the current article in rmail format
3567 \\[gnus-summary-pipe-output]\t Pipe the current article to a process
3568 \\[gnus-summary-move-article]\t Move the article to a different newsgroup
3569 \\[gnus-summary-respool-article]\t Respool the article
3570 \\[gnus-summary-edit-local-kill]\t Edit the local kill file
3571 \\[gnus-summary-edit-global-kill]\t Edit the global kill file
3572 \\[gnus-version]\t Display the current Gnus version
3573 \\[gnus-summary-exit]\t Exit the Summary buffer
3574 \\[gnus-summary-quit]\t Exit the Summary buffer without saving any changes
3575 \\[gnus-summary-describe-group]\t Describe the current newsgroup
3576 \\[gnus-summary-describe-briefly]\t Give a brief key overview
3577 \\[gnus-info-find-node]\t Go to the Gnus info node
3579 \\[gnus-kill-file-set-expunge-below] Automatically expunge articles below LEVEL.
3581 \\[gnus-kill-file-set-mark-below] Automatically mark articles below LEVEL.
3582 \\[gnus-summary-temporarily-raise-by-subject]\t Temporarily raise score for articles with the current subject
3583 \\[gnus-summary-temporarily-raise-by-author]\t Temporarily raise score for articles from the current author
3584 \\[gnus-summary-temporarily-raise-by-xref]\t Temporarily raise score for articles with the current cross-posting
3585 \\[gnus-summary-raise-by-subject]\t Permanently raise score for articles with the current subject
3586 \\[gnus-summary-raise-by-author]\t Permanently raise score for articles from the current author
3587 \\[gnus-summary-raise-followups-to-author]\t Permanently raise score for followups to the current author
3588 \\[gnus-summary-raise-by-xref]\t Permanently raise score for articles with the current cross-posting
3589 \\[gnus-summary-temporarily-lower-by-subject]\t Temporarily lower score for articles with the current subject
3590 \\[gnus-summary-temporarily-lower-by-author]\t Temporarily lower score for articles from the current author
3591 \\[gnus-summary-temporarily-lower-by-xref]\t Temporarily lower score for articles with the current cross-posting
3592 \\[gnus-summary-lower-by-subject]\t Permanently lower score for articles with the current subject
3593 \\[gnus-summary-lower-by-author]\t Permanently lower score for articles from the current author
3594 \\[gnus-summary-lower-followups-to-author]\t Permanently lower score for followups to the current author
3595 \\[gnus-summary-lower-by-thread]\t Permanently lower score for articles in the current thread
3596 \\[gnus-summary-lower-by-xref]\t Permanently lower score for articles with the current cross-posting
3599 (kill-all-local-variables)
3600 (let ((locals gnus-summary-local-variables))
3602 (make-local-variable (car locals))
3603 (set (car locals) nil)
3604 (setq locals (cdr locals))))
3605 (gnus-update-format-specifications)
3606 (setq mode-line-modified "--- ")
3607 (setq major-mode 'gnus-summary-mode)
3608 (setq mode-name "Summary")
3609 (make-local-variable 'minor-mode-alist)
3610 (or (assq 'gnus-show-threads minor-mode-alist)
3611 (setq minor-mode-alist
3612 (cons (list 'gnus-show-threads " Thread") minor-mode-alist)))
3613 (gnus-set-mode-line 'summary)
3614 (use-local-map gnus-summary-mode-map)
3615 (buffer-disable-undo (current-buffer))
3616 (setq buffer-read-only t) ;Disable modification
3617 (setq truncate-lines t)
3618 (setq selective-display t)
3619 (setq selective-display-ellipses t) ;Display `...'
3620 (run-hooks 'gnus-summary-mode-hook))
3622 (defun gnus-mouse-pick-article (e)
3625 (gnus-summary-next-page nil t))
3627 (defun gnus-summary-setup-buffer (group)
3628 "Initialize Summary buffer."
3629 (let ((buffer (concat "*Summary " group "*")))
3630 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
3631 (setq gnus-summary-buffer (set-buffer (get-buffer-create buffer)))
3632 (gnus-add-current-to-buffer-list)
3633 (gnus-summary-mode)))
3635 (defun gnus-summary-insert-dummy-line (sformat subject number)
3637 (setq sformat gnus-summary-dummy-line-format-spec))
3640 (insert (eval sformat))
3643 (insert (format "%s Z %d 0" subject number))
3644 (set-text-properties b (point) '(invisible t))
3647 (defun gnus-summary-insert-line
3648 (sformat header level current unread replied expirable subject-or-nil
3651 (setq sformat gnus-summary-line-format-spec))
3653 (make-string (* level gnus-thread-indent-level) ? ))
3654 (lines (or (header-lines header) 0))
3655 (interest (or gnus-summary-default-interest " "))
3656 (replied (if replied ?R ? ))
3657 (expirable (if expirable ?X ? ))
3658 (from (header-from header))
3659 (name-address (gnus-extract-address-components from))
3660 (address (car (cdr name-address)))
3661 (name (or (car name-address) (car (cdr name-address))))
3662 (number (header-number header))
3663 (subject (header-subject header))
3664 (buffer-read-only nil)
3665 (opening-bracket (if dummy ?\< ?\[))
3666 (closing-bracket (if dummy ?\> ?\]))
3668 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
3669 (if (not (numberp lines)) (setq lines 0))
3672 (insert-before-markers (eval sformat))
3674 (if (and gnus-visual gnus-mouse-face)
3675 (overlay-put (make-overlay b (point)) 'mouse-face gnus-mouse-face))
3676 ;; Info format SUBJECT INTEREST UNREAD NUMBER LEVEL
3677 (set-text-properties
3680 (insert (format "%s %d %c %d %d" (gnus-simplify-subject-re subject)
3681 (or gnus-summary-default-interest 5)
3682 unread number level)))
3683 (point) '(invisible t))
3686 (defun gnus-summary-update-lines ()
3687 ;; Rehighlight summary buffer according to `gnus-summary-highlight'.
3688 (if (and gnus-visual gnus-visual-summary-update-hook)
3690 (set-buffer gnus-summary-buffer)
3691 (goto-char (point-min))
3693 (run-hooks 'gnus-summary-update-hook)
3694 (forward-line 1)))))
3696 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
3699 (defun gnus-summary-read-group (group &optional show-all no-article kill-buffer)
3700 "Start reading news in newsgroup GROUP.
3701 If SHOW-ALL is non-nil, already read articles are also listed.
3702 If NO-ARTICLE is non-nil, no article is selected initially."
3703 (message "Retrieving newsgroup: %s..." (gnus-group-real-name group))
3704 (gnus-summary-setup-buffer group)
3705 (if (gnus-select-newsgroup group show-all)
3707 ;; You can change the order of subjects in this hook.
3708 (run-hooks 'gnus-select-group-hook)
3709 (gnus-summary-prepare)
3710 (if (and (zerop (buffer-size))
3711 gnus-newsgroup-dormant)
3712 (gnus-summary-show-all-dormant))
3715 gnus-newsgroup-killed
3716 (setq gnus-newsgroup-unreads
3717 (sort gnus-newsgroup-unreads (function <)))))
3718 (gnus-newsgroup-killed
3719 (if gnus-kill-killed nil gnus-newsgroup-killed)))
3720 (if (not (consp (car killed))) (setq killed (list killed)))
3721 ;; Function `gnus-apply-kill-file' must be called in this hook.
3722 (run-hooks 'gnus-apply-kill-hook)
3723 (setq gnus-newsgroup-killed killed))
3724 (if (zerop (buffer-size))
3726 ;; This newsgroup is empty.
3727 (gnus-summary-catchup-and-exit nil t) ;Without confirmations.
3728 (message "No unread news"))
3729 ;; Hide conversation thread subtrees. We cannot do this in
3730 ;; gnus-summary-prepare-hook since kill processing may not
3731 ;; work with hidden articles.
3732 (and gnus-show-threads
3733 gnus-thread-hide-subtree
3734 (gnus-summary-hide-all-threads))
3735 ;; Show first unread article if requested.
3736 (goto-char (point-min))
3737 (if (and (not no-article)
3738 gnus-auto-select-first
3739 (gnus-summary-first-unread-article))
3740 ;; Window is configured automatically.
3741 ;; Current buffer may be changed as a result of hook
3742 ;; evaluation, especially by gnus-summary-rmail-digest
3743 ;; command, so we should adjust cursor point carefully.
3744 (if (eq major-mode 'gnus-summary-mode)
3745 (gnus-summary-position-cursor))
3746 (gnus-configure-windows 'summary)
3747 (pop-to-buffer gnus-summary-buffer)
3748 (gnus-set-mode-line 'summary)
3749 (gnus-summary-position-cursor))
3750 (if (and kill-buffer
3751 (get-buffer kill-buffer)
3752 (buffer-name (get-buffer kill-buffer)))
3753 (kill-buffer kill-buffer))))
3754 ;; Cannot select newsgroup GROUP.
3755 (message "Couldn't select newsgroup")
3756 (set-buffer gnus-group-buffer)
3757 (gnus-summary-position-cursor)))
3759 (defun gnus-summary-prepare ()
3760 "Prepare summary list of current newsgroup in Summary buffer."
3761 (let ((buffer-read-only nil))
3763 (gnus-summary-prepare-threads
3764 (if gnus-show-threads
3765 (gnus-gather-threads (gnus-make-threads))
3766 gnus-newsgroup-headers)
3768 (gnus-summary-delete-dormant)
3769 ;; Erase header retrieval message.
3771 ;; Call hooks for modifying Summary buffer.
3772 ;; Suggested by sven@tde.LTH.Se (Sven Mattisson).
3773 (goto-char (point-min))
3774 (run-hooks 'gnus-summary-prepare-hook)))
3776 (defun gnus-summary-delete-dormant ()
3777 (let ((int gnus-newsgroup-dormant)
3778 (buffer-read-only nil)
3781 (if (gnus-summary-goto-subject (car int))
3784 (setq cur-level (gnus-summary-thread-level))
3786 (re-search-forward "[\n\r]")
3787 (if (<= (gnus-summary-thread-level) cur-level)
3788 ;; If the level of the next article is greater than the
3789 ;; level of this article, then it has to be the child of this
3790 ;; article, so we do not delete this article.
3792 (setq gnus-newsgroup-dormant-subjects
3793 (cons (cons (car int) (buffer-substring beg (point)))
3794 gnus-newsgroup-dormant-subjects))
3795 (delete-region beg (point))))))
3796 (setq int (cdr int)))))
3798 (defun gnus-gather-threads (threads)
3799 "Gather threads that have lost their roots."
3800 (if (not gnus-gather-loose-threads)
3802 (let ((hashtb (gnus-make-hashtable 1023))
3805 thread subject hthread unre-subject)
3807 (setq subject (header-subject (car (car threads))))
3808 (if (setq hthread (gnus-gethash
3810 (gnus-simplify-subject-re subject))
3813 (if (not (stringp (car (car hthread))))
3814 (setcar hthread (list subject (car hthread))))
3816 (append (car hthread) (cons (car threads) nil)))
3817 (setcdr prev (cdr threads))
3818 (setq threads prev))
3819 (gnus-sethash unre-subject threads hashtb))
3821 (setq threads (cdr threads)))
3824 (defun gnus-make-threads ()
3825 ;; This function takes the dependencies already made by
3826 ;; `gnus-get-newsgroup-headers' and builds the trees. First we go
3827 ;; through the dependecies in the hash table and finds all the
3828 ;; roots. Roots do not refer back to any valid articles.
3832 (if (not (car (symbol-value refs)))
3833 (setq mroots (append (cdr (symbol-value refs)) mroots))
3834 ;; Ok, these refer back to valid articles, but if
3835 ;; `gnus-thread-ignore-subject' is nil, we have to check that
3836 ;; the root has the same subject as its children. The children
3837 ;; that do not are made into roots and removed from the list
3839 (or gnus-thread-ignore-subject
3840 (let* ((prev (symbol-value refs))
3841 (subject (gnus-simplify-subject-re
3842 (header-subject (car prev))))
3843 (headers (cdr prev)))
3845 (if (not (string= subject
3846 (gnus-simplify-subject-re
3847 (header-subject (car headers)))))
3849 (setq mroots (cons (car headers) mroots))
3850 (setcdr prev (cdr headers)))
3851 (setq prev headers))
3852 (setq headers (cdr headers)))))))
3853 gnus-newsgroup-dependencies)
3855 ;; We sort the roots according to article number. (This has to be
3856 ;; done because all sequencing information was lost when we built
3857 ;; the dependecies hash table.)
3862 (< (header-number h1) (header-number h2)))))
3863 ;; Now we have all the roots, so we go through all them all and
3865 (mapcar (lambda (root) (gnus-make-sub-thread root)) roots)))
3867 (defun gnus-make-sub-thread (root)
3868 ;; This function makes a sub-tree for a node in the tree.
3869 (let ((children (reverse (cdr (gnus-gethash (downcase (header-id root))
3870 gnus-newsgroup-dependencies)))))
3874 (lambda (top) (gnus-make-sub-thread top)) children)))))
3876 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
3877 (defvar gnus-tmp-prev-subject "")
3879 ;; Basic ideas by Paul Dworkin <paul@media-lab.media.mit.edu>
3880 ;; Subject bug fix by jbw@bigbird.bu.edu (Joe Wells)
3881 (defun gnus-summary-prepare-threads
3882 (threads level &optional not-child no-subject)
3883 "Prepare Summary buffer from THREADS and indentation LEVEL.
3884 THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
3885 or a straight list of headers."
3886 (let (thread header number subject clevel)
3888 (setq thread (car threads)
3889 threads (cdr threads))
3890 ;; If `thread' is a cons, hierarchical threads are used. If not,
3891 ;; `thread' is the header.
3893 (setq header (car thread))
3894 (setq header thread))
3895 (if (stringp header)
3896 ;; The header is a dummy root.
3898 (cond ((eq gnus-summary-make-false-root 'adopt)
3899 ;; We let the first article adopt the rest.
3900 (gnus-summary-prepare-threads (list (car (cdr thread))) 0)
3901 (setq thread (cdr (cdr thread)))
3903 (gnus-summary-prepare-threads (list (car thread)) 1 t)
3904 (setq thread (cdr thread))))
3905 ((eq gnus-summary-make-false-root 'dummy)
3906 ;; We output a dummy root.
3907 (gnus-summary-insert-dummy-line
3908 nil header (header-number (car (car (cdr thread)))))
3910 ((eq gnus-summary-make-false-root 'empty)
3911 ;; We print the articles with empty subject fields.
3912 (gnus-summary-prepare-threads (list (car (cdr thread))) 0)
3913 (setq thread (cdr (cdr thread)))
3915 (gnus-summary-prepare-threads (list (car thread)) 0 nil t)
3916 (setq thread (cdr thread))))
3918 ;; We do not make a root for the gathered
3919 ;; sub-threads at all.
3921 ;; Print the sub-threads.
3922 (and (consp thread) (cdr thread)
3923 (gnus-summary-prepare-threads (cdr thread) clevel)))
3924 ;; The header is a real article.
3925 (setq number (header-number header)
3926 subject (header-subject header)
3927 gnus-tmp-prev-subject subject)
3928 (gnus-summary-insert-line
3929 nil header level nil
3930 (cond ((memq number gnus-newsgroup-marked) ?-)
3931 ((memq number gnus-newsgroup-dormant) ?+)
3932 ((memq number gnus-newsgroup-unreads) ? )
3934 (memq number gnus-newsgroup-replied)
3935 (memq number gnus-newsgroup-expirable)
3936 (if no-subject gnus-summary-same-subject
3937 (if (or (zerop level)
3938 (and gnus-thread-ignore-subject
3940 (gnus-simplify-subject-re gnus-tmp-prev-subject)
3941 (gnus-simplify-subject-re subject)))))
3943 gnus-summary-same-subject))
3945 ;; Recursively print subthreads.
3946 (and (consp thread) (cdr thread)
3947 (gnus-summary-prepare-threads (cdr thread) (1+ level)))))))
3949 (defun gnus-select-newsgroup (group &optional show-all)
3950 "Select newsgroup GROUP.
3951 If SHOW-ALL is non-nil, all articles in the group are selected."
3952 (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
3953 (real-group (gnus-group-real-name group))
3954 (info (nth 2 entry))
3955 articles header-marks)
3957 (if (eq (car entry) t)
3958 (or (if (nth 4 info)
3959 (gnus-activate-foreign-newsgroup info)
3960 (gnus-activate-newsgroup (car info)))
3961 (error "Couldn't request newsgroup %s" group)))
3962 (setq gnus-current-select-method (or (nth 4 info)
3963 gnus-select-method))
3964 (gnus-check-news-server (nth 4 info))
3965 (if (not (gnus-request-group group t))
3966 (error "Couldn't request newsgroup %s" group))
3968 (setq gnus-newsgroup-name group)
3969 (setq gnus-newsgroup-unselected nil)
3970 (setq gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
3973 ;; Check whether there are only dormant articles in this newsgroup.
3974 (= (length gnus-newsgroup-unreads)
3975 (length (cdr (assq 'dormant (nth 3 info))))))
3976 ;; Select all active articles.
3977 (setq articles (gnus-uncompress-sequence
3978 (gnus-gethash group gnus-active-hashtb))))
3980 ;; Select unread articles only.
3981 (setq articles gnus-newsgroup-unreads)))
3982 ;; Require confirmation if selecting large newsgroup.
3983 (if (not (numberp gnus-large-newsgroup))
3985 (let ((number (length articles))
3987 (if (> number gnus-large-newsgroup)
3993 "How many articles from %s (default %d): "
3994 gnus-newsgroup-name number))))
3996 (if (string-equal input "")
3997 number (string-to-int input))))
4000 (if (< (abs selected) number)
4004 ;; Select the N oldest articles.
4005 (setq articles (copy-sequence articles))
4006 (setq break (nthcdr (1- (abs selected)) articles))
4007 (setq gnus-newsgroup-unselected
4010 gnus-newsgroup-unreads))
4013 ;; Select the N most recent articles.
4014 (setq gnus-newsgroup-unselected
4015 (copy-sequence articles))
4016 (setq break (nthcdr (- number (1+ selected))
4017 gnus-newsgroup-unselected))
4018 (setq articles (cdr break))
4020 (setq gnus-newsgroup-unselected
4022 gnus-newsgroup-unselected
4023 gnus-newsgroup-unreads)))
4026 ;; Select no articles.
4027 (setq gnus-newsgroup-unselected articles)
4028 (setq articles nil)))))))
4032 ;; Create the list of headers from the headers.
4033 (setq gnus-newsgroup-headers
4034 (if (eq (gnus-retrieve-headers articles gnus-newsgroup-name) 'nov)
4036 (gnus-get-newsgroup-headers-xover articles))
4037 (gnus-get-newsgroup-headers)))
4038 ;; Remove cancelled articles from the list of unread articles.
4039 (setq gnus-newsgroup-unreads
4040 (gnus-intersection gnus-newsgroup-unreads
4043 (header-number headers))
4044 gnus-newsgroup-headers)))
4045 ;; Ticked articles must be a subset of unread articles.
4048 (gnus-adjust-marked-articles info)
4049 (setq gnus-newsgroup-marked (cdr (assq 'tick (nth 3 info))))
4050 (setq gnus-newsgroup-replied (cdr (assq 'reply (nth 3 info))))
4051 (setq gnus-newsgroup-expirable (cdr (assq 'expire (nth 3 info))))
4052 (setq gnus-newsgroup-killed (cdr (assq 'killed (nth 3 info))))
4053 (setq gnus-newsgroup-bookmarks (cdr (assq 'bookmark (nth 3 info))))
4054 (setq gnus-newsgroup-dormant (cdr (assq 'dormant (nth 3 info))))
4055 (setq gnus-newsgroup-processable nil)))
4056 ;; Check whether auto-expire is to be done in this group.
4057 (setq gnus-newsgroup-auto-expire
4058 (and (stringp gnus-auto-expirable-newsgroups)
4059 (string-match gnus-auto-expirable-newsgroups real-group)))
4060 ;; First and last article in this newsgroup.
4061 (setq gnus-newsgroup-begin
4062 (if gnus-newsgroup-headers
4063 (header-number (car gnus-newsgroup-headers))
4065 (setq gnus-newsgroup-end
4066 (if gnus-newsgroup-headers
4067 (header-number (gnus-last-element gnus-newsgroup-headers))
4069 ;; File name of the last saved article.
4070 (setq gnus-newsgroup-last-rmail nil)
4071 (setq gnus-newsgroup-last-mail nil)
4072 (setq gnus-newsgroup-last-folder nil)
4073 (setq gnus-newsgroup-last-file nil)
4074 ;; Reset article pointers etc.
4075 (setq gnus-current-article nil)
4076 (setq gnus-current-headers nil)
4077 (setq gnus-have-all-headers nil)
4078 (setq gnus-last-article nil)
4079 (setq gnus-xref-hashtb nil)
4080 (setq gnus-reffed-article-number -1)
4081 (setq gnus-newsgroup-headers-hashtb-by-number nil)
4082 ;; Update the format specifiers.
4083 (gnus-update-format-specifications)
4084 ;; GROUP is successfully selected.
4087 (defun gnus-adjust-marked-articles (info)
4088 "Remove all marked articles that are no longer legal."
4089 (let ((marked-lists (nth 3 info))
4090 (active (gnus-gethash (car info) gnus-active-hashtb))
4092 ;; There are four types of marked articles - ticked, replied,
4093 ;; expirable and dormant.
4095 (setq m (cdr (setq prev (car marked-lists))))
4096 (cond ((or (eq 'tick (car prev)) (eq 'dormant (car prev)))
4097 ;; Make sure that all ticked articles are a subset of the
4098 ;; unread/unselected articles.
4100 (if (or (memq (car m) gnus-newsgroup-unreads)
4101 (memq (car m) gnus-newsgroup-unselected))
4103 (setcdr prev (cdr m)))
4105 ((eq 'bookmark (car prev))
4106 ;; Bookmarks should be a subset of active articles.
4108 (if (< (car (car m)) (car active))
4109 (setcdr prev (cdr m))
4112 ((eq 'killed (car prev))
4113 ;; Articles that have been through the kill process are
4114 ;; to be a subset of active articles.
4115 (while (and m (< (cdr (car m)) (car active)))
4116 (setcdr prev (cdr m)))
4117 (if (and m (< (car (car m)) (car active)))
4118 (setcar (car m) (car active))))
4119 ((or (eq 'reply (car marked)) (eq 'expire (car marked)))
4120 ;; The replied and expirable articles have to be articles
4123 (if (< (car m) (car active))
4124 (setcdr prev (cdr m))
4127 (setq marked-lists (cdr marked-lists)))
4128 ;; Remove all lists that are empty.
4129 (setq marked-lists (nth 3 info))
4132 (while (= 1 (length (car marked-lists)))
4133 (setq marked-lists (cdr marked-lists)))
4134 (setq m (cdr (setq prev marked-lists)))
4136 (if (= 1 (length (car m)))
4137 (setcdr prev (cdr m))
4140 (setcar (nthcdr 3 info) marked-lists)))
4141 ;; Finally, if there are no marked lists at all left, and if there
4142 ;; are no elements after the lists in the info list, we just chop
4143 ;; the info list off before the marked lists.
4144 (if (and (null marked-lists) (not (nthcdr 4 info)))
4145 (setcdr (nthcdr 2 info) nil)))
4148 (defun gnus-set-marked-articles
4149 (info ticked replied expirable killed dormant bookmark)
4150 "Enter the various lists of marked articles into the newsgroup info list."
4153 (setq newmarked (cons (cons 'tick ticked) nil)))
4155 (setq newmarked (cons (cons 'reply replied) newmarked)))
4157 (setq newmarked (cons (cons 'expire expirable) newmarked)))
4159 (setq newmarked (cons (cons 'killed killed) newmarked)))
4161 (setq newmarked (cons (cons 'dormant dormant) newmarked)))
4163 (setq newmarked (cons (cons 'bookmark bookmark) newmarked)))
4166 (setcar (nthcdr 3 info) newmarked)
4167 (if (not (nthcdr 4 info))
4168 (setcdr (nthcdr 2 info) nil)
4169 (setcar (nthcdr 3 info) nil)))
4171 (setcdr (nthcdr 2 info) (cons newmarked nil))))))
4173 (defun gnus-set-mode-line (where)
4174 "This function sets the mode line of the Article or Summary buffers.
4175 If WHERE is `summary', the summary mode line format will be used."
4176 (if (memq where gnus-updated-mode-lines)
4179 (set-buffer gnus-summary-buffer)
4180 (let* ((mformat (if (eq where 'article)
4181 gnus-article-mode-line-format-spec
4182 gnus-summary-mode-line-format-spec))
4183 (group-name gnus-newsgroup-name)
4184 (article-number (or gnus-current-article 0))
4185 (unread (- (length gnus-newsgroup-unreads)
4186 (length gnus-newsgroup-dormant)))
4187 (unselected (length gnus-newsgroup-unselected))
4188 (unread-and-unselected
4189 (cond ((and (zerop unread) (zerop unselected)) "")
4190 ((zerop unselected) (format "{%d more}" unread))
4191 (t (format "{%d(+%d) more}" unread unselected))))
4193 (if gnus-current-headers
4194 (header-subject gnus-current-headers) ""))
4195 (max-len (if (eq where 'summary) 45 52)))
4196 (setq mode-string (eval mformat))
4197 (if (> (length mode-string) max-len)
4199 (concat (substring mode-string 0 (- max-len 4)) "...")))
4200 (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
4201 (setq mode-line-buffer-identification mode-string)
4202 (set-buffer-modified-p t))))
4204 (defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
4205 "Go through the HEADERS list and add all Xrefs to a hash table.
4206 The resulting hash table is returned, or nil if no Xrefs were found."
4207 (let ((prefix (if (and
4208 (string-match gnus-foreign-group-prefix from-newsgroup)
4209 (not (eq 'nnvirtual (car gnus-current-select-method))))
4210 gnus-foreign-group-prefix))
4211 (xref-hashtb (make-vector 63 0))
4212 start group entry number xrefs header)
4214 (setq header (car headers))
4215 (if (and (setq xrefs (header-xref header))
4216 (not (memq (header-number header) unreads)))
4219 (while (string-match "\\([^ :]+\\):\\([0-9]+\\)" xrefs start)
4220 (setq start (match-end 0))
4221 (setq group (concat prefix (substring xrefs (match-beginning 1)
4224 (string-to-int (substring xrefs (match-beginning 2)
4226 (if (setq entry (gnus-gethash group xref-hashtb))
4227 (setcdr entry (cons number (cdr entry)))
4228 (gnus-sethash group (cons number nil) xref-hashtb)))))
4229 (setq headers (cdr headers)))
4230 (if start xref-hashtb nil)))
4232 (defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
4233 "Look through all the headers and mark the Xrefs as read."
4234 (let (name entry read info xref-hashtb idlist active num range)
4235 (set-buffer gnus-group-buffer)
4236 (if (setq xref-hashtb
4237 (gnus-create-xref-hashtb from-newsgroup headers unreads))
4240 (if (string= from-newsgroup (setq name (symbol-name group)))
4242 (setq idlist (symbol-value group))
4243 ;; Dead groups are not updated.
4244 (if (and (setq entry (gnus-gethash name gnus-newsrc-hashtb))
4245 ;; Only do the xrefs if the group has the same
4246 ;; select method as the group we have just read.
4247 (or (and (not (nth 4 (setq info (nth 2 entry))))
4248 (eq gnus-current-select-method
4249 gnus-select-method))
4250 (eq (car gnus-current-select-method) 'nnvirtual)
4252 gnus-current-select-method)))
4255 ;; Set the new list of read articles in this group.
4256 (setq active (gnus-gethash name gnus-active-hashtb))
4257 ;; First peel off all illegal article numbers.
4261 (if (or (> (car id) (cdr active))
4262 (< (car id) (car active)))
4263 (setq idlist (delq (car id) idlist)))
4264 (setq id (cdr id)))))
4265 (setcar (nthcdr 2 info)
4269 (setq idlist (sort idlist '<)))))
4270 ;; Then we have to re-compute how many unread
4271 ;; articles there are in this group.
4274 (if (atom (car range))
4276 (setq num (- (1+ (cdr active)) (car active)))
4277 (setq num (- (cdr active) (- (1+ (cdr range))
4280 (setq num (+ num (- (1+ (cdr (car range)))
4281 (car (car range)))))
4282 (setq range (cdr range)))
4283 (setq num (- (cdr active) num)))
4284 ;; Update the number of unread articles.
4285 (setcar entry (max 0 num))
4286 ;; Update the Newsgroup buffer.
4287 (gnus-group-update-group name t)))))))
4290 (defsubst gnus-header-value ()
4291 (buffer-substring (match-end 0) (save-excursion (end-of-line) (point))))
4293 ;; Felix Lee function with jwz rewrites (and some lmi rewrites to boot).
4294 ;; Goes through the newsgroups headers and returns a list of arrays:
4295 (defun gnus-get-newsgroup-headers ()
4296 (setq gnus-article-internal-prepare-hook nil)
4298 (let ((cur nntp-server-buffer)
4299 (dependencies (gnus-make-hashtable (length gnus-newsgroup-unreads)))
4301 headers header subject from char c article unreads in-reply-to
4302 references end-header id dep ref end)
4303 (set-buffer nntp-server-buffer)
4305 (while (re-search-forward "^[23][0-9]+ \\([0-9]+\\)" nil t)
4311 header (make-vector 9 nil)
4313 (goto-char (match-beginning 1))
4315 header (setq article (read cur)))
4316 (setq end-header (save-excursion (search-forward "\n.\n" nil t)))
4317 (while (re-search-forward "^\\(from\\|subject\\|message-id\\|date\\|lines\\|xref\\|references\\|in-reply-to\\): "
4320 (setq char (downcase (following-char)))
4323 (header-set-subject header
4324 (setq subject (gnus-header-value))))
4326 (header-set-from header (setq from (gnus-header-value))))
4328 (header-set-xref header (gnus-header-value)))
4330 (header-set-lines header
4331 (string-to-int (gnus-header-value))))
4333 (header-set-date header (gnus-header-value)))
4335 (header-set-id header (setq id (gnus-header-value))))
4337 (setq references (gnus-header-value))
4338 (setq end (match-end 0))
4345 (search-backward ">" end t)
4348 (search-backward "<" end t)
4351 (setq in-reply-to (gnus-header-value))))
4354 (header-set-references header references)
4356 (string-match "<[^>]+>" in-reply-to)
4357 (header-set-references
4359 (setq ref (downcase (substring in-reply-to (match-beginning 0)
4361 (or subject (header-set-subject header "(none)"))
4362 (or from (header-set-from header "(nobody)"))
4363 ;; We build the thread tree.
4369 (concat "none+" (int-to-string
4370 (setq none-id (1+ none-id))))))
4372 (setcar (symbol-value dep) header)
4373 (set dep (list header)))
4374 (if (boundp (setq dep (intern (or ref "none") dependencies)))
4375 (setcdr (symbol-value dep)
4376 (cons header (cdr (symbol-value dep))))
4377 (set dep (list nil header)))
4378 (setq headers (cons header headers))
4380 (search-forward "\n.\n" nil t))
4381 (setq gnus-newsgroup-dependencies dependencies)
4382 (nreverse headers))))
4384 ;; The following macros and functions were written by Felix Lee
4385 ;; <flee@cse.psu.edu>.
4387 ;; This is almost 4x faster than (string-to-int (buffer-substring ... ))
4388 ;; primarily because of garbage collection. -jwz
4389 (defmacro gnus-read-integer (&optional point move-p)
4390 (` ((, (if move-p 'progn 'save-excursion))
4391 (,@ (if point (list (list 'goto-char point))))
4392 (if (and (<= (following-char) ?9)
4393 (>= (following-char) ?0))
4394 (read (current-buffer))
4397 (defmacro gnus-nov-skip-field ()
4398 '(search-forward "\t" eol 'end))
4400 (defmacro gnus-nov-field ()
4403 (progn (gnus-nov-skip-field) (1- (point)))))
4405 ;; Goes through the xover lines and returns a list of vectors
4406 (defun gnus-get-newsgroup-headers-xover (sequence)
4407 "Parse the news overview data in the server buffer, and return a
4408 list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
4409 ;; Get the Xref when the users reads the articles since most/some
4410 ;; NNTP servers do not include Xrefs when using XOVER.
4411 (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
4413 (set-buffer nntp-server-buffer)
4414 (let ((cur (current-buffer))
4415 (dependencies (gnus-make-hashtable (length gnus-newsgroup-unreads)))
4417 number header headers eol header id ref dep)
4418 (goto-char (point-min))
4419 (while (and sequence (not (eobp)))
4420 (setq number (read cur))
4421 (while (and sequence (< (car sequence) number))
4422 (setq sequence (cdr sequence)))
4424 (eq number (car sequence))
4426 (setq sequence (cdr sequence))
4431 ;; overview: [num subject from date id refs chars lines misc]
4435 (gnus-nov-field) ; subject
4436 (gnus-nov-field) ; from
4437 (gnus-nov-field) ; date
4438 (setq id (gnus-nov-field)) ; id
4441 (let ((beg (point)))
4442 (search-forward "\t" eol)
4443 (if (search-backward ">" beg t)
4449 (search-backward "<" beg t)
4452 (gnus-nov-field)) ; refs
4455 (if (/= (following-char) ?\t)
4458 (gnus-nov-field)) ; misc
4460 ;; We build the thread tree.
4465 (or id (concat "none+"
4467 (setq none (1+ none))))))
4469 (setcar (symbol-value dep) header)
4470 (set dep (list header)))
4471 (if (boundp (setq dep (intern (or ref "none") dependencies)))
4472 (setcdr (symbol-value dep)
4473 (cons header (cdr (symbol-value dep))))
4474 (set dep (list nil header)))
4475 (setq headers (cons header headers))))
4477 (setq headers (nreverse headers))
4478 (setq gnus-newsgroup-dependencies dependencies)
4481 (defun gnus-article-get-xrefs ()
4482 "Fill in the Xref value in `gnus-current-headers', if necessary.
4483 This is meant to be called in `gnus-article-internal-prepare-hook'."
4484 (or (not gnus-use-cross-reference)
4485 (let ((case-fold-search t)
4488 (gnus-narrow-to-headers)
4489 (goto-char (point-min))
4490 (if (or (and (eq (downcase (following-char)) ?x)
4491 (looking-at "Xref:"))
4492 (search-forward "\nXref:" nil t))
4494 (goto-char (1+ (match-end 0)))
4495 (setq xref (buffer-substring (point)
4496 (progn (end-of-line) (point))))
4498 (set-buffer gnus-summary-buffer)
4499 (header-set-xref gnus-current-headers xref))))))))
4501 (defalias 'gnus-find-header-by-number 'gnus-get-header-by-number)
4502 (make-obsolete 'gnus-find-header-by-number 'gnus-get-header-by-number)
4504 ;; Return a header specified by a NUMBER.
4505 (defun gnus-get-header-by-number (number)
4506 (or gnus-newsgroup-headers-hashtb-by-number
4507 (gnus-make-headers-hashtable-by-number))
4508 (gnus-gethash (int-to-string number)
4509 gnus-newsgroup-headers-hashtb-by-number))
4511 (defun gnus-make-headers-hashtable-by-number ()
4512 "Make hashtable for the variable gnus-newsgroup-headers by number."
4514 (headers gnus-newsgroup-headers))
4515 (setq gnus-newsgroup-headers-hashtb-by-number
4516 (gnus-make-hashtable (length headers)))
4518 (setq header (car headers))
4519 (gnus-sethash (int-to-string (header-number header))
4520 header gnus-newsgroup-headers-hashtb-by-number)
4521 (setq headers (cdr headers))
4524 (defun gnus-more-header-backward ()
4525 "Find new header backward."
4526 (let ((first (car (gnus-gethash gnus-newsgroup-name gnus-active-hashtb)))
4527 (artnum gnus-newsgroup-begin)
4529 (while (and (not header)
4531 (setq artnum (1- artnum))
4532 (setq header (gnus-read-header artnum)))
4535 (defun gnus-more-header-forward ()
4536 "Find new header forward."
4537 (let ((last (cdr (gnus-gethash gnus-newsgroup-name gnus-active-hashtb)))
4538 (artnum gnus-newsgroup-end)
4540 (while (and (not header)
4542 (setq artnum (1+ artnum))
4543 (setq header (gnus-read-header artnum)))
4546 (defun gnus-extend-newsgroup (header &optional backward)
4547 "Extend newsgroup selection with HEADER.
4548 Optional argument BACKWARD means extend toward backward."
4550 (let ((artnum (header-number header)))
4551 (setq gnus-newsgroup-headers
4553 (cons header gnus-newsgroup-headers)
4554 (nconc gnus-newsgroup-headers (list header))))
4555 (setq gnus-newsgroup-unselected
4556 (delq artnum gnus-newsgroup-unselected))
4557 (setq gnus-newsgroup-begin (min gnus-newsgroup-begin artnum))
4558 (setq gnus-newsgroup-end (max gnus-newsgroup-end artnum)))))
4561 (defun gnus-summary-search-group (&optional backward use-level)
4562 "Search for next unread newsgroup.
4563 If optional argument BACKWARD is non-nil, search backward instead."
4565 (set-buffer gnus-group-buffer)
4567 ;; We don't want to alter current point of Group mode buffer.
4568 (if (gnus-group-search-forward
4570 (if use-level (gnus-group-group-level) nil))
4571 (gnus-group-group-name))
4574 (defun gnus-summary-search-subject (&optional backward unread subject)
4575 "Search for article forward.
4576 If BACKWARD is non-nil, search backward.
4577 If UNREAD is non-nil, only unread articles are selected.
4578 If SUBJECT is non-nil, the article which has the same subject will be
4582 (function re-search-backward) (function re-search-forward)))
4583 ;; We have to take care of hidden lines.
4586 (format "%s [-0-9]+ %s \\([-0-9 ]+\\) [0-9]+[\n\r]"
4587 (regexp-quote (gnus-simplify-subject-re subject))
4588 (if unread gnus-unread-mark "."))
4589 (if unread (concat "^[" gnus-unread-mark "]") "^."))))
4594 (if (funcall func regexp nil t)
4596 (goto-char (match-beginning 0))
4597 (gnus-summary-article-number))
4599 ;; Adjust cursor point.
4600 (gnus-summary-position-cursor))))
4602 (defun gnus-summary-search-forward (&optional unread subject backward)
4603 "Search for article forward.
4604 If UNREAD is non-nil, only unread articles are selected.
4605 If SUBJECT is non-nil, the article which has the same subject will be
4607 If BACKWARD is non-nil, the search will be performed backwards instead."
4608 (gnus-summary-search-subject backward unread subject))
4610 (defun gnus-summary-search-backward (&optional unread subject)
4611 "Search for article backward.
4612 If 1st optional argument UNREAD is non-nil, only unread article is selected.
4613 If 2nd optional argument SUBJECT is non-nil, the article which has
4614 the same subject will be searched for."
4615 (gnus-summary-search-forward unread subject t))
4617 (defun gnus-summary-article-number (&optional number-or-nil)
4618 "The article number of the article on the current line.
4619 If there isn's an article number here, then we return the current
4623 (if (re-search-forward " [-0-9]+ [0-9]+[\n\r]" nil t)
4625 ;; jwz: this is faster than string-to-int/buffer-substring
4626 (goto-char (match-beginning 0))
4627 (read (current-buffer)))
4628 ;; We return the current if we couldn't find anything.
4629 (if number-or-nil nil gnus-current-article))))
4631 (defun gnus-summary-thread-level ()
4632 "The thread level of the article on the current line."
4635 (if (re-search-forward " [0-9]+[\n\r]" nil t)
4637 (goto-char (match-beginning 0))
4638 (read (current-buffer)))
4639 ;; We return zero if we couldn't find anything.
4642 (defun gnus-summary-article-mark ()
4643 "The mark on the current line."
4646 (if (re-search-forward ". [-0-9]+ [0-9]+[\n\r]" nil t)
4647 (char-after (match-beginning 0)))))
4649 (defun gnus-summary-subject-string ()
4650 "Return current subject string or nil if nothing."
4653 (if (re-search-forward " [-0-9]+ . [-0-9]+ [0-9]+[\n\r]" nil t)
4654 (let ((beg (previous-property-change (match-beginning 0)))
4655 (end (match-beginning 0))
4656 (buffer-read-only nil))
4657 (set-text-properties beg end nil)
4659 (buffer-substring beg end)
4660 (set-text-properties beg end '(invisible t))))
4663 (defun gnus-summary-interest ()
4664 "Return current article interest."
4667 (if (re-search-forward " [-0-9]+ . [-0-9]+ [0-9]+[\n\r]" nil t)
4669 (goto-char (match-beginning 0))
4670 (read (current-buffer)))
4671 ;; We return zero if we couldn't find anything.
4674 (defun gnus-summary-recenter ()
4675 "Center point in Summary window."
4676 ;; Scroll window so as to cursor comes center of Summary window
4677 ;; only when article is displayed.
4678 ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
4679 ;; Recenter only when requested.
4680 ;; Subbested by popovich@park.cs.columbia.edu
4681 (and gnus-auto-center-summary
4682 (get-buffer-window gnus-article-buffer)
4683 (< (/ (- (window-height) 1) 2)
4684 (count-lines (point) (point-max)))
4685 (recenter (/ (- (window-height) 2) 2))))
4687 (defun gnus-summary-jump-to-group (newsgroup)
4688 "Move point to NEWSGROUP in Group mode buffer."
4689 ;; Keep update point of Group mode buffer if visible.
4690 (if (eq (current-buffer)
4691 (get-buffer gnus-group-buffer))
4692 (save-window-excursion
4693 ;; Take care of tree window mode.
4694 (if (get-buffer-window gnus-group-buffer)
4695 (pop-to-buffer gnus-group-buffer))
4696 (gnus-group-jump-to-group newsgroup))
4698 ;; Take care of tree window mode.
4699 (if (get-buffer-window gnus-group-buffer)
4700 (pop-to-buffer gnus-group-buffer)
4701 (set-buffer gnus-group-buffer))
4702 (gnus-group-jump-to-group newsgroup))))
4704 ;; This function returns a list of article numbers based on the
4705 ;; difference between the ranges of read articles in this group and
4706 ;; the range of active articles.
4707 (defun gnus-list-of-unread-articles (group)
4708 (let* ((read (nth 2 (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
4709 (active (gnus-gethash group gnus-active-hashtb))
4711 unread first nlast unread)
4712 ;; If none are read, then all are unread.
4714 (setq first (car active))
4715 ;; If the range of read articles is a single range, then the
4716 ;; first unread article is the article after the last read
4717 ;; article. Sounds logical, doesn't it?
4718 (if (atom (car read))
4719 (setq first (1+ (cdr read)))
4720 ;; `read' is a list of ranges.
4723 (while (< first nlast)
4724 (setq unread (cons first unread))
4725 (setq first (1+ first))))
4726 (setq first (1+ (cdr (car read))))
4727 (setq nlast (car (car (cdr read))))
4728 (setq read (cdr read)))))
4729 ;; And add the last unread articles.
4730 (while (<= first last)
4731 (setq unread (cons first unread))
4732 (setq first (1+ first)))
4733 ;; Return the list of unread articles.
4737 ;; Gnus Summary mode commands.
4739 ;; Various summary commands
4741 (defun gnus-summary-toggle-truncation (arg)
4742 "Toggle truncation of summary lines.
4743 With arg, turn line truncation on iff arg is positive."
4745 (setq truncate-lines
4746 (if (null arg) (not truncate-lines)
4747 (> (prefix-numeric-value arg) 0)))
4750 (defun gnus-summary-reselect-current-group (show-all)
4751 "Once exit and then reselect the current newsgroup.
4752 Prefix argument SHOW-ALL means to select all articles."
4754 (let ((current-subject (gnus-summary-article-number)))
4755 (gnus-summary-exit t)
4756 ;; We have to adjust the point of Group mode buffer because the
4757 ;; current point was moved to the next unread newsgroup by
4759 (gnus-summary-jump-to-group gnus-newsgroup-name)
4760 (gnus-group-read-group show-all t)
4761 (gnus-summary-goto-subject current-subject)
4764 (defun gnus-summary-rescan-group (all)
4765 "Exit the newsgroup, ask for new articles, and select the newsgroup."
4767 ;; Fix by Ilja Weis <kult@uni-paderborn.de>.
4768 (let ((group gnus-newsgroup-name))
4769 (gnus-summary-exit t)
4770 (gnus-summary-jump-to-group group)
4772 (set-buffer gnus-group-buffer)
4773 (gnus-group-get-new-news-this-group 1))
4774 (gnus-summary-jump-to-group group)
4775 (gnus-group-read-group all)))
4777 (defun gnus-summary-exit (&optional temporary)
4778 "Exit reading current newsgroup, and then return to group selection mode.
4779 gnus-exit-group-hook is called with no arguments if that value is non-nil."
4781 (gnus-kill-save-kill-buffer)
4782 (let ((group gnus-newsgroup-name)
4784 (buf (current-buffer)))
4786 (headers gnus-newsgroup-headers)
4787 (unreads gnus-newsgroup-unreads)
4788 (unselected (setq gnus-newsgroup-unselected
4789 (sort gnus-newsgroup-unselected '<)))
4790 (ticked gnus-newsgroup-marked))
4791 ;; Important internal variables are saved, so we can reenter
4792 ;; the Summary buffer even if the hook changes them.
4793 (run-hooks 'gnus-exit-group-hook)
4794 (gnus-update-read-articles group unreads unselected ticked
4795 t gnus-newsgroup-replied
4796 gnus-newsgroup-expirable
4797 gnus-newsgroup-killed
4798 gnus-newsgroup-dormant
4799 gnus-newsgroup-bookmarks)
4800 ;; t means ignore unsubscribed newsgroups.
4801 (if gnus-use-cross-reference
4802 (gnus-mark-xrefs-as-read group headers unreads))
4803 ;; Do not switch windows but change the buffer to work.
4804 (set-buffer gnus-group-buffer)
4805 (gnus-group-update-group group))
4806 ;; Make sure where I was, and go to next newsgroup.
4807 (gnus-group-jump-to-group group)
4808 ; (gnus-group-next-unread-group 1)
4810 ;; If exiting temporary, caller should adjust Group mode
4811 ;; buffer point by itself.
4813 ;; Return to Group mode buffer.
4814 (if (and (get-buffer buf)
4815 (eq mode 'gnus-summary-mode))
4817 (if (get-buffer gnus-article-buffer)
4818 (bury-buffer gnus-article-buffer))
4819 (setq gnus-current-select-method gnus-select-method)
4820 (gnus-configure-windows 'newsgroups t)
4821 (pop-to-buffer gnus-group-buffer))))
4823 (defun gnus-summary-quit ()
4824 "Quit reading current newsgroup without updating read article info."
4826 (if (y-or-n-p "Do you really wanna quit reading this group? ")
4828 (message "") ;Erase "Yes or No" question.
4829 ;; Return to Group selection mode.
4830 (if (get-buffer gnus-summary-buffer)
4831 (bury-buffer gnus-summary-buffer))
4832 (if (get-buffer gnus-article-buffer)
4833 (bury-buffer gnus-article-buffer))
4834 (gnus-configure-windows 'newsgroups)
4835 (pop-to-buffer gnus-group-buffer)
4836 (gnus-group-jump-to-group gnus-newsgroup-name) ;Make sure where I was.
4837 (gnus-group-next-group 1))))
4839 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
4840 (defun gnus-summary-describe-group ()
4841 "Describe the current newsgroup."
4843 (gnus-group-describe-group gnus-newsgroup-name))
4845 (defun gnus-summary-describe-briefly ()
4846 "Describe Summary mode commands briefly."
4849 (substitute-command-keys "\\[gnus-summary-next-page]:Select \\[gnus-summary-next-unread-article]:Forward \\[gnus-summary-prev-unread-article]:Backward \\[gnus-summary-exit]:Exit \\[gnus-info-find-node]:Run Info \\[gnus-summary-describe-briefly]:This help")))
4851 ;; Walking around Group mode buffer from Summary mode.
4853 (defun gnus-summary-next-group (&optional no-article group)
4854 "Exit current newsgroup and then select next unread newsgroup.
4855 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
4857 (let ((ingroup gnus-newsgroup-name))
4858 (gnus-summary-exit t) ;Update all information.
4859 (gnus-group-jump-to-group ingroup)
4860 (let ((group (or group (gnus-summary-search-group)))
4861 (buf gnus-summary-buffer))
4864 (message "Selecting %s..." group)
4865 ;; We are now in Group mode buffer.
4866 ;; Make sure Group mode buffer point is on GROUP.
4867 (gnus-group-jump-to-group group)
4868 (gnus-summary-read-group group nil no-article buf)))))
4870 (defun gnus-summary-prev-group (no-article)
4871 "Exit current newsgroup and then select previous unread newsgroup.
4872 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
4874 ;; Make sure Group mode buffer point is on current newsgroup.
4875 (gnus-summary-jump-to-group gnus-newsgroup-name)
4876 (let ((group (gnus-summary-search-group t)))
4879 (message "Exiting %s..." gnus-newsgroup-name)
4882 (message "Selecting %s..." group)
4883 (gnus-summary-exit t) ;Exit Summary mode temporary.
4884 ;; We are now in Group mode buffer.
4885 ;; We have to adjust point of Group mode buffer because current
4886 ;; point is moved to next unread newsgroup by exiting.
4887 (gnus-summary-jump-to-group group)
4888 (gnus-summary-read-group group nil no-article)
4889 (or (eq (current-buffer)
4890 (get-buffer gnus-summary-buffer))
4891 (eq gnus-auto-select-next t)
4892 ;; Expected newsgroup has nothing to read since the articles
4893 ;; are marked as read by cross-referencing. So, try next
4894 ;; newsgroup. (Make sure we are in Group mode buffer now.)
4895 (and (eq (current-buffer)
4896 (get-buffer gnus-group-buffer))
4897 (gnus-summary-search-group t)
4898 (gnus-summary-read-group
4899 (gnus-summary-search-group t) nil no-article))
4903 ;; Walking around summary lines.
4905 (defun gnus-summary-first-subject (unread)
4906 "Go to the first unread subject.
4907 If UNREAD is non-nil, go to the first unread article.
4908 Returns nil if there are no unread articles."
4909 (let ((begin (point)))
4911 (if (re-search-forward
4912 (concat (if unread " " ".") " [-0-9]+ [0-9]+[\n\r]") nil t)
4915 ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
4916 (gnus-summary-position-cursor)
4918 ;; If there is no unread articles, stay where you are.
4920 (message "No more unread articles")
4923 (defun gnus-summary-next-subject (n &optional unread)
4924 "Go to next N'th summary line.
4925 If N is negative, go to the previous N'th subject line.
4926 If UNREAD is non-nil, only unread articles are selected.
4927 The difference between N and the actual number of steps taken is
4930 (let ((backward (< n 0))
4933 (gnus-summary-search-forward unread nil backward))
4935 (gnus-summary-recenter)
4936 (if (/= 0 n) (message "No more%s articles" (if unread " unread" "")))
4937 ; (gnus-summary-position-cursor)
4940 (defun gnus-summary-next-unread-subject (n)
4941 "Go to next N'th unread summary line."
4943 (gnus-summary-next-subject n t))
4945 (defun gnus-summary-prev-subject (n &optional unread)
4946 "Go to previous N'th summary line.
4947 If optional argument UNREAD is non-nil, only unread article is selected."
4949 (gnus-summary-next-subject (- n) unread))
4951 (defun gnus-summary-prev-unread-subject (n)
4952 "Go to previous N'th unread summary line."
4954 (gnus-summary-next-subject (- n) t))
4956 (defun gnus-summary-goto-subject (article)
4957 "Go the subject line of ARTICLE."
4961 (completing-read "Article number: "
4965 (int-to-string (header-number headers))))
4966 gnus-newsgroup-headers)
4967 nil 'require-match))))
4969 (error "No article number"))
4970 (if (or (eq article (gnus-summary-article-number t))
4971 (let ((org (point)))
4973 (if (re-search-forward
4974 (format "[^Z] %d [0-9]+[\n\r]" article) nil t)
4975 (goto-char (match-beginning 0))
4979 (gnus-summary-position-cursor)
4982 ;; Walking around summary lines with displaying articles.
4984 (defun gnus-summary-expand-window ()
4985 "Expand Summary window to show headers full window."
4987 (gnus-configure-windows 'summary)
4988 (pop-to-buffer gnus-summary-buffer))
4990 (defun gnus-summary-display-article (article &optional all-header)
4991 "Display ARTICLE in Article buffer."
4992 (setq gnus-summary-buffer (current-buffer))
4995 (gnus-configure-windows 'article)
4996 (pop-to-buffer gnus-summary-buffer)
4997 (gnus-article-prepare article all-header)
4998 (if (= (gnus-summary-article-mark) ?Z)
5001 (gnus-summary-position-cursor)))
5002 (gnus-summary-recenter)
5003 (gnus-set-mode-line 'summary)
5004 (run-hooks 'gnus-select-article-hook)
5005 ;; Successfully display article.
5008 (defun gnus-summary-select-article (&optional all-headers force)
5009 "Select the current article.
5010 Optional first argument ALL-HEADERS is non-nil, show all header fields.
5011 Optional second argument FORCE is nil, the article is only selected
5012 again when current header does not match with ALL-HEADERS option."
5013 (let ((article (gnus-summary-article-number))
5014 (all-headers (not (not all-headers)))) ;Must be T or NIL.
5015 (if (or (null gnus-current-article)
5016 (null gnus-article-current)
5017 (/= article (cdr gnus-article-current))
5018 (not (equal (car gnus-article-current) gnus-newsgroup-name))
5020 ;; The requested article is different from the current article.
5021 (gnus-summary-display-article article all-headers)
5023 (gnus-article-show-all-headers))
5024 (gnus-configure-windows 'article)
5025 (pop-to-buffer gnus-summary-buffer))))
5027 (defun gnus-summary-set-current-mark (&optional current-mark)
5028 "Obsolete function."
5031 (defun gnus-summary-next-article (unread &optional subject)
5032 "Select the article after the current one.
5033 If UNREAD is non-nil, only unread articles are selected."
5036 (cond ((gnus-summary-display-article
5037 (gnus-summary-search-forward unread subject)))
5039 gnus-auto-select-same
5040 (gnus-set-difference gnus-newsgroup-unreads
5041 (append gnus-newsgroup-marked
5042 gnus-newsgroup-dormant))
5044 '(gnus-summary-next-unread-article
5045 gnus-summary-next-page
5046 gnus-summary-kill-same-subject-and-select
5047 ;;gnus-summary-next-article
5048 ;;gnus-summary-next-same-subject
5049 ;;gnus-summary-next-unread-same-subject
5051 ;; Wrap article pointer if there are unread articles.
5052 ;; Hook function, such as gnus-summary-rmail-digest, may
5053 ;; change current buffer, so need check.
5054 (let ((buffer (current-buffer))
5055 (last-point (point)))
5056 ;; No more articles with same subject, so jump to the first
5058 (gnus-summary-first-unread-article)
5059 ;;(and (eq buffer (current-buffer))
5060 ;; (= (point) last-point)
5061 ;; ;; Ignore given SUBJECT, and try again.
5062 ;; (gnus-summary-next-article unread nil))
5063 (and (eq buffer (current-buffer))
5064 (< (point) last-point)
5065 (message "Wrapped"))
5067 ((and gnus-auto-extend-newsgroup
5068 (not unread) ;Not unread only
5069 (not subject) ;Only if subject is not specified.
5070 (setq header (gnus-more-header-forward)))
5071 ;; Extend to next article if possible.
5072 ;; Basic ideas by himacdonald@watdragon.waterloo.edu
5073 (gnus-extend-newsgroup header nil)
5074 ;; Threads feature must be turned off.
5075 (let ((buffer-read-only nil))
5076 (goto-char (point-max))
5077 (gnus-summary-prepare-threads (list header) 0))
5078 (gnus-summary-goto-article gnus-newsgroup-end))
5080 ;; Select next newsgroup automatically if requested.
5081 (gnus-summary-jump-to-group gnus-newsgroup-name)
5082 (let ((cmd (aref (this-command-keys) 0))
5083 (group (gnus-summary-search-group nil gnus-keep-same-level))
5085 (and gnus-auto-select-next
5086 ;;(null (gnus-set-difference gnus-newsgroup-unreads
5087 ;; gnus-newsgroup-marked))
5089 '(gnus-summary-next-unread-article
5090 gnus-summary-next-article
5091 gnus-summary-next-page
5092 gnus-summary-next-same-subject
5093 gnus-summary-next-unread-same-subject
5094 gnus-summary-kill-same-subject
5095 gnus-summary-kill-same-subject-and-select
5097 ;; Ignore characters typed ahead.
5098 (not (input-pending-p))
5100 ;; Keep just the event type of CMD.
5102 (setq cmd (car cmd)))
5103 (message "No more%s articles%s"
5104 (if unread " unread" "")
5105 (if (and auto-select
5106 (not (eq gnus-auto-select-next 'quietly)))
5108 (format " (Type %s for %s [%s])"
5109 (single-key-description cmd)
5112 group gnus-newsrc-hashtb)))
5113 (format " (Type %s to exit %s)"
5114 (single-key-description cmd)
5115 gnus-newsgroup-name))
5117 ;; Select next unread newsgroup automagically.
5118 (cond ((and auto-select
5119 (eq gnus-auto-select-next 'quietly))
5121 (gnus-summary-next-group))
5123 ;; Confirm auto selection.
5124 (let* ((event (read-event))
5129 (if (and (eq event type) (eq event cmd))
5130 (gnus-summary-next-group)
5131 (setq unread-command-events (list event)))))
5136 (defun gnus-summary-next-unread-article ()
5137 "Select unread article after current one."
5139 (gnus-summary-next-article t (and gnus-auto-select-same
5140 (gnus-summary-subject-string)))
5141 (gnus-summary-position-cursor))
5143 (defun gnus-summary-prev-article (unread &optional subject)
5144 "Select article before current one.
5145 If argument UNREAD is non-nil, only unread article is selected."
5148 (cond ((gnus-summary-display-article
5149 (gnus-summary-search-backward unread subject)))
5151 gnus-auto-select-same
5152 (gnus-set-difference gnus-newsgroup-unreads
5153 (append gnus-newsgroup-marked
5154 gnus-newsgroup-dormant))
5156 '(gnus-summary-prev-unread-article
5157 ;;gnus-summary-prev-page
5158 ;;gnus-summary-prev-article
5159 ;;gnus-summary-prev-same-subject
5160 ;;gnus-summary-prev-unread-same-subject
5162 ;; Ignore given SUBJECT, and try again.
5163 (gnus-summary-prev-article unread nil))
5165 (message "No more unread articles"))
5166 ((and gnus-auto-extend-newsgroup
5167 (not subject) ;Only if subject is not specified.
5168 (setq header (gnus-more-header-backward)))
5169 ;; Extend to previous article if possible.
5170 ;; Basic ideas by himacdonald@watdragon.waterloo.edu
5171 (gnus-extend-newsgroup header t)
5172 (let ((buffer-read-only nil))
5173 (goto-char (point-min))
5174 (gnus-summary-prepare-threads (list header) 0))
5175 (gnus-summary-goto-article gnus-newsgroup-begin)
5176 (gnus-summary-position-cursor))
5178 (message "No more articles"))
5181 (defun gnus-summary-prev-unread-article ()
5182 "Select unred article before current one."
5184 (gnus-summary-prev-article t (and gnus-auto-select-same
5185 (gnus-summary-subject-string))))
5187 (defun gnus-summary-next-page (lines &optional circular)
5188 "Show next page of selected article.
5189 If end of article, select next article.
5190 Argument LINES specifies lines to be scrolled up.
5191 If CIRCULAR is non-nil, go to the start of the article instead of
5192 instead of selecting the next article when reaching the end of the
5195 (setq gnus-summary-buffer (current-buffer))
5196 (let ((article (gnus-summary-article-number))
5198 (if (or (null gnus-current-article)
5199 (null gnus-article-current)
5200 (/= article (cdr gnus-article-current))
5201 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5202 ;; Selected subject is different from current article's.
5203 (gnus-summary-display-article article)
5204 (gnus-configure-windows 'article)
5205 (pop-to-buffer gnus-summary-buffer)
5206 (gnus-eval-in-buffer-window gnus-article-buffer
5207 (setq endp (gnus-article-next-page lines)))
5210 (gnus-summary-beginning-of-article))
5212 (message "End of message"))
5214 (gnus-summary-next-unread-article))))
5215 (gnus-summary-position-cursor))))
5217 (defun gnus-summary-prev-page (lines)
5218 "Show previous page of selected article.
5219 Argument LINES specifies lines to be scrolled down."
5221 (let ((article (gnus-summary-article-number)))
5222 (if (or (null gnus-current-article)
5223 (null gnus-article-current)
5224 (/= article (cdr gnus-article-current))
5225 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5226 ;; Selected subject is different from current article's.
5227 (gnus-summary-display-article article)
5228 (gnus-configure-windows 'article)
5229 (pop-to-buffer gnus-summary-buffer)
5230 (gnus-eval-in-buffer-window gnus-article-buffer
5231 (gnus-article-prev-page lines))
5232 (gnus-summary-position-cursor))))
5234 (defun gnus-summary-scroll-up (lines)
5235 "Scroll up (or down) one line current article.
5236 Argument LINES specifies lines to be scrolled up (or down if negative)."
5238 (gnus-summary-select-article)
5239 (gnus-eval-in-buffer-window gnus-article-buffer
5241 (if (gnus-article-next-page lines)
5242 (message "End of message")))
5244 (gnus-article-prev-page (- 0 lines))))
5247 (defun gnus-summary-next-same-subject ()
5248 "Select next article which has the same subject as current one."
5250 (gnus-summary-next-article nil (gnus-summary-subject-string)))
5252 (defun gnus-summary-prev-same-subject ()
5253 "Select previous article which has the same subject as current one."
5255 (gnus-summary-prev-article nil (gnus-summary-subject-string)))
5257 (defun gnus-summary-next-unread-same-subject ()
5258 "Select next unread article which has the same subject as current one."
5260 (gnus-summary-next-article t (gnus-summary-subject-string)))
5262 (defun gnus-summary-prev-unread-same-subject ()
5263 "Select previous unread article which has the same subject as current one."
5265 (gnus-summary-prev-article t (gnus-summary-subject-string)))
5267 (defun gnus-summary-first-unread-article ()
5268 "Select the first unread article.
5269 Return nil if there are no unread articles."
5271 (if (gnus-summary-first-subject t)
5272 (gnus-summary-display-article (gnus-summary-article-number))))
5274 (defun gnus-summary-goto-article (article &optional all-headers)
5275 "Fetch ARTICLE and display it if it exists.
5276 If ALL-HEADERS is non-nil, no header lines are hidden."
5280 (completing-read "Article number: "
5284 (int-to-string (header-number headers))))
5285 gnus-newsgroup-headers)
5286 nil 'require-match))))
5287 (if (gnus-summary-goto-subject article)
5288 (gnus-summary-display-article article all-headers)))
5290 (defun gnus-summary-goto-last-article ()
5291 "Go to last subject line."
5293 (if gnus-last-article
5294 (gnus-summary-goto-article gnus-last-article)))
5297 ;; Summary article oriented commands
5299 (defun gnus-summary-refer-parent-article ()
5300 "Refer parent article of current article."
5302 (let ((ref (header-references (gnus-get-header-by-number
5303 (gnus-summary-article-number))))
5305 (if (or (not ref) (equal ref ""))
5306 (error "No references in this article"))
5307 (and (string-match "<[^<>]*>[ \t]*$" ref)
5309 (substring ref (match-beginning 0) (match-end 0))))
5310 (if (stringp parent)
5311 (gnus-summary-refer-article parent)
5312 (error "Possibly malformed references"))))
5314 (defun gnus-summary-refer-article (message-id)
5315 "Refer article specified by MESSAGE-ID.
5316 NOTE: This command only works with newsgroup that use NNTP."
5317 (interactive "sMessage-ID: ")
5318 ;; Make sure that this command depends on the fact that article
5319 ;; related information is not updated when an article is retrieved
5321 (gnus-summary-select-article t) ;Request all headers.
5322 (if (and (stringp message-id)
5323 (> (length message-id) 0))
5324 (let ((current (header-id gnus-current-headers)))
5325 (gnus-eval-in-buffer-window
5327 ;; Construct the correct Message-ID if necessary.
5328 ;; Suggested by tale@pawl.rpi.edu.
5329 (or (string-match "^<" message-id)
5330 (setq message-id (concat "<" message-id)))
5331 (or (string-match ">$" message-id)
5332 (setq message-id (concat message-id ">"))))))
5333 (if (and (stringp message-id)
5334 (gnus-article-prepare message-id nil (gnus-read-header message-id)))
5336 (gnus-summary-insert-line
5337 nil gnus-current-headers 0 nil ?D nil nil
5338 (header-subject gnus-current-headers))
5340 (gnus-summary-position-cursor)
5341 (run-hooks 'gnus-summary-update-hook)
5343 (error "No such references")))
5345 (defun gnus-summary-next-digest (nth)
5346 "Move to head of NTH next digested message."
5348 (gnus-summary-select-article)
5349 (gnus-eval-in-buffer-window gnus-article-buffer
5350 (gnus-article-next-digest (or nth 1))
5353 (defun gnus-summary-prev-digest (nth)
5354 "Move to head of NTH previous digested message."
5356 (gnus-summary-select-article)
5357 (gnus-eval-in-buffer-window gnus-article-buffer
5358 (gnus-article-prev-digest (or nth 1))
5361 (defun gnus-summary-rmail-digest ()
5362 "Run RMAIL on current digest article.
5363 gnus-select-digest-hook will be called with no arguments, if that
5364 value is non-nil. It is possible to modify the article so that Rmail
5366 gnus-rmail-digest-hook will be called with no arguments, if that value
5367 is non-nil. The hook is intended to customize Rmail mode."
5369 (gnus-summary-select-article)
5371 (let ((artbuf gnus-article-buffer)
5372 (digbuf (get-buffer-create gnus-digest-buffer))
5373 (mail-header-separator ""))
5375 (gnus-add-current-to-buffer-list)
5376 (buffer-disable-undo (current-buffer))
5377 (setq buffer-read-only nil)
5379 (insert-buffer-substring artbuf)
5380 (run-hooks 'gnus-select-digest-hook)
5381 (gnus-convert-article-to-rmail)
5382 (goto-char (point-min))
5383 ;; Rmail initializations.
5384 (rmail-insert-rmail-file-header)
5386 (rmail-set-message-counters)
5387 (rmail-show-message)
5390 (undigestify-rmail-message)
5391 (rmail-expunge) ;Delete original message.
5392 ;; File name is meaningless but `save-buffer' requires it.
5393 (setq buffer-file-name "Gnus Digest")
5394 (setq mode-line-buffer-identification
5396 (header-subject gnus-current-headers)))
5397 ;; There is no need to write this buffer to a file.
5398 (make-local-variable 'write-file-hooks)
5399 (setq write-file-hooks
5401 (set-buffer-modified-p nil)
5402 (message "(No changes need to be saved)")
5403 'no-need-to-write-this-buffer)))
5404 ;; Default file name saving digest messages.
5405 (setq rmail-default-rmail-file
5406 (funcall gnus-rmail-save-name gnus-newsgroup-name
5407 gnus-current-headers gnus-newsgroup-last-rmail))
5408 (setq rmail-default-file
5409 (funcall gnus-mail-save-name gnus-newsgroup-name
5410 gnus-current-headers gnus-newsgroup-last-mail))
5411 ;; Prevent generating new buffer named ***<N> each time.
5412 (setq rmail-summary-buffer
5413 (get-buffer-create gnus-digest-summary-buffer))
5414 (run-hooks 'gnus-rmail-digest-hook)
5415 ;; Take all windows safely.
5416 (gnus-configure-windows '(1 0 0))
5417 (pop-to-buffer gnus-group-buffer)
5418 ;; Use Summary Article windows for Digest summary and
5420 (if gnus-digest-show-summary
5421 (let ((gnus-summary-buffer gnus-digest-summary-buffer)
5422 (gnus-article-buffer gnus-digest-buffer))
5423 (gnus-configure-windows 'article)
5424 (pop-to-buffer gnus-digest-buffer)
5426 (pop-to-buffer gnus-digest-summary-buffer)
5427 (message (substitute-command-keys
5428 "Type \\[rmail-summary-quit] to return to Gnus")))
5429 (let ((gnus-summary-buffer gnus-digest-buffer))
5430 (gnus-configure-windows 'summary)
5431 (pop-to-buffer gnus-digest-buffer)
5432 (message (substitute-command-keys
5433 "Type \\[rmail-quit] to return to Gnus")))
5435 ;; Move the buffers to the end of buffer list.
5436 (bury-buffer gnus-article-buffer)
5437 (bury-buffer gnus-group-buffer)
5438 (bury-buffer gnus-digest-summary-buffer)
5439 (bury-buffer gnus-digest-buffer))
5440 (error (set-buffer-modified-p nil)
5441 (kill-buffer digbuf)
5442 ;; This command should not signal an error because the
5443 ;; command is called from hooks.
5444 (ding) (message "Article is not a digest")))
5447 (defun gnus-summary-isearch-article ()
5448 "Do incremental search forward on current article."
5450 (gnus-summary-select-article)
5451 (gnus-eval-in-buffer-window gnus-article-buffer
5454 (defun gnus-summary-search-article-forward (regexp)
5455 "Search for an article containing REGEXP forward.
5456 gnus-select-article-hook is not called during the search."
5459 (concat "Search forward (regexp): "
5460 (if gnus-last-search-regexp
5461 (concat "(default " gnus-last-search-regexp ") "))))))
5462 (if (string-equal regexp "")
5463 (setq regexp (or gnus-last-search-regexp ""))
5464 (setq gnus-last-search-regexp regexp))
5465 (if (gnus-summary-search-article regexp nil)
5466 (gnus-eval-in-buffer-window gnus-article-buffer
5470 (error "Search failed: \"%s\"" regexp)
5473 (defun gnus-summary-search-article-backward (regexp)
5474 "Search for an article containing REGEXP backward.
5475 gnus-select-article-hook is not called during the search."
5478 (concat "Search backward (regexp): "
5479 (if gnus-last-search-regexp
5480 (concat "(default " gnus-last-search-regexp ") "))))))
5481 (if (string-equal regexp "")
5482 (setq regexp (or gnus-last-search-regexp ""))
5483 (setq gnus-last-search-regexp regexp))
5484 (if (gnus-summary-search-article regexp t)
5485 (gnus-eval-in-buffer-window gnus-article-buffer
5489 (error "Search failed: \"%s\"" regexp)
5492 (defun gnus-summary-search-article (regexp &optional backward)
5493 "Search for an article containing REGEXP.
5494 Optional argument BACKWARD means do search for backward.
5495 gnus-select-article-hook is not called during the search."
5496 (let ((gnus-select-article-hook nil) ;Disable hook.
5497 (gnus-mark-article-hook nil) ;Inhibit marking as read.
5500 (function re-search-backward) (function re-search-forward)))
5503 ;; Hidden thread subtrees must be searched for ,too.
5504 (gnus-summary-show-all-threads)
5505 ;; First of all, search current article.
5506 ;; We don't want to read article again from NNTP server nor reset
5508 (gnus-summary-select-article)
5509 (message "Searching article: %d..." gnus-current-article)
5510 (setq last gnus-current-article)
5511 (gnus-eval-in-buffer-window gnus-article-buffer
5514 ;; Begin search from current point.
5515 (setq found (funcall re-search regexp nil t))))
5516 ;; Then search next articles.
5517 (while (and (not found)
5518 (gnus-summary-display-article
5519 (gnus-summary-search-subject backward nil nil)))
5520 (message "Searching article: %d..." gnus-current-article)
5521 (gnus-eval-in-buffer-window gnus-article-buffer
5524 (goto-char (if backward (point-max) (point-min)))
5525 (setq found (funcall re-search regexp nil t)))
5528 ;; Adjust article pointer.
5529 (or (eq last gnus-current-article)
5530 (setq gnus-last-article last))
5531 ;; Return T if found such article.
5535 (defun gnus-summary-execute-command (field regexp command &optional backward)
5536 "If FIELD of article header matches REGEXP, execute a COMMAND string.
5537 If FIELD is an empty string (or nil), entire article body is searched for.
5538 If optional (prefix) argument BACKWARD is non-nil, do backward instead."
5540 (list (let ((completion-ignore-case t))
5541 (completing-read "Field name: "
5542 '(("Number")("Subject")("From")
5543 ("Lines")("Date")("Id")
5544 ("Xref")("References"))
5545 nil 'require-match))
5546 (read-string "Regexp: ")
5547 (read-key-sequence "Command: ")
5548 current-prefix-arg))
5549 ;; Hidden thread subtrees must be searched for ,too.
5550 (gnus-summary-show-all-threads)
5551 ;; We don't want to change current point nor window configuration.
5553 (save-window-excursion
5554 (message "Executing %s..." (key-description command))
5555 ;; We'd like to execute COMMAND interactively so as to give arguments.
5556 (gnus-execute field regexp
5558 (call-interactively '(, (key-binding command)))))
5560 (message "Executing %s... done" (key-description command)))))
5562 (defun gnus-summary-beginning-of-article ()
5563 "Scroll the article back to the beginning."
5565 (gnus-summary-select-article)
5566 (gnus-eval-in-buffer-window gnus-article-buffer
5568 (goto-char (point-min))
5569 (if gnus-break-pages
5570 (gnus-narrow-to-page))
5573 (defun gnus-summary-end-of-article ()
5574 "Scroll to the end of the article."
5576 (gnus-summary-select-article)
5577 (gnus-eval-in-buffer-window gnus-article-buffer
5579 (goto-char (point-max))
5580 (if gnus-break-pages
5581 (gnus-narrow-to-page))
5584 (defun gnus-summary-show-article ()
5585 "Force re-fetching of the current article."
5587 (gnus-summary-select-article gnus-have-all-headers t))
5589 (defun gnus-summary-toggle-header (arg)
5590 "Show the headers if they are hidden, or hide them if they are shown.
5591 If ARG is a positive number, show the entire header.
5592 If ARG is a negative number, hide the unwanted header lines."
5595 (set-buffer gnus-article-buffer)
5596 (let ((buffer-read-only nil))
5598 (if (> arg 0) (remove-text-properties 1 (point-max) '(invisible t))
5599 (if (< arg 0) (run-hooks 'gnus-article-display-hook)))
5600 (if (text-property-any 1 (point-max) 'invisible t)
5601 (remove-text-properties 1 (point-max) '(invisible t))
5602 (run-hooks 'gnus-article-display-hook))))))
5604 (defun gnus-summary-show-all-headers ()
5605 "Make all header lines visible."
5607 (gnus-article-show-all-headers))
5609 (defun gnus-summary-toggle-mime (arg)
5610 "Toggle MIME processing.
5611 If ARG is a positive number, turn MIME processing on."
5613 (setq gnus-show-mime
5614 (if (null arg) (not gnus-show-mime)
5615 (> (prefix-numeric-value arg) 0)))
5616 (gnus-summary-select-article t 'force))
5618 (defun gnus-summary-caesar-message (rotnum)
5619 "Caesar rotates all letters of current message by 13/47 places.
5620 With prefix arg, specifies the number of places to rotate each letter forward.
5621 Caesar rotates Japanese letters by 47 places in any case."
5623 (gnus-summary-select-article)
5624 (gnus-overload-functions)
5625 (gnus-eval-in-buffer-window gnus-article-buffer
5628 ;; We don't want to jump to the beginning of the message.
5629 ;; `save-excursion' does not do its job.
5630 (move-to-window-line 0)
5631 (let ((last (point)))
5632 (news-caesar-buffer-body rotnum)
5638 (defun gnus-summary-stop-page-breaking ()
5639 "Stop page breaking in the current article."
5641 (gnus-summary-select-article)
5642 (gnus-eval-in-buffer-window gnus-article-buffer (widen)))
5644 ;; Suggested by Brian Edmonds <bedmonds@prodigy.bc.ca>.
5646 (defun gnus-summary-move-article (n &optional to-newsgroup select-method)
5647 "Move the current article to a different newsgroup.
5648 If N is a positive number, move the N next articles.
5649 If N is a negative number, move the N previous articles.
5650 If N is nil and any articles have been marked with the process mark,
5651 move those articles instead.
5652 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to.
5653 If SELECT-METHOD is symbol, do not move to a specific newsgroup, but
5654 re-spool using this method.
5655 For this function to work, both the current newsgroup and the
5656 newsgroup that you want to move to have to support the `request-move'
5657 and `request-accept' functions. (Ie. mail newsgroups at present.)"
5659 (or (gnus-check-backend-function 'request-move-article gnus-newsgroup-name)
5660 (error "The current newsgroup does not support article moving"))
5661 (let (articles art-group)
5662 (if (and n (numberp n))
5663 (let ((backward (< n 0))
5667 (setq articles (cons (gnus-summary-article-number)
5669 (gnus-summary-search-forward nil nil backward))
5671 (setq articles (sort articles (function <))))
5672 (setq articles (or (setq gnus-newsgroup-processable
5673 (sort gnus-newsgroup-processable (function <)))
5674 (list (gnus-summary-article-number)))))
5675 (if (and (not to-newsgroup) (not select-method))
5678 (format "Where do you want to move %s? "
5679 (if (> (length articles) 1)
5680 (format "these %d articles" (length articles))
5682 gnus-active-hashtb nil t)))
5683 (or (gnus-check-backend-function 'request-accept-article
5684 (or select-method to-newsgroup))
5685 (error "%s does not support article moving" to-newsgroup))
5686 (message "Moving to %s: %s..." (or select-method to-newsgroup) articles)
5689 (gnus-request-move-article
5691 gnus-newsgroup-name (nth 1 gnus-current-select-method)
5692 (list 'gnus-request-accept-article
5693 (or select-method to-newsgroup))))
5694 (let* ((buffer-read-only nil)
5696 (gnus-gethash (car art-group) gnus-newsrc-hashtb)
5697 (gnus-gethash (concat gnus-foreign-group-prefix
5699 gnus-newsrc-hashtb)))
5700 (info (nth 2 entry))
5701 (article (car articles))
5702 (marked (nth 3 info)))
5703 (gnus-summary-goto-subject article)
5704 (delete-region (progn (beginning-of-line) (point))
5705 (progn (forward-line 1) (point)))
5706 (if (not (memq article gnus-newsgroup-unreads))
5707 (setcar (cdr (cdr info))
5708 (gnus-add-to-range (nth 2 info)
5709 (list (cdr art-group)))))
5710 ;; !!! Here one should copy all the marks over to the new
5711 ;; newsgroup, but I couldn't be bothered. nth on that!
5713 (message "Couldn't move article %s" (car articles)))
5714 (setq articles (cdr articles)))))
5716 (defun gnus-summary-respool-article (n &optional respool-method)
5717 "Respool the current article.
5718 The article will be squeezed through the mail spooling process again,
5719 which means that it will be put in some mail newsgroup or other
5720 depending on `nnmail-split-methods'.
5721 If N is a positive number, respool the N next articles.
5722 If N is a negative number, respool the N previous articles.
5723 If N is nil and any articles have been marked with the process mark,
5724 respool those articles instead.
5725 For this function to work, both the current newsgroup and the
5726 newsgroup that you want to move to have to support the `request-move'
5727 and `request-accept' functions. (Ie. mail newsgroups at present.)"
5730 (setq respool-method
5732 "What method do you want to use when respooling? "
5733 (gnus-methods-using 'respool) nil t)))
5734 (gnus-summary-move-article n nil respool-method))
5736 ;; Summary interest commands.
5738 ;; Suggested by boubaker@cenatls.cena.dgac.fr.
5740 (defun gnus-summary-raise-interest (n)
5741 "Raise the interest of the current article by N."
5743 (gnus-summary-set-interest (+ (gnus-summary-interest) n)))
5745 (defun gnus-summary-lower-interest (n)
5746 "Lower the interest of the current article by N."
5748 (gnus-summary-raise-interest (- n)))
5750 (defun gnus-summary-set-interest (n)
5751 "Set the interest of the current article to N."
5753 ;; Skip dummy header line.
5755 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5756 (let ((buffer-read-only nil)
5758 ;; Set visible interest.
5762 (insert (int-to-string (max 1 (min 9 n))))
5763 ;; Set invisible interest.
5764 (re-search-forward " [-0-9]+ . [-0-9]+ [0-9]+[\n\r]")
5765 (goto-char (1+ (match-beginning 0)))
5766 (delete-region (setq beg (point)) (progn (search-forward " ")
5769 (insert (int-to-string n))
5770 (set-text-properties beg (point) '(invisible t)))
5771 (run-hooks 'gnus-summary-update-hook)))
5773 (defmacro gnus-raise (field expression level)
5774 (` (gnus-kill (, field) (, expression)
5775 (function (gnus-summary-raise-interest (, level))) t)))
5777 (defmacro gnus-lower (field expression level)
5778 (` (gnus-kill (, field) (, expression)
5779 (function (gnus-summary-raise-interest (- (, level)))) t)))
5781 ;; Summary marking commands.
5783 (defun gnus-summary-kill-same-subject-and-select (unmark)
5784 "Mark articles which has the same subject as read, and then select the next.
5785 If UNMARK is positive, remove any kind of mark.
5786 If UNMARK is negative, tick articles."
5789 (setq unmark (prefix-numeric-value unmark)))
5791 (gnus-summary-mark-same-subject
5792 (gnus-summary-subject-string) unmark)))
5793 ;; Select next unread article. If auto-select-same mode, should
5794 ;; select the first unread article.
5795 (gnus-summary-next-article t (and gnus-auto-select-same
5796 (gnus-summary-subject-string)))
5797 (message "%d articles are marked as %s"
5798 count (if unmark "unread" "read"))
5801 (defun gnus-summary-kill-same-subject (unmark)
5802 "Mark articles which has the same subject as read.
5803 If UNMARK is positive, remove any kind of mark.
5804 If UNMARK is negative, tick articles."
5807 (setq unmark (prefix-numeric-value unmark)))
5809 (gnus-summary-mark-same-subject
5810 (gnus-summary-subject-string) unmark)))
5811 ;; If marked as read, go to next unread subject.
5813 ;; Go to next unread subject.
5814 (gnus-summary-next-subject 1 t))
5815 (message "%d articles are marked as %s"
5816 count (if unmark "unread" "read"))
5819 (defun gnus-summary-mark-same-subject (subject &optional unmark)
5820 "Mark articles with same SUBJECT as read, and return marked number.
5821 If optional argument UNMARK is positive, remove any kinds of marks.
5822 If optional argument UNMARK is negative, mark articles as unread instead."
5825 (cond ((null unmark)
5826 (gnus-summary-mark-as-read nil gnus-killed-mark))
5828 (gnus-summary-tick-article nil t))
5830 (gnus-summary-tick-article)))
5832 (gnus-summary-search-forward nil subject))
5833 (cond ((null unmark)
5834 (gnus-summary-mark-as-read nil gnus-killed-mark))
5836 (gnus-summary-tick-article nil t))
5838 (gnus-summary-tick-article)))
5839 (setq count (1+ count))
5841 ;; Hide killed thread subtrees. Does not work properly always.
5842 ;;(and (null unmark)
5843 ;; gnus-thread-hide-killed
5844 ;; (gnus-summary-hide-thread))
5845 ;; Return number of articles marked as read.
5849 (defun gnus-summary-mark-as-processable (n &optional unmark)
5850 "Set the process mark on the next N articles.
5851 If N is negative, mark backward instead. If UNMARK is non-nil, remove
5852 the process mark instead. The difference between N and the actual
5853 number of articles marked is returned."
5855 (let ((backward (< n 0))
5859 (gnus-summary-remove-process-mark
5860 (gnus-summary-article-number))
5861 (gnus-summary-set-process-mark
5862 (gnus-summary-article-number)))
5863 (= 0 (gnus-summary-next-subject (if backward -1 1))))
5865 (if (/= 0 n) (message "No more articles"))
5868 (defun gnus-summary-unmark-as-processable (n)
5869 "Remove the process mark from the next N articles.
5870 If N is negative, mark backward instead. The difference between N and
5871 the actual number of articles marked is returned."
5873 (gnus-summary-mark-as-processable n t))
5875 (defun gnus-summary-unmark-all-processable ()
5876 "Remove the process mark from all articles."
5879 (while gnus-newsgroup-processable
5880 (gnus-summary-remove-process-mark (car gnus-newsgroup-processable))))
5881 (gnus-summary-position-cursor))
5883 (defun gnus-summary-mark-as-expirable (n &optional unmark)
5884 "Mark N articles forward as expirable.
5885 If N is negative, mark backward instead. If UNMARK is non-nil, remove
5886 the expirably mark instead. The difference between N and the actual
5887 number of articles marked is returned."
5889 (let ((backward (< n 0))
5893 (gnus-summary-remove-expirable-mark
5894 (gnus-summary-article-number))
5895 (gnus-summary-set-expirable-mark
5896 (gnus-summary-article-number)))
5897 (= 0 (gnus-summary-next-subject (if backward -1 1))))
5899 (if (/= 0 n) (message "No more articles"))
5902 (defun gnus-summary-unmark-as-expirable (n)
5903 "Mark N articles forward as expirable.
5904 If N is negative, mark backward instead. The difference between N and
5905 the actual number of articles marked is returned."
5907 (gnus-summary-mark-as-expirable n t))
5909 (defun gnus-summary-set-expirable-mark (article)
5910 "Mark the current article as expirable and update the Summary line."
5911 (setq gnus-newsgroup-expirable (cons article gnus-newsgroup-expirable))
5912 (let ((buffer-read-only nil))
5913 (if (gnus-summary-goto-subject article)
5915 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5920 (run-hooks 'gnus-summary-update-hook)
5923 (defun gnus-summary-remove-expirable-mark (article)
5924 "Remove the expirable mark from ARTICLE as expirable and update the Summary line."
5925 (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable))
5926 (let ((buffer-read-only nil))
5927 (if (gnus-summary-goto-subject article)
5929 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
5934 (if (memq article gnus-newsgroup-processable) ?# ? ))
5935 (run-hooks 'gnus-summary-update-hook)
5938 (defun gnus-summary-expire-articles ()
5939 "Expire all articles that are marked as expirable in the current group."
5941 (if (and gnus-newsgroup-expirable
5942 (gnus-check-backend-function
5943 'gnus-request-expire-articles gnus-newsgroup-name))
5944 (setq gnus-newsgroup-expirable
5945 (gnus-request-expire-articles gnus-newsgroup-expirable
5946 gnus-newsgroup-name))))
5948 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
5949 (defun gnus-summary-delete-article (n)
5950 "Delete the N next (mail) articles.
5951 This command actually deletes articles. This is not a marking
5952 command. The article will disappear forever from you life, never to
5954 If N is negative, delete backwards.
5955 If N is nil and articles have been marked with the process mark,
5956 delete these instead."
5958 (or (gnus-check-backend-function 'request-expire-articles
5959 gnus-newsgroup-name)
5960 (error "The current newsgroup does not support article deletion."))
5961 ;; Compute the list of articles to delete.
5963 (if (and n (numberp n))
5964 (let ((backward (< n 0))
5968 (setq articles (cons (gnus-summary-article-number)
5970 (gnus-summary-search-forward nil nil backward))
5972 (setq articles (sort articles (function <))))
5973 (setq articles (or (setq gnus-newsgroup-processable
5974 (sort gnus-newsgroup-processable (function <)))
5975 (list (gnus-summary-article-number)))))
5976 (if (and gnus-novice-user
5978 (format "Do you really want to delete %s forever?"
5979 (if (> (length articles) 1) "these articles"
5982 ;; Delete the articles.
5983 (setq gnus-newsgroup-expirable
5984 (gnus-request-expire-articles
5985 articles gnus-newsgroup-name 'force)))))
5987 (defun gnus-summary-mark-article-as-replied (article)
5988 "Mark ARTICLE replied and update the Summary line."
5989 (setq gnus-newsgroup-replied (cons article gnus-newsgroup-replied))
5990 (let ((buffer-read-only nil))
5991 (if (gnus-summary-goto-subject article)
5999 (defun gnus-summary-set-bookmark (article)
6000 "Set a bookmark in current article."
6001 (interactive (list (gnus-summary-article-number)))
6002 (if (or (not (get-buffer gnus-article-buffer))
6003 (not gnus-current-article)
6004 (not gnus-article-current)
6005 (not (equal gnus-newsgroup-name (car gnus-article-current))))
6006 (error "No current article selected"))
6007 ;; Remove old bookmark, if one exists.
6008 (let ((old (assq article gnus-newsgroup-bookmarks)))
6009 (if old (setq gnus-newsgroup-bookmarks
6010 (delq old gnus-newsgroup-bookmarks))))
6011 ;; Set the new bookmark, which is on the form
6012 ;; (article-number . line-number-in-body).
6013 (setq gnus-newsgroup-bookmarks
6017 (set-buffer gnus-article-buffer)
6022 (search-forward "\n\n" nil t)
6025 gnus-newsgroup-bookmarks))
6026 (message "A bookmark has been added to the current article."))
6028 (defun gnus-summary-remove-bookmark (article)
6029 "Remove the bookmark from the current article."
6030 (interactive (list (gnus-summary-article-number)))
6031 ;; Remove old bookmark, if one exists.
6032 (let ((old (assq article gnus-newsgroup-bookmarks)))
6035 (setq gnus-newsgroup-bookmarks
6036 (delq old gnus-newsgroup-bookmarks))
6037 (message "Removed bookmark."))
6038 (message "No bookmark in current article."))))
6040 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
6041 (defun gnus-summary-mark-as-dormant (n)
6042 "Mark N articles forward as dormant.
6043 If N is negative, mark backward instead. The difference between N and
6044 the actual number of articles marked is returned."
6046 (gnus-summary-mark-forward n gnus-dormant-mark))
6048 (defun gnus-summary-set-process-mark (article)
6049 "Set the process mark on ARTICLE and update the Summary line."
6050 (setq gnus-newsgroup-processable (cons article gnus-newsgroup-processable))
6051 (let ((buffer-read-only nil))
6052 (if (gnus-summary-goto-subject article)
6054 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
6059 (run-hooks 'gnus-summary-update-hook)
6062 (defun gnus-summary-remove-process-mark (article)
6063 "Remove the process mark from ARTICLE and update the Summary line."
6064 (setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable))
6065 (let ((buffer-read-only nil))
6066 (if (gnus-summary-goto-subject article)
6068 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
6073 (if (memq article gnus-newsgroup-expirable) ?X ? ))
6074 (run-hooks 'gnus-summary-update-hook)
6077 (defun gnus-summary-mark-forward (n &optional unread)
6078 "Mark N articles as read forwards.
6079 If N is negative, mark backwards instead.
6080 If UNREAD is non-nil, mark articles as unread. In that case, UNREAD
6081 must either be \" \", \"-\" or \"I\".
6082 The difference between N and the actual number of articles marked is
6085 (let ((backward (< n 0))
6088 (gnus-summary-mark-article nil unread)
6089 (= 0 (gnus-summary-next-subject (if backward -1 1))))
6091 (if (/= 0 n) (message "No more %sarticles" (if unread "" "unread ")))
6092 (gnus-set-mode-line 'summary)
6095 (defun gnus-summary-mark-article (&optional article mark)
6096 "Mark ARTICLE with MARK.
6097 MARK can be any string (but it should just be one character long).
6098 Four MARK strings are reserved: \" \" (unread),
6099 \"-\" (ticked), \"I\" (dormant), \"D\" (read).
6100 If MARK is nil, then the default string \"D\" is used.
6101 If ARTICLE is nil, then the article on the current line will be
6103 (let* ((buffer-read-only nil)
6104 (mark (or mark "D"))
6105 (article (or article (gnus-summary-article-number))))
6106 (if (numberp mark) (setq mark (format "%c" mark)))
6108 (if (gnus-summary-goto-subject article)
6110 (gnus-summary-show-thread)
6112 (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
6113 ;; Fix the invisible mark.
6114 (re-search-forward ". [-0-9]+ [0-9]+[\n\r]")
6115 (goto-char (match-beginning 0))
6118 (set-text-properties (1- (point)) (point) '(invisible t))
6119 ;; Fix the visible mark.
6123 (run-hooks 'gnus-summary-update-hook)
6125 (if (or (string= mark gnus-unread-mark)
6126 (string= mark gnus-ticked-mark)
6127 (string= mark gnus-dormant-mark))
6128 (gnus-mark-article-as-unread article mark)
6129 (gnus-mark-article-as-read article)))))
6131 (defun gnus-mark-article-as-read (article)
6132 "Remember that ARTICLE is marked as read."
6133 ;; Make the article expirable.
6134 (if gnus-newsgroup-auto-expire
6135 (gnus-summary-set-expirable-mark article))
6136 ;; Remove from unread and marked list.
6137 (setq gnus-newsgroup-unreads
6138 (delq article gnus-newsgroup-unreads))
6139 (setq gnus-newsgroup-marked
6140 (delq article gnus-newsgroup-marked))
6141 (setq gnus-newsgroup-dormant
6142 (delq article gnus-newsgroup-dormant)))
6144 (defun gnus-mark-article-as-unread (article &optional mark)
6145 "Remember that ARTICLE is marked as unread.
6146 MARK is the mark type: \" \", \"-\" or \"I\"."
6147 ;; Add to unread list.
6148 (or (memq article gnus-newsgroup-unreads)
6149 (setq gnus-newsgroup-unreads
6150 (cons article gnus-newsgroup-unreads)))
6151 ;; Update the expired list.
6152 (gnus-summary-remove-expirable-mark article)
6153 ;; If CLEAR-MARK is non-nil, the article must be removed from marked
6154 ;; list. Otherwise, it must be added to the list.
6155 (setq gnus-newsgroup-marked
6156 (delq article gnus-newsgroup-marked))
6157 (setq gnus-newsgroup-dormant
6158 (delq article gnus-newsgroup-dormant))
6159 (if (equal mark gnus-ticked-mark)
6160 (setq gnus-newsgroup-marked
6161 (cons article gnus-newsgroup-marked)))
6162 (if (equal mark gnus-dormant-mark)
6163 (setq gnus-newsgroup-dormant
6164 (cons article gnus-newsgroup-dormant))))
6166 (defalias 'gnus-summary-mark-as-unread-forward
6167 'gnus-summary-tick-article-forward)
6168 (make-obsolete 'gnus-summary-mark-as-unread-forward
6169 'gnus-summary-tick-article--forward)
6170 (defun gnus-summary-tick-article-forward (n)
6171 "Tick N articles forwards.
6172 If N is negative, tick backwards instead.
6173 The difference between N and the number of articles ticked is returned."
6175 (gnus-summary-mark-forward n gnus-ticked-mark))
6177 (defalias 'gnus-summary-mark-as-unread-backward 'gnus-summary-tick-article-backward)
6178 (make-obsolete 'gnus-summary-mark-as-unread-backward 'gnus-summary-tick-article-backward)
6179 (defun gnus-summary-tick-article-backward (n)
6180 "Tick N articles backwards.
6181 The difference between N and the number of articles ticked is returned."
6183 (gnus-summary-mark-forward (- n) gnus-ticked-mark))
6185 (defalias 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
6186 (make-obsolete 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
6187 (defun gnus-summary-tick-article (&optional article clear-mark)
6188 "Mark current article as unread.
6189 Optional 1st argument ARTICLE specifies article number to be marked as unread.
6190 Optional 2nd argument CLEAR-MARK remove any kinds of mark."
6191 (gnus-summary-mark-article article (if clear-mark gnus-unread-mark
6194 (defun gnus-summary-mark-as-read-forward (n)
6195 "Mark N articles as read forwards.
6196 If N is negative, mark backwards instead.
6197 The difference between N and the actual number of articles marked is
6200 (gnus-summary-mark-forward n))
6202 (defun gnus-summary-mark-as-read-backward (n)
6203 "Mark the N articles as read backwards.
6204 The difference between N and the actual number of articles marked is
6207 (gnus-summary-mark-forward (- n)))
6209 (defun gnus-summary-mark-as-read (&optional article mark)
6210 "Mark current article as read.
6211 ARTICLE specifies the article to be marked as read.
6212 MARK specifies a string to be inserted at the beginning of the line.
6213 Any kind of string (length 1) except for a space and `-' is ok."
6214 (gnus-summary-mark-article article mark))
6216 (defun gnus-summary-clear-mark-forward (n)
6217 "Clear marks from N articles forward.
6218 If N is negative, clear backward instead.
6219 The difference between N and the number of marks cleared is returned."
6221 (gnus-summary-mark-forward n gnus-unread-mark))
6223 (defun gnus-summary-clear-mark-backward (n)
6224 "Clear marks from N articles backward.
6225 The difference between N and the number of marks cleared is returned."
6227 (gnus-summary-mark-forward (- n) gnus-unread-mark))
6229 ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
6230 (defun gnus-summary-delete-marked-as-read ()
6231 "Delete lines that are marked as read."
6233 (gnus-summary-delete-marked-with
6234 (concat gnus-read-mark gnus-killed-mark gnus-kill-file-mark)))
6236 (defun gnus-summary-delete-marked-with (marks)
6237 "Delete lines that are marked with MARKS (e.g. \"DK\")."
6238 (interactive "sMarks: ")
6239 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
6241 (set-buffer gnus-summary-buffer)
6242 (let ((buffer-read-only nil)
6243 (marks (concat "[" marks "]"))
6245 (goto-char (point-min))
6247 (if (looking-at marks)
6251 ;; Fix by Per Abrahamsen <amanda@iesd.auc.dk>.
6252 (setq gnus-newsgroup-expunged-lines
6253 (concat (or gnus-newsgroup-expunged-lines "")
6254 (buffer-substring beg (point))))
6255 (delete-region beg (point)))
6258 (or (zerop (buffer-size))
6260 (gnus-summary-prev-subject 1)
6261 (gnus-summary-position-cursor)))))
6263 (defun gnus-summary-expunge-below (score)
6264 "Delete articles with score less than SCORE."
6266 (setq score (if score
6267 (prefix-numeric-value score)
6268 gnus-summary-default-interest))
6270 (set-buffer gnus-summary-buffer)
6271 (goto-char (point-min))
6272 (let ((buffer-read-only nil)
6275 (if (< (gnus-summary-interest) score)
6279 (setq gnus-newsgroup-expunged-lines
6280 (buffer-substring beg (point)))
6281 (delete-region beg (point)))
6284 (or (zerop (buffer-size))
6286 (gnus-summary-prev-subject 1)
6287 (gnus-summary-position-cursor))))))
6289 (defun gnus-summary-mark-below (score mark)
6290 "Mark articles with score less than SCORE with MARK."
6291 (interactive "P\ncMark: ")
6292 (setq score (if score
6293 (prefix-numeric-value score)
6294 gnus-summary-default-interest))
6296 (set-buffer gnus-summary-buffer)
6297 (goto-char (point-min))
6299 (if (< (gnus-summary-interest) score)
6301 (gnus-summary-mark-article nil (char-to-string mark))
6303 (forward-line 1)))))
6305 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
6306 (defun gnus-summary-kill-below (score)
6307 "Kill articles with score below SCORE."
6309 (gnus-summary-mark-below score ?K))
6311 (defun gnus-summary-clear-above (score)
6312 "Clear all marks from articles with score above SCORE."
6314 (gnus-summary-mark-above score ? ))
6316 (defun gnus-summary-tick-above (score)
6317 "Tick all articles with score above SCORE."
6319 (gnus-summary-mark-above score ?-))
6321 (defun gnus-summary-mark-above (score mark)
6322 "Mark articles with score less than SCORE with MARK."
6323 (interactive "P\ncMark: ")
6324 (setq score (if score
6325 (prefix-numeric-value score)
6326 gnus-summary-default-interest))
6328 (set-buffer gnus-summary-buffer)
6329 (goto-char (point-min))
6331 (if (> (gnus-summary-interest) score)
6333 (gnus-summary-mark-article nil (char-to-string mark))
6335 (forward-line 1)))))
6337 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
6338 (defun gnus-summary-show-all-expunged ()
6339 "Show all previously expunge articles."
6341 (if (not gnus-newsgroup-expunged-lines)
6342 (error "No lines expunged."))
6343 (let ((buffer-read-only nil))
6344 (goto-char (point-min))
6346 (insert gnus-newsgroup-expunged-lines))
6347 (setq gnus-newsgroup-expunged-lines nil)))
6349 (defun gnus-summary-show-all-dormant ()
6350 "Display all the hidden articles that are marked as dormant."
6352 (let ((int gnus-newsgroup-dormant-subjects)
6353 (buffer-read-only nil))
6355 (error "No dormant articles hidden."))
6356 (goto-char (point-min))
6359 (insert (cdr (car int)))
6360 (setq int (cdr int))))
6361 (gnus-summary-position-cursor)
6362 (setq gnus-newsgroup-dormant-subjects nil)))
6364 (defun gnus-summary-catchup (all &optional quietly to-here)
6365 "Mark all articles not marked as unread in this newsgroup as read.
6366 If prefix argument ALL is non-nil, all articles are marked as read.
6367 If QUIETLY is non-nil, no questions will be asked.
6368 If TO-HERE is non-nil, it should be a point in the buffer. All
6369 articles before this point will be marked as read.
6370 The number of articles marked as read is returned."
6373 (not gnus-interactive-catchup) ;Without confirmation?
6377 "Do you really want to mark everything as read? "
6378 "Delete all articles not marked as unread? ")))
6379 (let ((unreads (length gnus-newsgroup-unreads)))
6380 (if (gnus-summary-first-subject (not all))
6381 (while (and (gnus-summary-mark-as-read nil gnus-catchup-mark)
6382 (if to-here (< (point) to-here) t)
6383 (gnus-summary-search-subject nil (not all)))))
6384 (- unreads (length gnus-newsgroup-unreads)))))
6386 (defun gnus-summary-catchup-to-here (&optional all)
6387 "Mark all unticked articles before the current one as read.
6388 If ALL is non-nil, also mark ticked and dormant articles as read."
6391 (gnus-summary-catchup all nil (point))
6392 (gnus-summary-position-cursor))
6394 (defun gnus-summary-catchup-all (&optional quietly)
6395 "Mark all articles in this newsgroup as read."
6397 (gnus-summary-catchup t quietly))
6399 (defun gnus-summary-catchup-and-exit (all &optional quietly)
6400 "Mark all articles not marked as unread in this newsgroup as read, then exit.
6401 If prefix argument ALL is non-nil, all articles are marked as read."
6403 (gnus-summary-catchup all quietly)
6404 ;; Select next newsgroup or exit.
6405 (if (eq gnus-auto-select-next 'quietly)
6406 (gnus-summary-next-group nil)
6407 (gnus-summary-exit)))
6409 (defun gnus-summary-catchup-all-and-exit (&optional quietly)
6410 "Mark all articles in this newsgroup as read, and then exit."
6412 (gnus-summary-catchup-and-exit t quietly))
6414 ;; Thread-based commands.
6416 (defun gnus-summary-toggle-threads (arg)
6417 "Toggle showing conversation threads.
6418 If ARG is positive number, turn showing conversation threads on."
6420 (let ((current (gnus-summary-article-number)))
6421 (setq gnus-show-threads
6422 (if (null arg) (not gnus-show-threads)
6423 (> (prefix-numeric-value arg) 0)))
6424 (gnus-summary-prepare)
6425 (gnus-summary-goto-subject current)))
6427 (defun gnus-summary-show-all-threads ()
6430 (if gnus-show-threads
6432 (let ((buffer-read-only nil))
6433 (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)))))
6435 (defun gnus-summary-show-thread ()
6436 "Show thread subtrees."
6438 (if gnus-show-threads
6440 (let ((buffer-read-only nil))
6441 (subst-char-in-region
6442 (progn (beginning-of-line) (point))
6443 (progn (end-of-line) (point)) ?\^M ?\n t)))))
6445 (defun gnus-summary-hide-all-threads ()
6446 "Hide all thread subtrees."
6448 (if gnus-show-threads
6450 (goto-char (point-min))
6451 (gnus-summary-hide-thread)
6452 (while (gnus-summary-search-forward)
6453 (gnus-summary-hide-thread)))))
6455 (defun gnus-summary-hide-thread ()
6456 "Hide thread subtrees."
6458 (if gnus-show-threads
6460 (let ((buffer-read-only nil)
6462 (level (gnus-summary-thread-level))
6464 ;; Go forward until either the buffer ends or the subthread
6466 (while (and (= 0 (forward-line 1))
6467 (> (gnus-summary-thread-level) level))
6469 (subst-char-in-region start end ?\n ?\^M t)))))
6471 (defun gnus-summary-go-to-next-thread (&optional previous)
6472 "Go to the same level (or less) next thread.
6473 If PREVIOUS is non-nil, go to previous thread instead.
6474 Return the article number moved to, or nil if moving was impossible."
6475 (let ((level (gnus-summary-thread-level))
6476 (article (gnus-summary-article-number)))
6478 (while (and (zerop (gnus-summary-prev-subject 1))
6479 (> (gnus-summary-thread-level) level)))
6480 (while (and (zerop (gnus-summary-next-subject 1))
6481 (> (gnus-summary-thread-level) level))))
6482 (let ((oart (gnus-summary-article-number)))
6483 (and (/= oart article) oart))))
6485 (defun gnus-summary-next-thread (n)
6486 "Go to the same level next N'th thread.
6487 If N is negative, search backward instead.
6488 Returns the difference between N and the number of skips actually
6491 (let ((backward (< n 0))
6494 (gnus-summary-go-to-next-thread backward))
6496 (gnus-summary-position-cursor)
6497 (if (/= 0 n) (message "No more threads" ))
6500 (defun gnus-summary-prev-thread (n)
6501 "Go to the same level previous N'th thread.
6502 Returns the difference between N and the number of skips actually
6505 (gnus-summary-next-thread (- n)))
6507 (defun gnus-summary-go-down-thread (&optional up same)
6508 "Go down one level in the current thread.
6509 If UP is non-nil, go up instead.
6510 If SAME is non-nil, also move to articles of the same level."
6511 (let ((level (gnus-summary-thread-level))
6513 (level-diff (if up -1 1))
6515 (if (not (and (= 0 (forward-line level-diff))
6516 (or (= (+ level level-diff)
6517 (setq l (gnus-summary-thread-level)))
6518 (and same (= level l)))))
6520 (/= start (point))))
6522 (defun gnus-summary-down-thread (n)
6523 "Go down thread N steps.
6524 If N is negative, go up instead.
6525 Returns the difference between N and how many steps down that were
6531 (gnus-summary-go-down-thread up))
6533 (gnus-summary-position-cursor)
6534 (if (/= 0 n) (message "Can't go further" ))
6537 (defun gnus-summary-up-thread (n)
6538 "Go up thread N steps.
6539 If N is negative, go up instead.
6540 Returns the difference between N and how many steps down that were
6543 (gnus-summary-down-thread (- n)))
6545 (defun gnus-summary-kill-thread (unmark)
6546 "Mark articles under current thread as read.
6547 If the prefix argument is positive, remove any kinds of marks.
6548 If the prefix argument is negative, tick articles instead."
6551 (setq unmark (prefix-numeric-value unmark)))
6553 (level (gnus-summary-thread-level)))
6556 ;; Mark the article...
6557 (cond ((null unmark) (gnus-summary-mark-as-read nil gnus-killed-mark))
6558 ((> unmark 0) (gnus-summary-tick-article nil t))
6559 (t (gnus-summary-tick-article)))
6560 ;; ...and go forward until either the buffer ends or the subtree
6562 (if (not (and (= 0 (forward-line 1))
6563 (> (gnus-summary-thread-level) level)))
6564 (setq killing nil))))
6565 ;; Hide killed subtrees.
6567 gnus-thread-hide-killed
6568 (gnus-summary-hide-thread))
6569 ;; If marked as read, go to next unread subject.
6571 ;; Go to next unread subject.
6572 (gnus-summary-next-subject 1 t)))
6573 (gnus-set-mode-line 'summary))
6575 ;; Summary sorting commands
6577 (defun gnus-summary-sort-by-number (reverse)
6578 "Sort Summary buffer by article number.
6579 Argument REVERSE means reverse order."
6581 (gnus-summary-keysort-summary
6588 (defun gnus-summary-sort-by-author (reverse)
6589 "Sort Summary buffer by author name alphabetically.
6590 If case-fold-search is non-nil, case of letters is ignored.
6591 Argument REVERSE means reverse order."
6593 (gnus-summary-keysort-summary
6594 (function string-lessp)
6596 (if case-fold-search
6597 (downcase (header-from a))
6602 (defun gnus-summary-sort-by-subject (reverse)
6603 "Sort Summary buffer by subject alphabetically. `Re:'s are ignored.
6604 If case-fold-search is non-nil, case of letters is ignored.
6605 Argument REVERSE means reverse order."
6607 (gnus-summary-keysort-summary
6608 (function string-lessp)
6610 (if case-fold-search
6611 (downcase (gnus-simplify-subject (header-subject a) 're-only))
6612 (gnus-simplify-subject (header-subject a) 're-only)))
6616 (defun gnus-summary-sort-by-date (reverse)
6617 "Sort Summary buffer by date.
6618 Argument REVERSE means reverse order."
6620 (gnus-summary-keysort-summary
6621 (function string-lessp)
6623 (gnus-sortable-date (header-date a)))
6627 (defun gnus-summary-keysort-summary (predicate key &optional reverse)
6628 "Sort Summary buffer by PREDICATE using a value passed by KEY.
6629 Optional argument REVERSE means reverse order."
6630 (let ((current (gnus-summary-article-number)))
6631 (gnus-keysort-headers predicate key reverse)
6632 (gnus-summary-prepare)
6633 (gnus-summary-goto-subject current)
6636 (defun gnus-summary-sort-summary (predicate &optional reverse)
6637 "Sort Summary buffer by PREDICATE.
6638 Optional argument REVERSE means reverse order."
6639 (let ((current (gnus-summary-article-number)))
6640 (gnus-sort-headers predicate reverse)
6641 (gnus-summary-prepare)
6642 (gnus-summary-goto-subject current)
6645 ;; Basic ideas by flee@cs.psu.edu (Felix Lee)
6647 (defun gnus-keysort-headers (predicate key &optional reverse)
6648 "Sort current headers by PREDICATE using a value passed by KEY safely.
6649 *Safely* means C-g quitting is disabled during sort.
6650 Optional argument REVERSE means reverse order."
6651 (let ((inhibit-quit t))
6652 (setq gnus-newsgroup-headers
6655 (gnus-keysort (nreverse gnus-newsgroup-headers) predicate key))
6656 (gnus-keysort gnus-newsgroup-headers predicate key)))
6659 (defun gnus-keysort (list predicate key)
6660 "Sort LIST by PREDICATE using a value passed by KEY."
6661 (mapcar (function cdr)
6662 (sort (mapcar (lambda (a) (cons (funcall key a) a)) list)
6664 (funcall predicate (car a) (car b))))))
6666 (defun gnus-sort-headers (predicate &optional reverse)
6667 "Sort current headers by PREDICATE safely.
6668 *Safely* means C-g quitting is disabled during sort.
6669 Optional argument REVERSE means reverse order."
6670 (let ((inhibit-quit t))
6671 (setq gnus-newsgroup-headers
6673 (nreverse (sort (nreverse gnus-newsgroup-headers) predicate))
6674 (sort gnus-newsgroup-headers predicate)))
6677 (defun gnus-string-lessp (a b)
6678 "Return T if first arg string is less than second in lexicographic order.
6679 If case-fold-search is non-nil, case of letters is ignored."
6680 (if case-fold-search
6681 (string-lessp (downcase a) (downcase b))
6682 (string-lessp a b)))
6684 (defun gnus-date-lessp (date1 date2)
6685 "Return T if DATE1 is earlyer than DATE2."
6686 (string-lessp (gnus-sortable-date date1)
6687 (gnus-sortable-date date2)))
6689 (defun gnus-sortable-date (date)
6690 "Make sortable string by string-lessp from DATE.
6691 Timezone package is used."
6692 (let* ((date (timezone-fix-time date nil nil)) ;[Y M D H M S]
6693 (year (aref date 0))
6694 (month (aref date 1))
6695 (day (aref date 2)))
6696 (timezone-make-sortable-date year month day
6697 (timezone-make-time-string
6698 (aref date 3) (aref date 4) (aref date 5)))
6702 ;; Summary saving commands.
6704 (defun gnus-summary-save-article (n)
6705 "Save the current article using the default saver function.
6706 If N is a positive number, save the N next articles.
6707 If N is a negative number, save the N previous articles.
6708 If N is nil and any articles have been marked with the process mark,
6709 save those articles instead.
6710 The variable `gnus-default-article-saver' specifies the saver function."
6712 (let (articles process)
6713 (if (and n (numberp n))
6714 (let ((backward (< n 0))
6718 (setq articles (cons (gnus-summary-article-number)
6720 (gnus-summary-search-forward nil nil backward))
6722 (setq articles (sort articles (function <))))
6723 (if gnus-newsgroup-processable
6725 (setq articles (setq gnus-newsgroup-processable
6726 (nreverse gnus-newsgroup-processable)))
6728 (setq articles (list (gnus-summary-article-number)))))
6730 (gnus-summary-display-article (car articles) t)
6731 (if (not gnus-save-all-headers)
6732 (gnus-article-hide-headers t))
6733 (if gnus-default-article-saver
6734 (funcall gnus-default-article-saver)
6735 (error "No default saver is defined."))
6737 (gnus-summary-remove-process-mark (car articles)))
6738 (setq articles (cdr articles)))
6739 (if process (setq gnus-newsgroup-processable
6740 (nreverse gnus-newsgroup-processable)))
6743 (defun gnus-summary-pipe-output (arg)
6744 "Pipe the current article to a subprocess.
6745 If N is a positive number, pipe the N next articles.
6746 If N is a negative number, pipe the N previous articles.
6747 If N is nil and any articles have been marked with the process mark,
6748 pipe those articles instead."
6750 (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
6751 (gnus-summary-save-article arg)))
6753 (defun gnus-summary-save-article-rmail (arg)
6754 "Append the current article to an Rmail file.
6755 If N is a positive number, save the N next articles.
6756 If N is a negative number, save the N previous articles.
6757 If N is nil and any articles have been marked with the process mark,
6758 save those articles instead."
6760 (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail))
6761 (gnus-summary-save-article arg)))
6763 (defun gnus-summary-save-in-rmail (&optional filename)
6764 "Append this article to Rmail file.
6765 Optional argument FILENAME specifies file name.
6766 Directory to save to is default to `gnus-article-save-directory' which
6767 is initialized from the SAVEDIR environment variable."
6770 (funcall gnus-rmail-save-name gnus-newsgroup-name
6771 gnus-current-headers gnus-newsgroup-last-rmail)))
6775 (concat "Save article in rmail file: (default "
6776 (file-name-nondirectory default-name) ") ")
6777 (file-name-directory default-name)
6779 (gnus-make-directory (file-name-directory filename))
6780 (gnus-eval-in-buffer-window
6785 (gnus-output-to-rmail filename))))
6786 ;; Remember the directory name to save articles.
6787 (setq gnus-newsgroup-last-rmail filename)))
6789 (defun gnus-summary-save-in-mail (&optional filename)
6790 "Append this article to Unix mail file.
6791 Optional argument FILENAME specifies file name.
6792 Directory to save to is default to `gnus-article-save-directory' which
6793 is initialized from the SAVEDIR environment variable."
6795 (funcall gnus-mail-save-name gnus-newsgroup-name
6796 gnus-current-headers gnus-newsgroup-last-mail)))
6800 (concat "Save article in Unix mail file: (default "
6801 (file-name-nondirectory default-name) ") ")
6802 (file-name-directory default-name)
6805 (expand-file-name filename
6807 (file-name-directory default-name))))
6808 (gnus-make-directory (file-name-directory filename))
6809 (gnus-eval-in-buffer-window
6814 (if (and (file-readable-p filename) (rmail-file-p filename))
6815 (gnus-output-to-rmail filename)
6816 (rmail-output filename 1 t t)))))
6817 ;; Remember the directory name to save articles.
6818 (setq gnus-newsgroup-last-mail filename)))
6820 (defun gnus-summary-save-in-file (&optional filename)
6821 "Append this article to file.
6822 Optional argument FILENAME specifies file name.
6823 Directory to save to is default to `gnus-article-save-directory' which
6824 is initialized from the SAVEDIR environment variable."
6826 (funcall gnus-file-save-name gnus-newsgroup-name
6827 gnus-current-headers gnus-newsgroup-last-file)))
6831 (concat "Save article in file: (default "
6832 (file-name-nondirectory default-name) ") ")
6833 (file-name-directory default-name)
6835 (gnus-make-directory (file-name-directory filename))
6836 (gnus-eval-in-buffer-window
6841 (gnus-output-to-file filename))))
6842 ;; Remember the directory name to save articles.
6843 (setq gnus-newsgroup-last-file filename)))
6845 (defun gnus-summary-save-in-pipe (&optional command)
6846 "Pipe this article to subprocess."
6847 (let ((command (read-string "Shell command on article: "
6848 gnus-last-shell-command)))
6849 (if (string-equal command "")
6850 (setq command gnus-last-shell-command))
6851 (gnus-eval-in-buffer-window
6855 (shell-command-on-region (point-min) (point-max) command nil)))
6856 (setq gnus-last-shell-command command)))
6858 ;; Summary killfile commands
6860 ;; Much modification of the kill code and some of the functions are
6861 ;; written by Per Abrahamsen <amanda@iesd.auc.dk>.
6863 (defun gnus-summary-temporarily-lower-by-subject (level)
6864 "Temporarily lower score by LEVEL for current subject.
6865 See `gnus-kill-expiry-days'."
6867 (gnus-kill-file-temporarily-lower-by-subject
6869 (let ((article (gnus-summary-article-number)))
6870 (if article (gnus-get-header-by-number article)
6871 (error "No article on current line")))))
6873 (defun gnus-summary-temporarily-lower-by-author (level)
6874 "Temporarily lower score by LEVEL for current author.
6875 See `gnus-kill-expiry-days'."
6877 (gnus-kill-file-temporarily-lower-by-author
6879 (let ((article (gnus-summary-article-number)))
6880 (if article (gnus-get-header-by-number article)
6881 (error "No article on current line")))))
6883 (defun gnus-summary-temporarily-lower-by-xref (level)
6884 "Temporarily lower score by LEVEL for current xref.
6885 See `gnus-kill-expiry-days'."
6887 (gnus-kill-file-temporarily-lower-by-xref
6889 (let ((article (gnus-summary-article-number)))
6890 (if article (gnus-get-header-by-number article)
6891 (error "No article on current line")))))
6893 (defun gnus-summary-temporarily-lower-by-thread (level)
6894 "Temporarily lower score by LEVEL for current thread.
6895 See `gnus-kill-expiry-days'."
6897 (gnus-kill-file-temporarily-lower-by-thread
6899 (let ((article (gnus-summary-article-number)))
6900 (if article (gnus-get-header-by-number article)
6901 (error "No article on current line")))))
6903 (defun gnus-summary-lower-by-subject (level)
6904 "Lower score by LEVEL for current subject."
6906 (gnus-kill-file-lower-by-subject
6908 (let ((article (gnus-summary-article-number)))
6909 (if article (gnus-get-header-by-number article)
6910 (error "No article on current line")))))
6912 (defun gnus-summary-lower-by-author (level)
6913 "Lower score by LEVEL for current author."
6915 (gnus-kill-file-lower-by-author
6917 (let ((article (gnus-summary-article-number)))
6918 (if article (gnus-get-header-by-number article)
6919 (error "No article on current line")))))
6921 (defun gnus-summary-lower-by-xref (level)
6922 "Lower score by LEVEL for current xref."
6924 (gnus-kill-file-lower-by-xref
6926 (let ((article (gnus-summary-article-number)))
6927 (if article (gnus-get-header-by-number article)
6928 (error "No article on current line")))))
6930 (defun gnus-summary-lower-followups-to-author (level)
6931 "Lower score by LEVEL for all followups to the current author."
6933 (gnus-kill-file-lower-followups-to-author
6935 (let ((article (gnus-summary-article-number)))
6936 (if article (gnus-get-header-by-number article)
6937 (error "No article on current line")))))
6939 (defun gnus-summary-temporarily-raise-by-subject (level)
6940 "Temporarily raise score by LEVEL for current subject.
6941 See `gnus-kill-expiry-days'."
6943 (gnus-kill-file-temporarily-raise-by-subject
6945 (let ((article (gnus-summary-article-number)))
6946 (if article (gnus-get-header-by-number article)
6947 (error "No article on current line")))))
6949 (defun gnus-summary-temporarily-raise-by-author (level)
6950 "Temporarily raise score by LEVEL for current author.
6951 See `gnus-kill-expiry-days'."
6953 (gnus-kill-file-temporarily-raise-by-author
6955 (let ((article (gnus-summary-article-number)))
6956 (if article (gnus-get-header-by-number article)
6957 (error "No article on current line")))))
6959 (defun gnus-summary-temporarily-raise-by-xref (level)
6960 "Temporarily raise score by LEVEL for current xref.
6961 See `gnus-kill-expiry-days'."
6963 (gnus-kill-file-temporarily-raise-by-xref
6965 (let ((article (gnus-summary-article-number)))
6966 (if article (gnus-get-header-by-number article)
6967 (error "No article on current line")))))
6969 (defun gnus-summary-temporarily-raise-by-thread (level)
6970 "Temporarily raise score by LEVEL for current thread.
6971 See `gnus-kill-expiry-days'."
6973 (gnus-kill-file-temporarily-raise-by-thread
6975 (let ((article (gnus-summary-article-number)))
6976 (if article (gnus-get-header-by-number article)
6977 (error "No article on current line")))))
6979 (defun gnus-summary-raise-by-subject (level)
6980 "Raise score by LEVEL for current subject."
6982 (gnus-kill-file-raise-by-subject
6984 (let ((article (gnus-summary-article-number)))
6985 (if article (gnus-get-header-by-number article)
6986 (error "No article on current line")))))
6988 (defun gnus-summary-raise-by-author (level)
6989 "Raise score by LEVEL for current author."
6991 (gnus-kill-file-raise-by-author
6993 (let ((article (gnus-summary-article-number)))
6994 (if article (gnus-get-header-by-number article)
6995 (error "No article on current line")))))
6997 (defun gnus-summary-raise-by-xref (level)
6998 "Raise score by LEVEL for current xref."
7000 (gnus-kill-file-raise-by-xref
7002 (let ((article (gnus-summary-article-number)))
7003 (if article (gnus-get-header-by-number article)
7004 (error "No article on current line")))))
7006 (defun gnus-summary-edit-global-kill ()
7007 "Edit a global KILL file."
7009 (setq gnus-current-kill-article (gnus-summary-article-number))
7010 (gnus-kill-file-edit-file nil) ;Nil stands for global KILL file.
7012 (substitute-command-keys
7013 "Editing a global KILL file (Type \\[gnus-kill-file-exit] to exit)")))
7015 (defun gnus-summary-raise-followups-to-author (level)
7016 "Raise score by LEVEL for all followups to the current author."
7018 (gnus-kill-file-raise-followups-to-author
7020 (let ((article (gnus-summary-article-number)))
7021 (if article (gnus-get-header-by-number article)
7022 (error "No article on current line")))))
7024 (defun gnus-summary-edit-local-kill ()
7025 "Edit a local KILL file applied to the current newsgroup."
7027 (setq gnus-current-kill-article (gnus-summary-article-number))
7028 (gnus-kill-file-edit-file gnus-newsgroup-name)
7030 (substitute-command-keys
7031 "Editing a local KILL file (Type \\[gnus-kill-file-exit] to exit)")))
7036 ;;; Gnus Article Mode
7039 (if gnus-article-mode-map
7041 (setq gnus-article-mode-map (make-keymap))
7042 (suppress-keymap gnus-article-mode-map)
7043 (define-key gnus-article-mode-map " " 'gnus-article-next-page)
7044 (define-key gnus-article-mode-map "\177" 'gnus-article-prev-page)
7045 (define-key gnus-article-mode-map "r" 'gnus-article-refer-article)
7046 (define-key gnus-article-mode-map "h" 'gnus-article-show-summary)
7047 (define-key gnus-article-mode-map "s" 'gnus-article-show-summary)
7048 (define-key gnus-article-mode-map "m" 'gnus-article-mail)
7049 (define-key gnus-article-mode-map "M" 'gnus-article-mail-with-original)
7050 (define-key gnus-article-mode-map "?" 'gnus-article-describe-briefly)
7051 (define-key gnus-article-mode-map "\C-c\C-i" 'gnus-info-find-node))
7053 (defun gnus-article-mode ()
7054 "Major mode for browsing through an article.
7055 All normal editing commands are switched off.
7056 The following commands are available:
7058 \\<gnus-article-mode-map>
7059 \\[gnus-article-next-page]\t Scroll the article one page forwards
7060 \\[gnus-article-prev-page]\t Scroll the article one page backwards
7061 \\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
7062 \\[gnus-article-show-summary]\t Display the Summary buffer
7063 \\[gnus-article-mail]\t Send a reply to the address near point
7064 \\[gnus-article-mail-with-original]\t Send a reply to the address near point; include the original article
7065 \\[gnus-article-describe-briefly]\t Describe the current mode briefly
7066 \\[gnus-info-find-node]\t Go to the Gnus info node
7070 (kill-all-local-variables)
7071 (setq mode-line-modified "--- ")
7072 (setq major-mode 'gnus-article-mode)
7073 (setq mode-name "Article")
7074 (make-local-variable 'minor-mode-alist)
7075 (or (assq 'gnus-show-mime minor-mode-alist)
7076 (setq minor-mode-alist
7077 (cons (list 'gnus-show-mime " MIME") minor-mode-alist)))
7078 (use-local-map gnus-article-mode-map)
7079 (make-local-variable 'page-delimiter)
7080 (setq page-delimiter gnus-page-delimiter)
7081 (make-local-variable 'mail-header-separator)
7082 (setq mail-header-separator "") ;For caesar function.
7083 (buffer-disable-undo (current-buffer))
7084 (setq buffer-read-only t) ;Disable modification
7085 (run-hooks 'gnus-article-mode-hook))
7087 (defun gnus-article-setup-buffer ()
7088 "Initialize Article mode buffer."
7089 (or (get-buffer gnus-article-buffer)
7091 (set-buffer (get-buffer-create gnus-article-buffer))
7092 (gnus-add-current-to-buffer-list)
7093 (gnus-article-mode))
7096 (defun gnus-request-article-this-buffer (article &optional group)
7097 "Get an article and insert it into this buffer."
7098 ;; Using `gnus-request-article' directly will insert the article into
7099 ;; `nntp-server-buffer' - so we'll save some time by not having to
7100 ;; copy it from the server buffer into the article buffer.
7102 ;; We only request an article by message-id when we do not have the
7103 ;; headers for it, so we'll have to get those.
7104 (if (stringp article) (gnus-read-header article))
7105 ;; If the article number is negative, that means that this article
7106 ;; doesn't belong in this newsgroup (possibly), so we find its
7107 ;; message-id and request it by id instead of number.
7108 (if (and (numberp article) (< article 0))
7110 (set-buffer gnus-summary-buffer)
7113 (gnus-gethash (int-to-string article)
7114 gnus-newsgroup-headers-hashtb-by-number)))))
7115 ;; Get the article and into the article buffer.
7116 (gnus-request-article article group (current-buffer)))
7118 (defun gnus-read-header (id)
7119 "Read the headers of article ID and enter them into the Gnus system."
7120 (or gnus-newsgroup-headers-hashtb-by-number
7121 (gnus-make-headers-hashtable-by-number))
7123 (if (not (setq header
7124 (car (if (let ((nntp-xover-is-evil t))
7125 (gnus-retrieve-headers (list id)
7126 gnus-newsgroup-name))
7127 (gnus-get-newsgroup-headers)))))
7130 (header-set-number header gnus-reffed-article-number))
7131 (setq gnus-newsgroup-headers (cons header gnus-newsgroup-headers))
7132 (gnus-sethash (int-to-string (header-number header)) header
7133 gnus-newsgroup-headers-hashtb-by-number)
7135 (setq gnus-reffed-article-number (1- gnus-reffed-article-number)))
7136 (setq gnus-current-headers header)
7139 (defun gnus-article-prepare (article &optional all-headers header)
7140 "Prepare ARTICLE in Article mode buffer.
7141 ARTICLE can be either a article number or Message-ID.
7142 If ARTICLE is an id, HEADER should be the article headers.
7143 If ALL-HEADERS is non-nil, no headers are hidden."
7145 ;; Make sure we start are in a Summary buffer.
7146 (if (eq major-mode 'gnus-summary-mode)
7147 (setq gnus-summary-buffer (current-buffer))
7148 (set-buffer gnus-summary-buffer))
7149 ;; Make sure the connection to the server is alive.
7150 (if (not (gnus-server-opened gnus-current-select-method))
7152 (gnus-check-news-server gnus-current-select-method)
7153 (gnus-request-group gnus-newsgroup-name t)))
7154 (or gnus-newsgroup-headers-hashtb-by-number
7155 (gnus-make-headers-hashtable-by-number))
7156 (let* ((article (if header (header-number header) article))
7157 (summary-buffer (current-buffer))
7158 (internal-hook gnus-article-internal-prepare-hook)
7159 (bookmark (cdr (assq article gnus-newsgroup-bookmarks)))
7160 (group gnus-newsgroup-name))
7162 (set-buffer gnus-article-buffer)
7163 (let ((buffer-read-only nil))
7166 (if (gnus-request-article-this-buffer article group)
7168 ;; gnus-have-all-headers must be either T or NIL.
7169 (setq gnus-have-all-headers
7170 (not (not (or all-headers gnus-show-all-headers))))
7171 (if (and (numberp article)
7172 (not (eq article gnus-current-article)))
7173 ;; Seems like a new article has been selected.
7174 ;; `gnus-current-article' must be an article number.
7176 (set-buffer summary-buffer)
7177 (setq gnus-last-article gnus-current-article)
7178 (setq gnus-current-article article)
7179 (setq gnus-current-headers
7180 (gnus-get-header-by-number
7181 gnus-current-article))
7182 (setq gnus-article-current
7183 (cons gnus-newsgroup-name
7184 (header-number gnus-current-headers)))
7185 (run-hooks 'gnus-mark-article-hook)
7187 (run-hooks 'gnus-visual-mark-article-hook))
7188 ;; Set the global newsgroup variables here.
7189 ;; Suggested by Jim Sisolak
7190 ;; <sisolak@trans4.neep.wisc.edu>.
7191 (gnus-set-global-variables)))
7192 ;; Hooks for getting information from the article.
7193 ;; This hook must be called before being narrowed.
7194 (run-hooks 'internal-hook)
7195 (run-hooks 'gnus-article-prepare-hook)
7196 ;; Decode MIME message.
7197 (if (and gnus-show-mime
7198 (gnus-fetch-field "Mime-Version"))
7199 (funcall gnus-show-mime-method))
7200 ;; Perform the article display hooks.
7201 (let ((buffer-read-only nil))
7202 (run-hooks 'gnus-article-display-hook))
7204 (goto-char (point-min))
7205 (if gnus-break-pages
7206 (gnus-narrow-to-page))
7207 (gnus-set-mode-line 'article)
7209 ;; There is no such article.
7210 (if (numberp article)
7211 (gnus-summary-mark-as-read article))
7213 (message "No such article (may be canceled)")
7218 (message "Moved to bookmark.")
7219 (search-forward "\n\n" nil t)
7220 (forward-line bookmark)))
7222 (get-buffer-window gnus-article-buffer) (point))))))))
7224 (defun gnus-set-global-variables ()
7225 ;; Set the global equivalents of the Summary buffer-local variables
7226 ;; to the latest values they had. These reflect the Summary buffer
7227 ;; that was in action when the last article was fetched.
7228 (let ((name gnus-newsgroup-name)
7229 (marked gnus-newsgroup-marked)
7230 (unread gnus-newsgroup-unreads)
7231 (headers gnus-current-headers))
7233 (set-buffer gnus-group-buffer)
7234 (setq gnus-newsgroup-name name)
7235 (setq gnus-newsgroup-marked marked)
7236 (setq gnus-newsgroup-unreads unread)
7237 (setq gnus-current-headers headers))))
7239 (defun gnus-article-show-all-headers ()
7240 "Show all article headers in Article mode buffer."
7242 (setq gnus-have-all-headers t)
7243 (gnus-article-setup-buffer)
7244 (set-buffer gnus-article-buffer)
7245 (let ((buffer-read-only nil))
7246 (remove-text-properties 1 (point-max) '(invisible t)))))
7248 (defun gnus-article-hide-headers-if-wanted ()
7249 "Hide unwanted headers if `gnus-have-all-headers' is nil.
7250 Provided for backwards compatability."
7251 (or gnus-have-all-headers
7252 (gnus-article-hide-headers)))
7254 (defun gnus-article-hide-headers (&optional delete)
7255 "Hide unwanted headers and possibly sort them as well."
7258 (let ((sorted gnus-sorted-header-list)
7259 (buffer-read-only nil)
7260 want want-list beg want-l)
7261 ;; First we narrow to just the headers.
7264 ;; Hide any "From " lines at the beginning of (mail) articles.
7265 (while (looking-at rmail-unix-mail-delimiter)
7268 (add-text-properties 1 (point) '(invisible t)))
7269 ;; Then treat the rest of the header lines.
7272 (progn (search-forward "\n\n" nil t) (forward-line -1) (point)))
7273 ;; Then we use the two regular expressions
7274 ;; `gnus-ignored-headers' and `gnus-visible-headers' to
7275 ;; select which header lines is to remain visible in the
7278 (while (re-search-forward "^[^ \t]*:" nil t)
7280 ;; We add the headers we want to keep to a list and delete
7281 ;; them from the buffer.
7282 (if (or (and (stringp gnus-visible-headers)
7283 (looking-at gnus-visible-headers))
7284 (and (not (stringp gnus-visible-headers))
7285 (stringp gnus-ignored-headers)
7286 (not (looking-at gnus-ignored-headers))))
7290 ;; Be sure to get multi-line headers...
7291 (re-search-forward "^[^ \t]*:" nil t)
7294 (cons (buffer-substring beg (point)) want-list))
7295 (delete-region beg (point))
7298 ;; Next we perform the sorting by looking at
7299 ;; `gnus-sorted-header-list'.
7301 (while (and sorted want-list)
7302 (setq want-l want-list)
7304 (not (string-match (car sorted) (car want-l))))
7305 (setq want-l (cdr want-l)))
7308 (insert (car want-l))
7309 (setq want-list (delq (car want-l) want-list))))
7310 (setq sorted (cdr sorted)))
7311 ;; Any headers that were not matched by the sorted list we
7312 ;; just tack on the end of the visible header list.
7314 (insert (car want-list))
7315 (setq want-list (cdr want-list)))
7316 ;; And finally we make the unwanted headers invisible.
7318 (delete-region (point) (point-max))
7319 ;; Suggested by Sudish Joseph <joseph@cis.ohio-state.edu>.
7320 (add-text-properties (point) (point-max) '(invisible t)))))))
7322 (defun gnus-article-hide-signature ()
7323 "Hides the signature in an article.
7324 It does this by hiding everyting after "^-- *$", which is what all
7325 signatures should be preceded by. Note that this may mean that parts
7326 of an article may disappear if the article has such a line in the
7327 middle of the text."
7329 (goto-char (point-max))
7330 (if (re-search-backward "^-- *$" nil t)
7332 (add-text-properties (point) (point-max) '(invisible t))))))
7334 (defun gnus-article-hide-citation ()
7335 "Hide all cited text.
7336 This function uses the famous, extremely intelligent \"shoot in foot\"
7337 algorithm - which is simply deleting all lines that start with
7338 \">\". Your mileage may vary. If you come up with anything better,
7339 please do mail it to me."
7342 (search-forward "\n\n" nil t)
7344 (if (looking-at ">")
7345 (add-text-properties
7346 (point) (save-excursion (forward-line 1) (point))
7350 ;; Written by Per Abrahamsen <amanda@iesd.auc.dk>.
7351 (defun gnus-article-treat-overstrike ()
7352 ;; Prepare article for overstrike commands.
7354 (set-buffer gnus-article-buffer)
7355 (let ((buffer-read-only nil))
7356 (while (search-forward "\b" nil t)
7357 (let ((next (following-char))
7358 (previous (char-after (- (point) 2))))
7359 (cond ((eq next previous)
7360 (delete-region (- (point) 2) (point))
7361 (put-text-property (point) (1+ (point))
7364 (delete-region (1- (point)) (1+ (point)))
7365 (put-text-property (1- (point)) (point)
7368 (delete-region (- (point) 2) (point))
7369 (put-text-property (point) (1+ (point))
7370 'face 'underline))))))))
7374 (defun gnus-output-to-rmail (file-name)
7375 "Append the current article to an Rmail file named FILE-NAME."
7377 ;; Most of these codes are borrowed from rmailout.el.
7378 (setq file-name (expand-file-name file-name))
7379 (setq rmail-default-rmail-file file-name)
7380 (let ((artbuf (current-buffer))
7381 (tmpbuf (get-buffer-create " *Gnus-output*")))
7383 (or (get-file-buffer file-name)
7384 (file-exists-p file-name)
7386 (concat "\"" file-name "\" does not exist, create it? "))
7387 (let ((file-buffer (create-file-buffer file-name)))
7389 (set-buffer file-buffer)
7390 (rmail-insert-rmail-file-header)
7391 (let ((require-final-newline nil))
7392 (write-region (point-min) (point-max) file-name t 1)))
7393 (kill-buffer file-buffer))
7394 (error "Output file does not exist")))
7396 (buffer-disable-undo (current-buffer))
7398 (insert-buffer-substring artbuf)
7399 (gnus-convert-article-to-rmail)
7400 ;; Decide whether to append to a file or to an Emacs buffer.
7401 (let ((outbuf (get-file-buffer file-name)))
7403 (append-to-file (point-min) (point-max) file-name)
7404 ;; File has been visited, in buffer OUTBUF.
7406 (let ((buffer-read-only nil)
7407 (msg (and (boundp 'rmail-current-message)
7408 rmail-current-message)))
7409 ;; If MSG is non-nil, buffer is in RMAIL mode.
7412 (narrow-to-region (point-max) (point-max))))
7413 (insert-buffer-substring tmpbuf)
7416 (goto-char (point-min))
7418 (search-backward "\^_")
7419 (narrow-to-region (point) (point-max))
7420 (goto-char (1+ (point-min)))
7421 (rmail-count-new-messages t)
7422 (rmail-show-message msg))))))
7424 (kill-buffer tmpbuf)
7427 (defun gnus-output-to-file (file-name)
7428 "Append the current article to a file named FILE-NAME."
7429 (setq file-name (expand-file-name file-name))
7430 (let ((artbuf (current-buffer))
7431 (tmpbuf (get-buffer-create " *Gnus-output*")))
7434 (buffer-disable-undo (current-buffer))
7436 (insert-buffer-substring artbuf)
7437 ;; Append newline at end of the buffer as separator, and then
7439 (goto-char (point-max))
7441 (append-to-file (point-min) (point-max) file-name))
7442 (kill-buffer tmpbuf)
7445 (defun gnus-convert-article-to-rmail ()
7446 "Convert article in current buffer to Rmail message format."
7447 (let ((buffer-read-only nil))
7448 ;; Convert article directly into Babyl format.
7449 ;; Suggested by Rob Austein <sra@lcs.mit.edu>
7450 (goto-char (point-min))
7451 (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
7452 (while (search-forward "\n\^_" nil t) ;single char
7453 (replace-match "\n^_")) ;2 chars: "^" and "_"
7454 (goto-char (point-max))
7457 (defun gnus-narrow-to-page (&optional arg)
7458 "Make text outside current page invisible except for page delimiter.
7459 A numeric arg specifies to move forward or backward by that many pages,
7460 thus showing a page other than the one point was originally in."
7462 (setq arg (if arg (prefix-numeric-value arg) 0))
7464 (forward-page -1) ;Beginning of current page.
7469 (forward-page (1- arg))))
7470 ;; Find the end of the page.
7472 ;; If we stopped due to end of buffer, stay there.
7473 ;; If we stopped after a page delimiter, put end of restriction
7474 ;; at the beginning of that line.
7475 ;; These are commented out.
7476 ;; (if (save-excursion (beginning-of-line)
7477 ;; (looking-at page-delimiter))
7478 ;; (beginning-of-line))
7479 (narrow-to-region (point)
7481 ;; Find the top of the page.
7483 ;; If we found beginning of buffer, stay there.
7484 ;; If extra text follows page delimiter on same line,
7486 ;; Otherwise, show text starting with following line.
7487 (if (and (eolp) (not (bobp)))
7492 (defun gnus-gmt-to-local ()
7493 "Rewrite Date: field described in GMT to local in current buffer.
7494 The variable gnus-local-timezone is used for local time zone.
7495 Intended to be used with gnus-article-prepare-hook."
7499 (goto-char (point-min))
7500 (narrow-to-region (point-min)
7501 (progn (search-forward "\n\n" nil 'move) (point)))
7502 (goto-char (point-min))
7503 (if (re-search-forward "^Date:[ \t]\\(.*\\)$" nil t)
7504 (let ((buffer-read-only nil)
7505 (date (buffer-substring (match-beginning 1) (match-end 1))))
7506 (delete-region (match-beginning 1) (match-end 1))
7508 (timezone-make-date-arpa-standard date nil gnus-local-timezone))
7513 ;; Article mode commands
7515 (defun gnus-article-next-page (lines)
7516 "Show next page of current article.
7517 If end of article, return non-nil. Otherwise return nil.
7518 Argument LINES specifies lines to be scrolled up."
7520 (move-to-window-line -1)
7521 ;; Fixed by enami@ptgd.sony.co.jp (enami tsugutomo)
7524 (and (pos-visible-in-window-p) ;Not continuation line.
7526 ;; Nothing in this page.
7527 (if (or (not gnus-break-pages)
7530 (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer?
7532 (gnus-narrow-to-page 1) ;Go to next page.
7535 ;; More in this page.
7539 ;; Long lines may cause an end-of-buffer error.
7540 (goto-char (point-max))))
7544 (defun gnus-article-prev-page (lines)
7545 "Show previous page of current article.
7546 Argument LINES specifies lines to be scrolled down."
7548 (move-to-window-line 0)
7549 (if (and gnus-break-pages
7551 (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
7553 (gnus-narrow-to-page -1) ;Go to previous page.
7554 (goto-char (point-max))
7556 (scroll-down lines)))
7558 (defun gnus-article-next-digest (nth)
7559 "Move to head of NTH next digested message.
7560 Set mark at end of digested message."
7561 ;; Stop page breaking in digest mode.
7564 ;; Skip NTH - 1 digest.
7565 ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
7566 ;; Digest separator is customizable.
7567 ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
7568 (while (and (> nth 1)
7569 (re-search-forward gnus-digest-separator nil 'move))
7570 (setq nth (1- nth)))
7571 (if (re-search-forward gnus-digest-separator nil t)
7572 (let ((begin (point)))
7573 ;; Search for end of this message.
7575 (if (re-search-forward gnus-digest-separator nil t)
7577 (search-backward "\n\n") ;This may be incorrect.
7579 (goto-char (point-max)))
7580 (push-mark) ;Set mark at end of digested message.
7583 ;; Show From: and Subject: fields.
7585 (message "End of message")
7588 (defun gnus-article-prev-digest (nth)
7589 "Move to head of NTH previous digested message."
7590 ;; Stop page breaking in digest mode.
7593 ;; Skip NTH - 1 digest.
7594 ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
7595 ;; Digest separator is customizable.
7596 ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
7597 (while (and (> nth 1)
7598 (re-search-backward gnus-digest-separator nil 'move))
7599 (setq nth (1- nth)))
7600 (if (re-search-backward gnus-digest-separator nil t)
7601 (let ((begin (point)))
7602 ;; Search for end of this message.
7604 (if (re-search-forward gnus-digest-separator nil t)
7606 (search-backward "\n\n") ;This may be incorrect.
7608 (goto-char (point-max)))
7609 (push-mark) ;Set mark at end of digested message.
7611 ;; Show From: and Subject: fields.
7613 (goto-char (point-min))
7614 (message "Top of message")
7617 (defun gnus-article-refer-article ()
7618 "Read article specified by message-id around point."
7620 (search-forward ">" nil t) ;Move point to end of "<....>".
7621 (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
7623 (buffer-substring (match-beginning 1) (match-end 1))))
7624 (set-buffer gnus-summary-buffer)
7625 (gnus-summary-refer-article message-id))
7626 (error "No references around point")))
7628 (defun gnus-article-mail (yank)
7629 "Send a reply to the address near point.
7630 If YANK is non-nil, include the original article."
7634 (save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
7635 (save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
7638 (switch-to-buffer gnus-summary-buffer)
7639 (funcall gnus-mail-reply-method yank address)))))
7641 (defun gnus-article-mail-with-original ()
7642 "Send a reply to the address near point and include the original article."
7644 (gnus-article-mail 'yank))
7646 (defun gnus-article-show-summary ()
7647 "Reconfigure windows to show Summary buffer."
7649 (gnus-configure-windows 'article)
7650 (pop-to-buffer gnus-summary-buffer)
7651 (gnus-summary-goto-subject gnus-current-article))
7653 (defun gnus-article-describe-briefly ()
7654 "Describe Article mode commands briefly."
7657 (substitute-command-keys "\\[gnus-article-next-page]:Next page \\[gnus-article-prev-page]:Prev page \\[gnus-article-show-summary]:Show Summary \\[gnus-info-find-node]:Run Info \\[gnus-article-describe-briefly]:This help")))
7659 ;; caesar-region written by phr@prep.ai.mit.edu Nov 86
7660 ;; Modified by tower@prep Nov 86
7661 ;; Modified by umerin@flab.flab.Fujitsu.JUNET for ROT47.
7663 (defun gnus-caesar-region (&optional n)
7664 "Caesar rotation of region by N, default 13, for decrypting netnews.
7665 ROT47 will be performed for Japanese text in any case."
7666 (interactive (if current-prefix-arg ; Was there a prefix arg?
7667 (list (prefix-numeric-value current-prefix-arg))
7669 (cond ((not (numberp n)) (setq n 13))
7670 (t (setq n (mod n 26)))) ;canonicalize N
7671 (if (not (zerop n)) ; no action needed for a rot of 0
7673 (if (or (not (boundp 'caesar-translate-table))
7674 (not caesar-translate-table)
7675 (/= (aref caesar-translate-table ?a) (+ ?a n)))
7676 (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
7677 (message "Building caesar-translate-table...")
7678 (setq caesar-translate-table (make-vector 256 0))
7680 (aset caesar-translate-table i i)
7682 (setq lower (concat lower lower) upper (upcase lower) i 0)
7684 (aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
7685 (aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
7687 ;; ROT47 for Japanese text.
7688 ;; Thanks to ichikawa@flab.fujitsu.junet.
7690 (let ((t1 (logior ?O 128))
7691 (t2 (logior ?! 128))
7692 (t3 (logior ?~ 128)))
7694 (aset caesar-translate-table i
7695 (let ((v (aref caesar-translate-table i)))
7696 (if (<= v t1) (if (< v t2) v (+ v 47))
7697 (if (<= v t3) (- v 47) v))))
7699 (message "Building caesar-translate-table... done")))
7700 (let ((from (region-beginning))
7703 (setq str (buffer-substring from to))
7704 (setq len (length str))
7706 (aset str i (aref caesar-translate-table (aref str i)))
7709 (delete-region from to)
7714 ;;; Gnus KILL-File Mode
7717 (if gnus-kill-file-mode-map
7719 (setq gnus-kill-file-mode-map (copy-keymap emacs-lisp-mode-map))
7720 (define-key gnus-kill-file-mode-map "\C-c\C-x"
7721 'gnus-kill-file-set-expunge-below)
7722 (define-key gnus-kill-file-mode-map "\C-c\C-m"
7723 'gnus-kill-file-set-mark-below)
7724 (define-key gnus-kill-file-mode-map "\C-c\C-k\C-s"
7725 'gnus-kill-file-temporarily-lower-by-subject)
7726 (define-key gnus-kill-file-mode-map "\C-c\C-k\C-a"
7727 'gnus-kill-file-temporarily-lower-by-author)
7728 (define-key gnus-kill-file-mode-map "\C-c\C-k\C-x"
7729 'gnus-kill-file-temporarily-lower-by-xref)
7730 (define-key gnus-kill-file-mode-map "\C-c\C-ks"
7731 'gnus-kill-file-lower-by-subject)
7732 (define-key gnus-kill-file-mode-map "\C-c\C-ka"
7733 'gnus-kill-file-lower-by-author)
7734 (define-key gnus-kill-file-mode-map "\C-c\C-kt"
7735 'gnus-kill-file-lower-by-thread)
7736 (define-key gnus-kill-file-mode-map "\C-c\C-kx"
7737 'gnus-kill-file-lower-by-xref)
7738 (define-key gnus-kill-file-mode-map "\C-c\C-kf"
7739 'gnus-kill-file-lower-followups-to-author)
7740 (define-key gnus-kill-file-mode-map "\C-c\C-i\C-s"
7741 'gnus-kill-file-temporarily-raise-by-subject)
7742 (define-key gnus-kill-file-mode-map "\C-c\C-i\C-a"
7743 'gnus-kill-file-temporarily-raise-by-author)
7744 (define-key gnus-kill-file-mode-map "\C-c\C-i\C-t"
7745 'gnus-kill-file-temporarily-raise-by-thread)
7746 (define-key gnus-kill-file-mode-map "\C-c\C-i\C-x"
7747 'gnus-kill-file-temporarily-raise-by-xref)
7748 (define-key gnus-kill-file-mode-map "\C-c\C-is"
7749 'gnus-kill-file-raise-by-subject)
7750 (define-key gnus-kill-file-mode-map "\C-c\C-ia"
7751 'gnus-kill-file-raise-by-author)
7752 (define-key gnus-kill-file-mode-map "\C-c\C-ix"
7753 'gnus-kill-file-raise-by-xref)
7754 (define-key gnus-kill-file-mode-map "\C-c\C-if"
7755 'gnus-kill-file-raise-followups-to-author)
7756 (define-key gnus-kill-file-mode-map "\C-c\C-a" 'gnus-kill-file-apply-buffer)
7757 (define-key gnus-kill-file-mode-map "\C-c\C-e" 'gnus-kill-file-apply-last-sexp)
7758 (define-key gnus-kill-file-mode-map "\C-c\C-c" 'gnus-kill-file-exit)
7759 (define-key gnus-kill-file-mode-map "\C-c\C-i" 'gnus-info-find-node))
7761 (defun gnus-kill-file-mode ()
7762 "Major mode for editing KILL file.
7764 In addition to Emacs-Lisp Mode, the following commands are available:
7766 \\[gnus-kill-file-set-expunge-below] Automatically expunge articles below LEVEL.
7767 \\[gnus-kill-file-set-mark-below] Automatically mark articles below LEVEL.
7768 \\[gnus-kill-file-temporarily-lower-by-author] Insert temporary lower command for current author.
7769 \\[gnus-kill-file-temporarily-lower-by-thread] Insert temporary lower command for current thread.
7770 \\[gnus-kill-file-temporarily-lower-by-xref] Insert temporary lower command for current cross-posting.
7771 \\[gnus-kill-file-lower-by-subject] Insert permanent lower command for current subject.
7772 \\[gnus-kill-file-lower-by-author] Insert permanent lower command for current author.
7773 \\[gnus-kill-file-lower-followups-to-author] Insert permanent lower command for followups to the current author.
7774 \\[gnus-kill-file-lower-by-xref] Insert permanent lower command for current cross-posting.
7775 \\[gnus-kill-file-temporarily-raise-by-subject] Insert temporary raise command for current subject.
7776 \\[gnus-kill-file-temporarily-raise-by-author] Insert temporary raise command for current author.
7777 \\[gnus-kill-file-temporarily-raise-by-thread] Insert temporary raise command for current thread.
7778 \\[gnus-kill-file-temporarily-raise-by-xref] Insert temporary raise command for current cross-posting.
7779 \\[gnus-kill-file-raise-by-subject] Insert permanent raise command for current subject.
7780 \\[gnus-kill-file-raise-by-author] Insert permanent raise command for current author.
7781 \\[gnus-kill-file-raise-followups-to-author] Insert permanent raise command for followups to the current author.
7782 \\[gnus-kill-file-raise-by-xref] Insert permanent raise command for current cross-posting.
7783 \\[gnus-kill-file-apply-buffer] Apply current buffer to selected newsgroup.
7784 \\[gnus-kill-file-apply-last-sexp] Apply sexp before point to selected newsgroup.
7785 \\[gnus-kill-file-exit] Save file and exit editing KILL file.
7786 \\[gnus-info-find-node] Read Info about KILL file.
7788 A KILL file contains Lisp expressions to be applied to a selected
7789 newsgroup. The purpose is to mark articles as read on the basis of
7790 some set of regexps. A global KILL file is applied to every newsgroup,
7791 and a local KILL file is applied to a specified newsgroup. Since a
7792 global KILL file is applied to every newsgroup, for better performance
7795 A KILL file can contain any kind of Emacs Lisp expressions expected
7796 to be evaluated in the Summary buffer. Writing Lisp programs for this
7797 purpose is not so easy because the internal working of Gnus must be
7798 well-known. For this reason, Gnus provides a general function which
7799 does this easily for non-Lisp programmers.
7801 The `gnus-kill' function executes commands available in Summary Mode
7802 by their key sequences. `gnus-kill' should be called with FIELD,
7803 REGEXP and optional COMMAND and ALL. FIELD is a string representing
7804 the header field or an empty string. If FIELD is an empty string, the
7805 entire article body is searched for. REGEXP is a string which is
7806 compared with FIELD value. COMMAND is a string representing a valid
7807 key sequence in Summary mode or Lisp expression. COMMAND defaults to
7808 '(gnus-summary-mark-as-read nil \"X\"). Make sure that COMMAND is
7809 executed in the Summary buffer. If the second optional argument ALL
7810 is non-nil, the COMMAND is applied to articles which are already
7811 marked as read or unread. Articles which are marked are skipped over
7814 For example, if you want to mark articles of which subjects contain
7815 the string `AI' as read, a possible KILL file may look like:
7817 (gnus-kill \"Subject\" \"AI\")
7819 If you want to mark articles with `D' instead of `X', you can use
7820 the following expression:
7822 (gnus-kill \"Subject\" \"AI\" \"d\")
7824 In this example it is assumed that the command
7825 `gnus-summary-mark-as-read-forward' is assigned to `d' in Summary Mode.
7827 It is possible to delete unnecessary headers which are marked with
7828 `X' in a KILL file as follows:
7830 (gnus-expunge \"X\")
7832 If the Summary buffer is empty after applying KILL files, Gnus will
7833 exit the selected newsgroup normally. If headers which are marked
7834 with `D' are deleted in a KILL file, it is impossible to read articles
7835 which are marked as read in the previous Gnus sessions. Marks other
7836 than `D' should be used for articles which should really be deleted.
7838 Entry to this mode calls emacs-lisp-mode-hook and
7839 gnus-kill-file-mode-hook with no arguments, if that value is non-nil."
7841 (kill-all-local-variables)
7842 (use-local-map gnus-kill-file-mode-map)
7843 (set-syntax-table emacs-lisp-mode-syntax-table)
7844 (setq major-mode 'gnus-kill-file-mode)
7845 (setq mode-name "KILL-File")
7846 (lisp-mode-variables nil)
7847 (run-hooks 'emacs-lisp-mode-hook 'gnus-kill-file-mode-hook))
7849 (defun gnus-kill-file-edit-file (newsgroup)
7850 "Begin editing a KILL file of NEWSGROUP.
7851 If NEWSGROUP is nil, the global KILL file is selected."
7852 (interactive "sNewsgroup: ")
7853 (let ((file (gnus-newsgroup-kill-file newsgroup)))
7854 (gnus-make-directory (file-name-directory file))
7855 ;; Save current window configuration if this is first invocation.
7856 (or (and (get-file-buffer file)
7857 (get-buffer-window (get-file-buffer file)))
7858 (setq gnus-winconf-kill-file (current-window-configuration)))
7860 (let ((buffer (find-file-noselect file)))
7861 (cond ((get-buffer-window buffer)
7862 (pop-to-buffer buffer))
7863 ((eq major-mode 'gnus-group-mode)
7864 (gnus-configure-windows '(1 0 0)) ;Take all windows.
7865 (pop-to-buffer gnus-group-buffer)
7866 (let ((gnus-summary-buffer buffer))
7867 (gnus-configure-windows '(1 1 0)) ;Split into two.
7868 (pop-to-buffer buffer)))
7869 ((eq major-mode 'gnus-summary-mode)
7870 (gnus-configure-windows 'article)
7871 (pop-to-buffer gnus-article-buffer)
7872 (bury-buffer gnus-article-buffer)
7873 (switch-to-buffer buffer))
7875 (find-file-other-window file))
7877 (gnus-kill-file-mode)
7880 (defun gnus-kill-set-kill-buffer ()
7881 (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
7882 (if (get-file-buffer file)
7883 (set-buffer (get-file-buffer file))
7884 (set-buffer (find-file-noselect file))
7887 (defun gnus-kill-save-kill-buffer ()
7889 (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
7890 (if (get-file-buffer file)
7892 (set-buffer (get-file-buffer file))
7893 (if (buffer-modified-p)
7895 (kill-buffer (current-buffer)))))))
7897 (defun gnus-article-fetch-field (field)
7899 (set-buffer gnus-article-buffer)
7903 (narrow-to-region 1 (save-excursion
7904 (search-forward "\n\n" nil t) (point)))
7907 (mail-fetch-field field)
7910 (defun gnus-kill-file-enter-kill (field regexp level date edit)
7911 ;; Enter kill file entry.
7912 ;; FIELD: String containing the name of the header field to kill.
7913 ;; REGEXP: The string to kill.
7914 ;; LEVEL: How much to raise the score by.
7915 ;; DATE: A date string for expire kill or nil for permanent kills.
7916 ;; EDIT: Allow the user to edit REGEXP iff non-nil.
7918 (gnus-kill-set-kill-buffer)
7919 (goto-char (point-min))
7920 (let ((regexp (if edit
7921 (read-string (concat "Add " level " to articles with "
7922 (downcase field) " matching: ")
7925 entry string kill beg)
7926 (setq entry (if date (cons regexp date) regexp)
7927 string (format "(gnus-raise %S (quote %S) %S)\n"
7929 (while (and (setq beg (point))
7931 (setq kill (read (current-buffer)))
7933 (or (not (eq (nth 0 kill) 'gnus-raise))
7934 (not (string= (downcase (nth 1 kill)) (downcase field)))
7935 (not (eq (nth 3 kill) level))))
7939 (goto-char (point-min))
7941 (let ((list (nth 2 kill)))
7942 (if (and (listp list) (eq 'quote (car list)))
7943 (setq list (car (cdr list))))
7946 (if (and (listp list) (listp (cdr list)))
7947 (list 'quote (cons entry list))
7948 (list 'quote (list entry list)))))
7949 (delete-region beg (point))
7950 (insert (gnus-pp-gnus-kill kill)))
7951 (gnus-kill-file-apply-string string))
7952 ;; Added by by Sudish Joseph <joseph@cis.ohio-state.edu>.
7954 (message "Added kill file entry %s: %s" (downcase field) regexp))))
7956 (defun gnus-kill-file-set-variable (symbol value)
7957 ;; Set SYMBOL to VALUE in the kill file.
7959 (gnus-kill-set-kill-buffer)
7960 (goto-char (point-min))
7961 (let ((string (format "(setq %S %S)\n" symbol value))
7963 (while (and (setq beg (point))
7965 (setq kill (read (current-buffer)))
7967 (or (not (eq (nth 0 kill) 'setq))
7968 (not (eq (nth 1 kill) symbol))))
7972 (goto-char (point-min))
7974 (delete-region beg (point))
7977 (defun gnus-kill-file-set-expunge-below (level)
7978 "Automatically expunge articles with score below LEVEL."
7980 (setq level (if level
7981 (prefix-numeric-value level)
7982 gnus-summary-default-interest))
7983 (gnus-kill-file-set-variable 'expunge-below level)
7984 (message "Set expunge below level to %d." level))
7986 (defun gnus-kill-file-set-mark-below (level)
7987 "Automatically mark articles with score below LEVEL as killed."
7989 (setq level (if level
7990 (prefix-numeric-value level)
7991 gnus-summary-default-interest))
7992 (gnus-kill-file-set-variable 'mark-below level)
7993 (message "Set mark below level to %d." level))
7995 (defun gnus-kill-file-temporarily-raise-by-subject (level &optional header)
7996 "Temporarily raise score by LEVEL for current subject.
7997 See `gnus-kill-expiry-days'."
7999 (gnus-kill-file-raise-by-subject level header (current-time-string)))
8001 (defun gnus-kill-file-temporarily-raise-by-author (level &optional header)
8002 "Temporarily raise score by LEVEL for current author.
8003 See `gnus-kill-expiry-days'."
8005 (gnus-kill-file-raise-by-author level header (current-time-string)))
8007 (defun gnus-kill-file-temporarily-raise-by-thread (level &optional header)
8008 "Temporarily raise score by LEVEL for current thread.
8009 See `gnus-kill-expiry-days'."
8011 (gnus-kill-file-enter-kill
8013 (regexp-quote (header-id (or header gnus-current-headers)))
8015 (current-time-string)
8018 (defun gnus-kill-file-temporarily-raise-by-xref (level &optional header)
8019 "Insert temporary KILL commands for articles that have been crossposted.
8020 By default use the current crossposted groups.
8021 See `gnus-kill-expiry-days'."
8023 (gnus-kill-file-raise-by-xref level header (current-time-string)))
8025 (defun gnus-kill-file-raise-by-subject (level &optional header date)
8026 "Raise score by LEVEL for current subject."
8028 (gnus-kill-file-enter-kill
8031 (gnus-simplify-subject
8032 (header-subject (or header gnus-current-headers))))
8037 (defun gnus-kill-file-raise-by-author (level &optional header date)
8038 "Raise score by LEVEL for current author."
8040 (gnus-kill-file-enter-kill
8042 (regexp-quote (header-from (or header gnus-current-headers)))
8047 (defun gnus-kill-file-raise-by-xref (level &optional header date)
8048 "Raise score by LEVEL for articles that have been crossposted.
8049 By default use the current crossposted groups."
8051 (let ((xref (header-xref (or header gnus-current-headers)))
8055 (while (string-match " \\([^ \t]+\\):" xref start)
8056 (setq start (match-end 0))
8059 (substring xref (match-beginning 1) (match-end 1)))
8060 gnus-newsgroup-name))
8061 (gnus-kill-file-enter-kill
8063 (concat " " (regexp-quote group) ":")
8068 (defun gnus-kill-file-raise-followups-to-author
8069 (level &optional header)
8070 "Raise score for all followups to the current author."
8072 (let ((name (header-from (or header gnus-current-headers)))
8075 (gnus-kill-set-kill-buffer)
8076 (goto-char (point-min))
8077 (setq name (read-string (concat "Add " level
8078 " to followup articles to: ")
8079 (regexp-quote name)))
8081 (format "(gnus-kill %S %S '(gnus-summary-temporarily-raise-by-thread %S))\n"
8084 (gnus-kill-file-apply-string string))
8085 (message "Added permanent kill file entry for followups to %s." name)))
8087 (defun gnus-kill-file-temporarily-lower-by-subject (level &optional header)
8088 "Temporarily lower score by LEVEL for current subject.
8089 See `gnus-kill-expiry-days'."
8091 (gnus-kill-file-lower-by-subject level header (current-time-string)))
8093 (defun gnus-kill-file-temporarily-lower-by-author (level &optional header)
8094 "Temporarily lower score by LEVEL for current author.
8095 See `gnus-kill-expiry-days'."
8097 (gnus-kill-file-lower-by-author level header (current-time-string)))
8099 (defun gnus-kill-file-temporarily-lower-by-thread (level &optional header)
8100 "Temporarily lower score by LEVEL for current thread.
8101 See `gnus-kill-expiry-days'."
8103 (gnus-kill-file-temporarily-raise-by-thread (- level) header))
8105 (defun gnus-kill-file-temporarily-lower-by-xref (level &optional header)
8106 "Insert temporary KILL commands for articles that have been crossposted.
8107 By default use the current crossposted groups.
8108 See `gnus-kill-expiry-days'."
8110 (gnus-kill-file-lower-by-xref level header (current-time-string)))
8112 (defun gnus-kill-file-lower-by-subject (level &optional header date)
8113 "Lower score by LEVEL for current subject."
8115 (gnus-kill-file-raise-by-subject (- level) header date))
8117 (defun gnus-kill-file-lower-by-author (level &optional header date)
8118 "Lower score by LEVEL for current author."
8120 (gnus-kill-file-raise-by-author (- level) header date))
8122 (defun gnus-kill-file-lower-by-xref (level &optional header date)
8123 "Lower score by LEVEL for articles that have been crossposted.
8124 By default use the current crossposted groups."
8125 (gnus-kill-file-raise-by-xref (- level) header date))
8127 (defun gnus-kill-file-lower-followups-to-author
8128 (level &optional header)
8129 "Lower score for all followups to the current author."
8131 (gnus-kill-file-raise-followups-to-author (- level) header))
8133 (defun gnus-kill-file-apply-buffer ()
8134 "Apply current buffer to current newsgroup."
8136 (if (and gnus-current-kill-article
8137 (get-buffer gnus-summary-buffer))
8138 ;; Assume newsgroup is selected.
8139 (gnus-kill-file-apply-string (buffer-string))
8140 (ding) (message "No newsgroup is selected.")))
8142 (defun gnus-kill-file-apply-string (string)
8143 "Apply STRING to current newsgroup."
8145 (let ((string (concat "(progn \n" string "\n)" )))
8147 (save-window-excursion
8148 (pop-to-buffer gnus-summary-buffer)
8149 (eval (car (read-from-string string)))))))
8151 (defun gnus-kill-file-apply-last-sexp ()
8152 "Apply sexp before point in current buffer to current newsgroup."
8154 (if (and gnus-current-kill-article
8155 (get-buffer gnus-summary-buffer))
8156 ;; Assume newsgroup is selected.
8159 (save-excursion (forward-sexp -1) (point)) (point))))
8161 (save-window-excursion
8162 (pop-to-buffer gnus-summary-buffer)
8163 (eval (car (read-from-string string))))))
8164 (ding) (message "No newsgroup is selected.")))
8166 (defun gnus-kill-file-exit ()
8167 "Save a KILL file, then return to the previous buffer."
8170 (let ((killbuf (current-buffer)))
8171 ;; We don't want to return to Article buffer.
8172 (and (get-buffer gnus-article-buffer)
8173 (bury-buffer gnus-article-buffer))
8174 ;; Delete the KILL file windows.
8175 (delete-windows-on killbuf)
8176 ;; Restore last window configuration if available.
8177 (and gnus-winconf-kill-file
8178 (set-window-configuration gnus-winconf-kill-file))
8179 (setq gnus-winconf-kill-file nil)
8180 ;; Kill the KILL file buffer. Suggested by tale@pawl.rpi.edu.
8181 (kill-buffer killbuf)))
8183 ;; Basic ideas by emv@math.lsa.umich.edu (Edward Vielmetti)
8185 (defun gnus-batch-kill ()
8187 Usage: emacs -batch -l gnus -f gnus-batch-kill NEWSGROUP ..."
8188 (if (not noninteractive)
8189 (error "gnus-batch-kill is to be used only with -batch"))
8194 (gnus-parse-n-options
8195 (apply (function concat)
8196 (mapcar (lambda (g) (concat g " "))
8197 command-line-args-left))))
8198 (yes (car yes-and-no))
8199 (no (cdr yes-and-no))
8200 ;; Disable verbose message.
8201 (gnus-novice-user nil)
8202 (gnus-large-newsgroup nil))
8203 ;; Eat all arguments.
8204 (setq command-line-args-left nil)
8207 ;; Apply kills to specified newsgroups in command line arguments.
8208 (setq newsrc (copy-sequence gnus-newsrc-assoc))
8210 (setq group (car (car newsrc)))
8211 (setq subscribed (nth 1 (car newsrc)))
8212 (setq newsrc (cdr newsrc))
8214 (not (zerop (car (gnus-gethash group gnus-newsrc-hashtb))))
8216 (string-match yes group) t)
8218 (not (string-match no group))))
8220 (gnus-summary-read-group group nil t)
8221 (if (eq (current-buffer) (get-buffer gnus-summary-buffer))
8222 (gnus-summary-exit t))
8225 ;; Finally, exit Emacs.
8226 (set-buffer gnus-group-buffer)
8232 (defun gnus-Newsgroup-kill-file (newsgroup)
8233 "Return the name of a KILL file of NEWSGROUP.
8234 If NEWSGROUP is nil, return the global KILL file instead."
8235 (cond ((or (null newsgroup)
8236 (string-equal newsgroup ""))
8237 ;; The global KILL file is placed at top of the directory.
8238 (expand-file-name gnus-kill-file-name
8239 (or gnus-kill-files-directory "~/News")))
8240 (gnus-use-long-file-name
8241 ;; Append ".KILL" to capitalized newsgroup name.
8242 (expand-file-name (concat (gnus-capitalize-newsgroup newsgroup)
8243 "." gnus-kill-file-name)
8244 (or gnus-kill-files-directory "~/News")))
8246 ;; Place "KILL" under the hierarchical directory.
8247 (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
8248 "/" gnus-kill-file-name)
8249 (or gnus-kill-files-directory "~/News")))
8252 (defun gnus-newsgroup-kill-file (newsgroup)
8253 "Return the name of a KILL file of NEWSGROUP.
8254 If NEWSGROUP is nil, return the global KILL file instead."
8255 (cond ((or (null newsgroup)
8256 (string-equal newsgroup ""))
8257 ;; The global KILL file is placed at top of the directory.
8258 (expand-file-name gnus-kill-file-name
8259 (or gnus-kill-files-directory "~/News")))
8260 (gnus-use-long-file-name
8261 ;; Append ".KILL" to newsgroup name.
8262 (expand-file-name (concat newsgroup "." gnus-kill-file-name)
8263 (or gnus-kill-files-directory "~/News")))
8265 ;; Place "KILL" under the hierarchical directory.
8266 (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
8267 "/" gnus-kill-file-name)
8268 (or gnus-kill-files-directory "~/News")))
8272 (defalias 'gnus-expunge 'gnus-summary-delete-marked-with)
8274 (defun gnus-apply-kill-file ()
8275 "Apply KILL file to the current newsgroup.
8276 Returns the number of articles killed."
8277 (let ((kill-files (list (gnus-newsgroup-kill-file nil)
8278 (gnus-newsgroup-kill-file gnus-newsgroup-name)))
8279 (unreads (length gnus-newsgroup-unreads))
8280 (mark-below gnus-summary-default-interest)
8281 (gnus-summary-inhibit-highlight t)
8286 (if (file-exists-p (car kill-files))
8288 (find-file (car kill-files))
8289 (goto-char (point-min))
8292 (setq form (condition-case nil
8293 (read (current-buffer)) (error nil))))
8294 (if (eq (car form) 'gnus-kill)
8296 (delete-region beg (point))
8297 (insert (or (eval form) "")))
8300 (setq kill-files (cdr kill-files))))
8301 (if expunge-below (gnus-summary-expunge-below expunge-below))
8302 (if mark-below (gnus-summary-mark-below mark-below ?X) )
8303 (let (gnus-summary-inhibit-highlight)
8304 (gnus-summary-update-lines))
8306 (let ((nunreads (- unreads (length gnus-newsgroup-unreads))))
8308 (message "Killed %d articles" nunreads))
8312 ;; Kill changes and new format by suggested by JWZ and Sudish Joseph
8313 ;; <joseph@cis.ohio-state.edu>.
8314 (defun gnus-kill (field regexp &optional exe-command all)
8315 "If FIELD of an article matches REGEXP, execute COMMAND.
8316 Optional 1st argument COMMAND is default to
8317 (gnus-summary-mark-as-read nil \"X\").
8318 If optional 2nd argument ALL is non-nil, articles marked are also applied to.
8319 If FIELD is an empty string (or nil), entire article body is searched for.
8320 COMMAND must be a lisp expression or a string representing a key sequence."
8321 ;; We don't want to change current point nor window configuration.
8323 (save-window-excursion
8324 ;; Selected window must be Summary buffer to execute keyboard
8325 ;; macros correctly. See command_loop_1.
8326 (switch-to-buffer gnus-summary-buffer 'norecord)
8327 (goto-char (point-min)) ;From the beginning.
8328 (let ((kill-list regexp)
8329 (date (current-time-string))
8330 (command (or exe-command '(gnus-summary-mark-as-read
8331 nil gnus-kill-file-mark)))
8333 (if (listp kill-list)
8335 (if (not (consp (cdr kill-list)))
8336 ;; It's on the form (regexp . date).
8337 (if (= 0 (gnus-execute field (car kill-list)
8338 command nil (not all)))
8339 (if (> (gnus-days-between (cdr kill-list) date)
8340 gnus-kill-expiry-days)
8342 (setcdr kill-list date))
8343 (while (setq kill (car kill-list))
8345 ;; It's a temporary kill.
8347 (setq kdate (cdr kill))
8348 (if (= 0 (gnus-execute field (car kill) command
8350 (if (> (gnus-days-between kdate date)
8351 gnus-kill-expiry-days)
8352 ;; Time limit has been exceeded, so we
8353 ;; remove the match.
8355 (setcdr prev (cdr kill-list))
8356 (setq regexp (cdr regexp))))
8357 ;; Successful kill. Set the date to today.
8358 (setcdr kill date)))
8359 ;; It's a permanent kill.
8360 (gnus-execute field kill command nil (not all)))
8361 (setq prev kill-list)
8362 (setq kill-list (cdr kill-list))))
8363 (gnus-execute field kill-list command nil (not all)))
8367 (nconc (list 'gnus-kill field
8368 (if (consp regexp) (list 'quote regexp) regexp))
8369 (if (or exe-command all) (list (list 'quote exe-command)))
8370 (if all (list t) nil)))))
8372 (defun gnus-pp-gnus-kill (object)
8373 (if (or (not (consp (nth 2 object)))
8374 (not (consp (cdr (nth 2 object))))
8375 (and (eq 'quote (car (nth 2 object)))
8376 (not (consp (cdr (car (cdr (nth 2 object))))))))
8377 (concat "\n" (prin1-to-string object))
8379 (set-buffer (get-buffer-create "*Gnus PP*"))
8380 (buffer-disable-undo (current-buffer))
8382 (insert (format "\n(gnus-kill %S\n '(" (nth 1 object)))
8383 (let ((klist (car (cdr (nth 2 object))))
8386 (insert (if first (progn (setq first nil) "") "\n ")
8387 (prin1-to-string (car klist)))
8388 (setq klist (cdr klist))))
8391 (insert "\n '" (prin1-to-string (nth 3 object))))
8396 (buffer-substring (point-min) (point-max))
8397 (kill-buffer (current-buffer))))))
8399 (defun gnus-days-between (date1 date2)
8400 ;; Return the number of days between date1 and date2.
8401 (let ((d1 (mapcar (lambda (s) (and s (string-to-int s)) )
8402 (timezone-parse-date date1)))
8403 (d2 (mapcar (lambda (s) (and s (string-to-int s)) )
8404 (timezone-parse-date date2))))
8405 (- (timezone-absolute-from-gregorian
8406 (nth 1 d1) (nth 2 d1) (car d1))
8407 (timezone-absolute-from-gregorian
8408 (nth 1 d2) (nth 2 d2) (car d2)))))
8410 (defun gnus-execute (field regexp form &optional backward ignore-marked)
8411 "If FIELD of article header matches REGEXP, execute lisp FORM (or a string).
8412 If FIELD is an empty string (or nil), entire article body is searched for.
8413 If optional 1st argument BACKWARD is non-nil, do backward instead.
8414 If optional 2nd argument IGNORE-MARKED is non-nil, articles which are
8415 marked as read or ticked are ignored."
8418 function header article)
8419 (if (or (null field) (string-equal field ""))
8421 ;; Get access function of header filed.
8422 (setq function (intern-soft (concat "gnus-header-" (downcase field))))
8423 (if (and function (fboundp function))
8424 (setq function (symbol-function function))
8425 (error "Unknown header field: \"%s\"" field))
8426 ;; The function is a macro, so we have to check whether is has
8427 ;; been compiled or not, and make a real function out of it.
8428 ;; (if (consp (cdr function))
8429 ;; (setq function (nth 3 function))
8430 ;; (setq function (list 'byte-code (aref function 1) (aref function 2)
8431 ;; (aref function 3)))))
8432 ;; Make FORM funcallable.
8433 (if (and (listp form) (not (eq (car form) 'lambda)))
8434 (setq form (list 'lambda nil form)))
8435 ;; Starting from the current article.
8436 (while (or (not article) ; Do the first line.
8437 (gnus-summary-search-subject backward ignore-marked))
8438 (setq article (gnus-summary-article-number))
8439 (or (gnus-member-of-range article gnus-newsgroup-killed)
8441 ;; Articles marked as read, ticked and dormant
8442 ;; should be ignored.
8443 (or (not (memq article gnus-newsgroup-unreads))
8444 (memq article gnus-newsgroup-marked)
8445 (memq article gnus-newsgroup-dormant)))
8446 (gnus-execute-1 function regexp form article)
8447 (setq killed-no (1+ killed-no)))))
8450 (defun gnus-execute-1 (function regexp form article)
8456 ;; Compare with header field.
8457 (let ((header (gnus-get-header-by-number article))
8461 (setq value (funcall function header))
8462 ;; Number (Lines:) or symbol must be converted to string.
8464 (setq value (prin1-to-string value)))
8465 (setq did-kill (string-match regexp value)))
8466 (if (stringp form) ;Keyboard macro.
8467 (execute-kbd-macro form)
8469 ;; Search article body.
8470 (let ((gnus-current-article nil) ;Save article pointer.
8471 (gnus-last-article nil)
8472 (gnus-break-pages nil) ;No need to break pages.
8473 (gnus-mark-article-hook nil)) ;Inhibit marking as read.
8474 (message "Searching for article: %d..." article)
8475 (gnus-article-setup-buffer)
8476 (gnus-article-prepare article t)
8478 (set-buffer gnus-article-buffer)
8479 (goto-char (point-min))
8480 (setq did-kill (re-search-forward regexp nil t)))
8481 (if (stringp form) ;Keyboard macro.
8482 (execute-kbd-macro form)
8488 ;;; Gnus Posting Functions
8491 (defvar gnus-organization-file "/usr/lib/news/organization"
8492 "*Local news organization file.")
8494 (defvar gnus-post-news-buffer "*post-news*")
8495 (defvar gnus-winconf-post-news nil)
8497 (autoload 'news-reply-mode "rnewspost")
8499 ;;; Post news commands of Gnus Group Mode and Summary Mode
8501 (defun gnus-group-post-news ()
8504 ;; Save window configuration.
8505 (setq gnus-winconf-post-news (current-window-configuration))
8506 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
8507 (or gnus-newsgroup-name (setq gnus-newsgroup-name (gnus-group-group-name)))
8509 (gnus-post-news 'post nil)
8510 (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
8511 (not (zerop (buffer-size))))
8512 ;; Restore last window configuration.
8513 (set-window-configuration gnus-winconf-post-news)))
8514 ;; We don't want to return to Summary buffer nor Article buffer later.
8515 (if (get-buffer gnus-summary-buffer)
8516 (bury-buffer gnus-summary-buffer))
8517 (if (get-buffer gnus-article-buffer)
8518 (bury-buffer gnus-article-buffer)))
8520 (defun gnus-summary-post-news ()
8523 ;; Save window configuration.
8524 (setq gnus-winconf-post-news (current-window-configuration))
8526 (gnus-post-news 'post gnus-newsgroup-name)
8527 (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
8528 (not (zerop (buffer-size))))
8529 ;; Restore last window configuration.
8530 (set-window-configuration gnus-winconf-post-news)))
8531 ;; We don't want to return to Article buffer later.
8532 (if (get-buffer gnus-article-buffer)
8533 (bury-buffer gnus-article-buffer)))
8535 (defun gnus-summary-followup (yank)
8536 "Compose a followup to an article.
8537 If prefix argument YANK is non-nil, original article is yanked automatically."
8539 (gnus-summary-select-article t)
8540 (let ((headers gnus-current-headers)
8541 (gnus-newsgroup-name gnus-newsgroup-name))
8542 ;; Check Followup-To: poster.
8543 (set-buffer gnus-article-buffer)
8544 (if (and gnus-use-followup-to
8545 (string-equal "poster" (gnus-fetch-field "followup-to"))
8546 (or (not (eq gnus-use-followup-to t))
8548 "Do you want to ignore `Followup-To: poster'? "))))
8549 ;; Mail to the poster. Gnus is now RFC1036 compliant.
8550 (gnus-summary-reply yank)
8551 ;; Save window configuration.
8552 (setq gnus-winconf-post-news (current-window-configuration))
8554 (gnus-post-news 'followup headers gnus-article-buffer yank)
8555 (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
8556 (not (zerop (buffer-size))))
8557 ;; Restore last window configuration.
8558 (set-window-configuration gnus-winconf-post-news)))
8559 ;; We don't want to return to Article buffer later.
8560 (bury-buffer gnus-article-buffer))))
8562 (defun gnus-summary-followup-with-original ()
8563 "Compose a followup to an article and include the original article."
8565 (gnus-summary-followup t))
8567 (defun gnus-summary-cancel-article ()
8568 "Cancel an article you posted."
8570 (gnus-summary-select-article t)
8571 (gnus-eval-in-buffer-window gnus-article-buffer
8572 (gnus-cancel-news)))
8574 (defun gnus-summary-supersede-article ()
8575 "Compose an article that will supersede a previous article.
8576 This is done simply by taking the old article and adding a Supersedes
8577 header line with the old Message-ID."
8581 (downcase (mail-strip-quoted-names
8582 (header-from gnus-current-headers)))
8583 (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
8584 (error "This article is not yours."))
8585 (gnus-summary-select-article t)
8587 (set-buffer gnus-article-buffer)
8588 (let ((buffer-read-only nil))
8589 (goto-char (point-min))
8590 (search-forward "\n\n" nil t)
8591 (if (not (re-search-backward "^Message-ID: " nil t))
8592 (error "No Message-ID in this article"))))
8593 (if (gnus-post-news 'post gnus-newsgroup-name)
8596 (insert-buffer gnus-article-buffer)
8597 (goto-char (point-min))
8598 (search-forward "\n\n" nil t)
8599 (if (not (re-search-backward "^Message-ID: " nil t))
8600 (error "No Message-ID in this article")
8601 (replace-match "Supersedes: "))
8602 (search-forward "\n\n")
8604 (insert mail-header-separator))))
8607 ;;; Post a News using NNTP
8610 (fset 'sendnews 'gnus-post-news)
8613 (fset 'postnews 'gnus-post-news)
8615 (defun gnus-post-news (method &optional header article-buffer yank)
8616 "Begin editing a new USENET news article to be posted.
8617 Type \\[describe-mode] in the buffer to get a list of commands."
8619 (if (or (not gnus-novice-user)
8623 (format "%s" (car gnus-current-select-method))
8624 gnus-valid-select-methods))))
8625 (y-or-n-p "Are you sure you want to post to all of USENET? "))
8626 (let ((sumart (if (eq method 'followup)
8628 (set-buffer gnus-summary-buffer)
8629 (cons (current-buffer) gnus-current-article))))
8631 (if (and gnus-interactive-post
8632 (not gnus-expert-user)
8636 (completing-read "Newsgroup: " gnus-active-hashtb nil t)))
8637 (setq mail-reply-buffer article-buffer)
8638 (setq gnus-post-news-buffer
8640 (gnus-request-post-buffer
8641 method (if (stringp header)
8642 (gnus-group-real-name header) header)
8644 (if (eq method 'post)
8646 (delete-other-windows)
8647 (switch-to-buffer post-buf))
8648 (delete-other-windows)
8651 (switch-to-buffer article-buffer)
8652 (pop-to-buffer post-buf))
8653 (switch-to-buffer post-buf)))
8654 (gnus-overload-functions)
8655 (make-local-variable 'gnus-article-reply)
8656 (make-local-variable 'gnus-article-check-size)
8657 (setq gnus-article-reply sumart)
8658 ;; Handle author copy using FCC field.
8659 (if gnus-author-copy
8661 (mail-position-on-field "FCC")
8662 (insert gnus-author-copy)))
8663 (goto-char (point-min))
8664 (if (and (eq method 'post) (not header))
8666 (search-forward (concat "\n" mail-header-separator "\n"))
8669 (run-hooks 'news-reply-header-hook)
8670 (mail-yank-original nil)))
8671 (if gnus-post-prepare-function
8672 (funcall gnus-post-prepare-function
8673 (if (stringp header) header gnus-newsgroup-name))))))
8674 (setq gnus-article-check-size (cons (buffer-size) (gnus-article-checksum)))
8678 (defun gnus-inews-news ()
8679 "Send a news message."
8681 ;; Check whether the article is a Good Net Citizen.
8682 (if (not (gnus-inews-check-post))
8685 ;; Looks ok, so we do the nasty.
8686 (let* ((case-fold-search nil)
8687 (server-running (gnus-server-opened gnus-select-method))
8688 (reply gnus-article-reply))
8690 ;; Connect to default NNTP server if necessary.
8691 ;; Suggested by yuki@flab.fujitsu.junet.
8692 (gnus-start-news-server) ;Use default server.
8693 ;; NNTP server must be opened before current buffer is modified.
8695 (goto-char (point-min))
8696 (run-hooks 'news-inews-hook)
8701 (goto-char (point-min))
8702 (search-forward (concat "\n" mail-header-separator "\n"))
8705 ;; Correct newsgroups field: change sequence of spaces to comma and
8706 ;; eliminate spaces around commas. Eliminate imbedded line breaks.
8707 (goto-char (point-min))
8708 (if (search-forward-regexp "^Newsgroups: +" nil t)
8712 (if (re-search-forward "^[^ \t]" nil 'end)
8715 (goto-char (point-min))
8716 (replace-regexp "\n[ \t]+" " ") ;No line breaks (too confusing)
8717 (goto-char (point-min))
8718 (replace-regexp "[ \t\n]*,[ \t\n]*\\|[ \t]+" ",")
8721 ;; Mail the message too if To: or Cc: exists.
8722 (if (or (mail-fetch-field "to" nil t)
8723 (mail-fetch-field "cc" nil t))
8724 (if gnus-mail-send-method
8726 (message "Sending via mail...")
8728 (funcall gnus-mail-send-method)
8729 (message "Sending via mail... done"))
8731 (message "No mailer defined. To: and/or Cc: fields ignored.")
8734 ;; Send to NNTP server.
8735 (message "Posting to USENET...")
8736 (if (gnus-inews-article)
8738 (message "Posting to USENET... done")
8740 (get-buffer (car reply))
8741 (buffer-name (car reply)))
8744 (set-buffer gnus-summary-buffer)
8745 (gnus-summary-mark-article-as-replied
8747 ;; We cannot signal an error.
8748 (ding) (message "Article rejected: %s"
8749 (gnus-status-message gnus-select-method)))
8750 (set-buffer-modified-p nil))
8751 ;; If NNTP server is opened by gnus-inews-news, close it by myself.
8753 (gnus-close-server gnus-current-select-method))
8754 (and (fboundp 'bury-buffer) (bury-buffer))
8755 ;; Restore last window configuration.
8756 (and gnus-winconf-post-news
8757 (set-window-configuration gnus-winconf-post-news))
8758 (setq gnus-winconf-post-news nil))))
8760 (defun gnus-inews-check-post ()
8761 "Check whether the post looks ok."
8763 ;; First check for an empty Subject line.
8767 (goto-char (point-min))
8768 (progn (search-forward (concat "\n" mail-header-separator "\n"))
8770 (goto-char (point-min))
8771 (if (or (not (re-search-forward "^Subject: +" nil t))
8774 "The Subject field is empty. Do you really want to post this article? ")
8776 ;; Then use the (size . checksum) variable to see whether the
8777 ;; article is empty or has only quoted text.
8778 (if (and (= (buffer-size) (car gnus-article-check-size))
8779 (= (gnus-article-checksum) (cdr gnus-article-check-size)))
8780 (yes-or-no-p "It looks like there's no new text in your article. Really post it? ")
8783 (defun gnus-article-checksum ()
8787 (setq sum (logxor sum (following-char)))
8791 (defun gnus-cancel-news ()
8792 "Cancel an article you posted."
8794 (if (yes-or-no-p "Do you really want to cancel this article? ")
8800 ;; Get header info. from original article.
8802 (gnus-article-show-all-headers)
8803 (goto-char (point-min))
8804 (search-forward "\n\n" nil 'move)
8805 (narrow-to-region (point-min) (point))
8806 (setq from (mail-fetch-field "from"))
8807 (setq newsgroups (mail-fetch-field "newsgroups"))
8808 (setq message-id (mail-fetch-field "message-id"))
8809 (setq distribution (mail-fetch-field "distribution")))
8810 ;; Verify if the article is absolutely user's by comparing
8811 ;; user id with value of its From: field.
8814 (downcase (mail-strip-quoted-names from))
8815 (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
8817 (ding) (message "This article is not yours."))
8818 ;; Make control article.
8819 (set-buffer (get-buffer-create " *Gnus-canceling*"))
8820 (buffer-disable-undo (current-buffer))
8822 (insert "Newsgroups: " newsgroups "\n"
8823 "Subject: cancel " message-id "\n"
8824 "Control: cancel " message-id "\n"
8825 mail-header-separator "\n"
8827 ;; Send the control article to NNTP server.
8828 (message "Canceling your article...")
8829 (if (gnus-inews-article)
8830 (message "Canceling your article... done")
8831 (ding) (message "Failed to cancel your article"))
8832 ;; Kill the article buffer.
8833 (kill-buffer (current-buffer))
8838 ;;; Lowlevel inews interface
8840 (defun gnus-inews-article ()
8841 "Post an article in current buffer using NNTP protocol."
8842 (let ((artbuf (current-buffer))
8843 (tmpbuf (get-buffer-create " *Gnus-posting*")))
8845 (goto-char (point-max))
8846 ;; require a newline at the end for inews to append .signature to
8847 (or (= (preceding-char) ?\n)
8849 ;; Prepare article headers. All message body such as signature
8850 ;; must be inserted before Lines: field is prepared.
8852 (goto-char (point-min))
8856 (search-forward (concat "\n" mail-header-separator "\n"))
8859 (gnus-inews-insert-headers)
8863 (buffer-disable-undo (current-buffer))
8865 (insert-buffer-substring artbuf)
8866 ;; Remove the header separator.
8867 (goto-char (point-min))
8868 (search-forward (concat "\n" mail-header-separator "\n"))
8869 (replace-match "\n\n")
8870 ;; This hook may insert a signature.
8871 (run-hooks 'gnus-prepare-article-hook)
8872 ;; Run final inews hooks. This hook may do FCC.
8873 ;; The article must be saved before being posted because
8874 ;; `gnus-request-post' modifies the buffer.
8875 (run-hooks 'gnus-inews-article-hook)
8876 ;; Post an article to NNTP server.
8877 ;; Return NIL if post failed.
8879 (gnus-request-post gnus-current-select-method)
8880 (kill-buffer (current-buffer)))
8883 (defun gnus-inews-insert-headers ()
8884 "Prepare article headers.
8885 Fields already prepared in the buffer are not modified.
8886 Fields in `gnus-required-headers' will be generated."
8888 (let ((date (gnus-inews-date))
8889 (message-id (gnus-inews-message-id))
8890 (organization (gnus-inews-organization)))
8891 (goto-char (point-min))
8892 (and (memq 'Path gnus-required-headers)
8893 (or (mail-fetch-field "path")
8894 (gnus-insert-end "Path: " (gnus-inews-path) "\n")))
8895 (and (memq 'From gnus-required-headers)
8896 (or (mail-fetch-field "from")
8897 (gnus-insert-end "From: " (gnus-inews-user-name) "\n")))
8898 ;; If there is no subject, make Subject: field.
8899 (and (memq 'Subject gnus-required-headers)
8900 (or (mail-fetch-field "subject")
8901 (gnus-insert-end "Subject: \n")))
8902 ;; If there is no newsgroups, make Newsgroups: field.
8903 (and (memq 'Newsgroups gnus-required-headers)
8904 (or (mail-fetch-field "newsgroups")
8905 (gnus-insert-end "Newsgroups: \n")))
8907 (memq 'Message-ID gnus-required-headers)
8909 (if (mail-fetch-field "message-id")
8911 (goto-char (point-min))
8912 (re-search-forward "^Message-ID" nil t)
8913 (delete-region (progn (beginning-of-line) (point))
8914 (progn (forward-line 1) (point)))))
8915 (gnus-insert-end "Message-ID: " message-id "\n")))
8917 (memq 'Date gnus-required-headers)
8918 (or (mail-fetch-field "date")
8919 (gnus-insert-end "Date: " date "\n")))
8920 ;; Optional fields in RFC977 and RFC1036
8922 (memq 'Organization gnus-required-headers)
8923 (or (mail-fetch-field "organization")
8924 (let ((begin (point-max))
8927 (gnus-insert-end "Organization: " organization "\n")
8928 (fill-region-as-paragraph begin (point-max)))))
8929 (and (memq 'Distribution gnus-required-headers)
8930 (or (mail-fetch-field "distribution")
8931 (gnus-insert-end "Distribution: \n")))
8932 (and (memq 'Lines gnus-required-headers)
8933 (or (mail-fetch-field "lines")
8934 (gnus-insert-end "Lines: " (gnus-inews-lines) "\n")))
8935 (and (memq 'X-Newsreader gnus-required-headers)
8936 (or (mail-fetch-field "x-newsreader")
8937 (gnus-insert-end "X-Newsreader: " gnus-version "\n")))
8941 (defun gnus-insert-end (&rest args)
8943 (goto-char (point-max))
8944 (apply 'insert args)))
8946 (defun gnus-inews-insert-signature ()
8947 "Insert signature file in current article buffer.
8948 If there is a file named .signature-DISTRIBUTION. Set the variable to
8949 nil to prevent appending the signature file automatically.
8950 Signature file is specified by the variable gnus-signature-file."
8954 (if gnus-signature-file
8955 (expand-file-name gnus-signature-file nil)))
8957 (goto-char (point-min))
8958 (search-forward "\n\n")
8959 (narrow-to-region (point-min) (point))
8960 (setq distribution (mail-fetch-field "distribution"))
8964 ;; Insert signature.
8965 (if (file-exists-p signature)
8967 (goto-char (point-max))
8969 (insert-file-contents signature)))
8972 (defun gnus-inews-do-fcc ()
8973 "Process FCC: fields in current article buffer.
8974 Unless the first character of the field is `|', the article is saved
8975 to the specified file using the function specified by the variable
8976 gnus-author-copy-saver. The default function rmail-output saves in
8977 Unix mailbox format.
8978 If the first character is `|', the contents of the article is send to
8979 a program specified by the rest of the value."
8980 (let ((fcc-list nil)
8982 (case-fold-search t)) ;Should ignore case.
8985 (goto-char (point-min))
8986 (search-forward "\n\n")
8987 (narrow-to-region (point-min) (point))
8988 (goto-char (point-min))
8989 (while (re-search-forward "^FCC:[ \t]*" nil t)
8991 (cons (buffer-substring
8995 (skip-chars-backward " \t")
8998 (delete-region (match-beginning 0)
8999 (progn (forward-line 1) (point))))
9000 ;; Process FCC operations.
9003 (setq fcc-file (car fcc-list))
9004 (setq fcc-list (cdr fcc-list))
9005 (cond ((string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" fcc-file)
9006 (let ((program (substring fcc-file
9007 (match-beginning 1) (match-end 1))))
9008 ;; Suggested by yuki@flab.fujitsu.junet.
9009 ;; Send article to named program.
9010 (call-process-region (point-min) (point-max) shell-file-name
9011 nil nil nil "-c" program)
9014 ;; Suggested by hyoko@flab.fujitsu.junet.
9015 ;; Save article in Unix mail format by default.
9016 (if (and gnus-author-copy-saver
9017 (not (eq gnus-author-copy-saver 'rmail-output)))
9018 (funcall gnus-author-copy-saver fcc-file)
9019 (if (and (file-readable-p fcc-file) (rmail-file-p fcc-file))
9020 (gnus-output-to-rmail fcc-file)
9021 (rmail-output fcc-file 1 t t)))
9027 (defun gnus-inews-path ()
9029 (let ((login-name (gnus-inews-login-name)))
9030 (cond ((null gnus-use-generic-path)
9031 (concat (nth 1 gnus-select-method) "!" login-name))
9032 ((stringp gnus-use-generic-path)
9033 ;; Support GENERICPATH. Suggested by vixie@decwrl.dec.com.
9034 (concat gnus-use-generic-path "!" login-name))
9038 (defun gnus-inews-user-name ()
9039 "Return user's network address as `NAME@DOMAIN (FULLNAME)'."
9040 (let ((full-name (gnus-inews-full-name)))
9041 (concat (or user-mail-address
9042 (if (or gnus-user-login-name gnus-use-generic-from
9043 gnus-local-domain (getenv "DOMAINNAME"))
9044 (concat (gnus-inews-login-name) "@"
9045 (gnus-inews-domain-name gnus-use-generic-from))
9047 ;; User's full name.
9048 (cond ((string-equal full-name "") "")
9049 ((string-equal full-name "&") ;Unix hack.
9050 (concat " (" (user-login-name) ")"))
9052 (concat " (" full-name ")")))
9055 (defun gnus-inews-login-name ()
9056 "Return user login name.
9057 Got from the variable `gnus-user-login-name' and the function
9059 (or gnus-user-login-name (user-login-name)))
9061 (defun gnus-inews-full-name ()
9062 "Return user full name.
9063 Got from the variable `gnus-user-full-name', the environment variable
9064 NAME, and the function `user-full-name'."
9065 (or gnus-user-full-name
9066 (getenv "NAME") (user-full-name)))
9068 (defun gnus-inews-domain-name (&optional genericfrom)
9069 "Return user's domain name.
9070 If optional argument GENERICFROM is a string, use it as the domain
9071 name; if it is non-nil, strip of local host name from the domain name.
9072 If the function `system-name' returns full internet name and the
9073 domain is undefined, the domain name is got from it."
9074 (if (or genericfrom gnus-local-domain (getenv "DOMAINNAME"))
9075 (let ((domain (or (if (stringp genericfrom) genericfrom)
9076 (getenv "DOMAINNAME")
9078 ;; Function `system-name' may return full internet name.
9079 ;; Suggested by Mike DeCorte <mrd@sun.soe.clarkson.edu>.
9080 (if (string-match "\\." (system-name))
9081 (substring (system-name) (match-end 0)))
9082 (read-string "Domain name (no host): ")))
9083 (host (or (if (string-match "\\." (system-name))
9084 (substring (system-name) 0 (match-beginning 0)))
9086 (if (string-equal "." (substring domain 0 1))
9087 (setq domain (substring domain 1)))
9088 ;; Support GENERICFROM as same as standard Bnews system.
9089 ;; Suggested by ohm@kaba.junet and vixie@decwrl.dec.com.
9090 (cond ((null genericfrom)
9091 (concat host "." domain))
9092 ;;((stringp genericfrom) genericfrom)
9094 (substring user-mail-address (1+ (string-match "@" user-mail-address)))))
9096 (defun gnus-inews-message-id ()
9097 "Generate unique Message-ID for user."
9098 ;; Message-ID should not contain a slash and should be terminated by
9099 ;; a number. I don't know the reason why it is so.
9100 (concat "<" (gnus-inews-unique-id) "@" (gnus-inews-domain-name) ">"))
9102 (defun gnus-inews-unique-id ()
9103 "Generate unique ID from user name and current time."
9104 (let ((date (current-time-string))
9105 (name (gnus-inews-login-name)))
9106 (if (string-match "^[^ ]+ \\([^ ]+\\)[ ]+\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) [0-9][0-9]\\([0-9][0-9]\\)"
9108 (concat (upcase name) "."
9109 (substring date (match-beginning 6) (match-end 6)) ;Year
9110 (substring date (match-beginning 1) (match-end 1)) ;Month
9111 (substring date (match-beginning 2) (match-end 2)) ;Day
9112 (substring date (match-beginning 3) (match-end 3)) ;Hour
9113 (substring date (match-beginning 4) (match-end 4)) ;Minute
9114 (substring date (match-beginning 5) (match-end 5)) ;Second
9116 (error "Cannot understand current-time-string: %s." date))
9119 (defun gnus-current-time-zone (time)
9120 "The local time zone in effect at TIME, or nil if not known."
9121 (let ((z (and (fboundp 'current-time-zone) (current-time-zone time))))
9122 (if (and z (car z)) z gnus-local-timezone)))
9124 (defun gnus-inews-date ()
9125 "Date string of today.
9126 If `current-time-zone' works, or if `gnus-local-timezone' is set correctly,
9127 this yields a date that conforms to RFC 822. Otherwise a buggy date will
9128 be generated; this might work with some older news servers."
9129 (let* ((now (and (fboundp 'current-time) (current-time)))
9130 (zone (gnus-current-time-zone now)))
9132 (gnus-inews-valid-date now zone)
9133 ;; No timezone info.
9134 (gnus-inews-buggy-date now))))
9136 (defun gnus-inews-valid-date (&optional time zone)
9137 "A date string that represents TIME and conforms to the Usenet standard.
9138 TIME is optional and defaults to the current time.
9139 Some older versions of Emacs always act as if TIME is nil.
9140 The optional argument ZONE specifies the local time zone (default GMT)."
9141 (timezone-make-date-arpa-standard
9142 (if (fboundp 'current-time)
9143 (current-time-string time)
9144 (current-time-string))
9147 (defun gnus-inews-buggy-date (&optional time)
9148 "A buggy date string that represents TIME.
9149 TIME is optional and defaults to the current time.
9150 Some older versions of Emacs always act as if TIME is nil."
9151 (let ((date (if (fboundp 'current-time)
9152 (current-time-string time)
9153 (current-time-string))))
9154 (if (string-match "^[^ ]+ \\([^ ]+\\)[ ]+\\([0-9]+\\) \\([0-9:]+\\) [0-9][0-9]\\([0-9][0-9]\\)"
9156 (concat (substring date (match-beginning 2) (match-end 2)) ;Day
9158 (substring date (match-beginning 1) (match-end 1)) ;Month
9160 (substring date (match-beginning 4) (match-end 4)) ;Year
9162 (substring date (match-beginning 3) (match-end 3))) ;Time
9163 (error "Cannot understand current-time-string: %s." date))
9166 (defun gnus-inews-organization ()
9167 "Return user's organization.
9168 The ORGANIZATION environment variable is used if defined.
9169 If not, the variable gnus-local-organization is used instead.
9170 If the value begins with a slash, it is taken as the name of a file
9171 containing the organization."
9172 ;; The organization must be got in this order since the ORGANIZATION
9173 ;; environment variable is intended for user specific while
9174 ;; gnus-local-organization is for machine or organization specific.
9176 (let* ((private-file (expand-file-name "~/.organization" nil))
9177 (organization (or (getenv "ORGANIZATION")
9178 gnus-local-organization
9180 (and (stringp organization)
9181 (> (length organization) 0)
9182 (string-equal (substring organization 0 1) "/")
9183 ;; Get it from the user and system file.
9184 ;; Suggested by roland@wheaties.ai.mit.edu (Roland McGrath).
9185 (let ((dist (mail-fetch-field "distribution")))
9187 (cond ((file-exists-p (concat organization "-" dist))
9188 (concat organization "-" dist))
9189 ((file-exists-p organization) organization)
9190 ((file-exists-p gnus-organization-file)
9191 gnus-organization-file)
9194 (cond ((not (stringp organization)) nil)
9195 ((and (string-equal (substring organization 0 1) "/")
9196 (file-exists-p organization))
9197 ;; If the first character is `/', assume it is the name of
9198 ;; a file containing the organization.
9200 (let ((tmpbuf (get-buffer-create " *Gnus organization*")))
9203 (insert-file-contents organization)
9204 (prog1 (buffer-string)
9205 (kill-buffer tmpbuf))
9207 ((string-equal organization private-file) nil) ;No such file
9211 (defun gnus-inews-lines ()
9212 "Count the number of lines and return numeric string."
9216 (goto-char (point-min))
9217 (search-forward "\n\n" nil 'move)
9218 (int-to-string (count-lines (point) (point-max))))))
9222 ;;; Gnus Mail Functions
9225 (autoload 'news-mail-reply "rnewspost")
9226 (autoload 'news-mail-other-window "rnewspost")
9228 ;;; Mail reply commands of Gnus Summary Mode
9230 (defun gnus-summary-reply (yank)
9231 "Reply mail to news author.
9232 If prefix argument YANK is non-nil, original article is yanked automatically.
9233 Customize the variable gnus-mail-reply-method to use another mailer."
9235 ;; Bug fix by jbw@bigbird.bu.edu (Joe Wells)
9236 ;; Stripping headers should be specified with mail-yank-ignored-headers.
9237 (gnus-summary-select-article t)
9238 (setq gnus-winconf-post-news (current-window-configuration))
9239 (let ((gnus-newsgroup-name gnus-newsgroup-name))
9240 (bury-buffer gnus-article-buffer)
9241 (funcall gnus-mail-reply-method yank)))
9243 (defun gnus-summary-reply-with-original ()
9244 "Reply mail to news author with original article.
9245 Customize the variable gnus-mail-reply-method to use another mailer."
9247 (gnus-summary-reply t))
9249 (defun gnus-summary-mail-forward ()
9250 "Forward the current message to another user.
9251 Customize the variable gnus-mail-forward-method to use another mailer."
9253 (gnus-summary-select-article t)
9254 (set-buffer gnus-article-buffer)
9255 (let ((gnus-newsgroup-name gnus-newsgroup-name))
9256 (funcall gnus-mail-forward-method)))
9258 (defun gnus-summary-mail-other-window ()
9259 "Compose mail in other window.
9260 Customize the variable `gnus-mail-other-window-method' to use another
9263 (let ((gnus-newsgroup-name gnus-newsgroup-name))
9264 (funcall gnus-mail-other-window-method)))
9266 (defun gnus-mail-reply-using-mail (&optional yank to-address)
9268 (set-buffer gnus-summary-buffer)
9269 (let ((info (nth 2 (gnus-gethash gnus-newsgroup-name gnus-newsrc-hashtb)))
9270 (group (gnus-group-real-name gnus-newsgroup-name))
9271 (cur (cons (current-buffer) gnus-current-article))
9272 from subject date to reply-to message-of
9273 references message-id sender follow-to)
9274 (set-buffer (get-buffer-create "*mail*"))
9276 (make-local-variable 'gnus-article-reply)
9277 (setq gnus-article-reply cur)
9278 (local-set-key "\C-c\C-c" 'gnus-mail-send-and-exit)
9279 (local-set-key "\C-c\C-y" 'gnus-mail-yank-original)
9280 (if (and (buffer-modified-p)
9282 (not (y-or-n-p "Unsent article being composed; erase it? ")))
9286 (set-buffer gnus-article-buffer)
9287 (goto-char (point-min))
9288 (narrow-to-region (point-min)
9289 (progn (search-forward "\n\n") (point)))
9290 (set-text-properties (point-min) (point-max) nil)
9291 (if (and (boundp 'gnus-reply-to-function)
9292 gnus-reply-to-function)
9295 (gnus-narrow-to-headers)
9296 (setq follow-to (funcall gnus-reply-to-function group)))))
9297 (setq from (mail-fetch-field "from"))
9298 (setq date (mail-fetch-field "date"))
9301 (string-match " *at \\| *@ \\| *(\\| *<" from)))
9303 (concat (if stop-pos (substring from 0 stop-pos) from)
9304 "'s message of " date))))
9305 (setq sender (mail-fetch-field "sender"))
9306 (setq subject (or (mail-fetch-field "subject")
9308 (or (string-match "^[Rr][Ee]:" subject)
9309 (setq subject (concat "Re: " subject)))
9310 (setq reply-to (mail-fetch-field "reply-to"))
9311 (setq references (mail-fetch-field "references"))
9312 (setq message-id (mail-fetch-field "message-id"))
9314 (setq news-reply-yank-from from)
9315 (setq news-reply-yank-message-id message-id)
9316 (mail-setup (or to-address follow-to reply-to from sender)
9317 subject message-of nil gnus-article-buffer nil)
9318 ;; Fold long references line to follow RFC1036.
9319 (mail-position-on-field "References")
9320 (let ((begin (- (point) (length "References: ")))
9323 (if references (insert references))
9324 (if (and references message-id) (insert " "))
9325 (if message-id (insert message-id))
9326 ;; The region must end with a newline to fill the region
9327 ;; without inserting extra newline.
9328 (fill-region-as-paragraph begin (1+ (point))))
9329 (goto-char (point-min))
9330 (search-forward (concat "\n" mail-header-separator "\n"))
9332 (let ((last (point)))
9333 (run-hooks 'news-reply-header-hook)
9334 (mail-yank-original nil)
9337 (let ((mail (current-buffer)))
9338 (switch-to-buffer gnus-article-buffer)
9339 (delete-other-windows)
9340 (switch-to-buffer-other-window mail))
9341 (delete-other-windows)
9342 (switch-to-buffer (current-buffer))))))
9344 (defun gnus-mail-yank-original ()
9346 (run-hooks 'news-reply-header-hook)
9347 (mail-yank-original nil))
9349 (defun gnus-mail-send-and-exit ()
9351 (let ((reply gnus-article-reply))
9352 (mail-send-and-exit nil)
9354 (get-buffer (car reply))
9355 (buffer-name (car reply)))
9357 (set-buffer (car reply))
9358 (gnus-summary-mark-article-as-replied
9360 (if gnus-winconf-post-news
9361 (set-window-configuration gnus-winconf-post-news)))
9363 (defun gnus-mail-forward-using-mail ()
9364 "Forward the current message to another user using mail."
9365 ;; This is almost a carbon copy of rmail-forward in rmail.el.
9366 (let ((forward-buffer (current-buffer))
9368 (concat "[" gnus-newsgroup-name "] "
9369 (or (gnus-fetch-field "Subject") "")))
9371 ;; If only one window, use it for the mail buffer.
9372 ;; Otherwise, use another window for the mail buffer
9373 ;; so that the Rmail buffer remains visible
9374 ;; and sending the mail will get back to it.
9375 (if (if (one-window-p t)
9376 (mail nil nil subject)
9377 (mail-other-window nil nil subject))
9379 (setq beg (goto-char (point-max)))
9380 (insert "------- Start of forwarded message -------\n")
9381 (insert-buffer forward-buffer)
9382 (goto-char (point-max))
9383 (insert "------- End of forwarded message -------\n")
9384 ;; Suggested by Sudish Joseph <joseph@cis.ohio-state.edu>.
9386 (while (setq beg (next-single-property-change (point) 'invisible))
9388 (delete-region beg (or (next-single-property-change
9391 ;; You have a chance to arrange the message.
9392 (run-hooks 'gnus-mail-forward-hook)))))
9394 (defun gnus-mail-other-window-using-mail ()
9395 "Compose mail other window using mail."
9396 (news-mail-other-window)
9397 (gnus-overload-functions))
9404 (defvar gnus-dribble-ignore nil)
9406 (defun gnus-dribble-file-name ()
9407 (concat gnus-startup-file "-dribble"))
9409 (defun gnus-dribble-open ()
9412 (setq gnus-dribble-buffer (find-file-noselect (gnus-dribble-file-name))))
9413 (buffer-disable-undo (current-buffer))
9414 (bury-buffer gnus-dribble-buffer)
9416 (goto-char (point-max))))
9418 (defun gnus-dribble-enter (string)
9419 (if (not gnus-dribble-ignore)
9420 (let ((obuf (current-buffer)))
9421 (set-buffer gnus-dribble-buffer)
9422 (insert string "\n")
9423 (set-window-point (get-buffer-window (current-buffer)) (point-max))
9424 (set-buffer obuf))))
9426 (defun gnus-dribble-read-file ()
9427 (let ((dribble-file (gnus-dribble-file-name)))
9429 (set-buffer (setq gnus-dribble-buffer
9431 (file-name-nondirectory dribble-file))))
9432 (gnus-add-current-to-buffer-list)
9434 (set-visited-file-name dribble-file)
9435 (buffer-disable-undo (current-buffer))
9436 (bury-buffer (current-buffer))
9437 (set-buffer-modified-p nil)
9438 (let ((auto (make-auto-save-file-name))
9439 (gnus-dribble-ignore t))
9440 (if (or (file-exists-p auto) (file-exists-p dribble-file))
9442 (if (file-newer-than-file-p auto dribble-file)
9443 (setq dribble-file auto))
9444 (insert-file-contents dribble-file)
9445 (if (not (zerop (buffer-size)))
9446 (set-buffer-modified-p t))
9447 (if (y-or-n-p "Auto-save file exists. Do you want to read it? ")
9449 (message "Reading %s..." dribble-file)
9450 (eval-current-buffer)
9451 (message "Reading %s...done" dribble-file)))))))))
9453 (defun gnus-dribble-delete-file ()
9455 (set-buffer gnus-dribble-buffer)
9456 (let ((auto (make-auto-save-file-name)))
9457 (if (file-exists-p auto)
9459 (if (file-exists-p (gnus-dribble-file-name))
9460 (delete-file (gnus-dribble-file-name)))
9462 (set-buffer-modified-p nil))))
9464 (defun gnus-dribble-save ()
9465 (if (and gnus-dribble-buffer
9466 (buffer-name gnus-dribble-buffer))
9468 (set-buffer gnus-dribble-buffer)
9471 (defun gnus-dribble-clear ()
9473 (if (and gnus-dribble-buffer
9474 (buffer-name (get-buffer gnus-dribble-buffer)))
9476 (set-buffer gnus-dribble-buffer)
9478 (set-buffer-modified-p nil)
9479 (setq buffer-saved-size (buffer-size))))))
9482 ;;; Server Communication
9485 (defun gnus-start-news-server (&optional confirm)
9486 "Open a method for getting news.
9487 If CONFIRM is non-nil, the user will be asked for an NNTP server."
9489 (if gnus-current-select-method
9490 ;; Stream is already opened.
9492 ;; Open NNTP server.
9493 (if (null gnus-nntp-service) (setq gnus-nntp-server nil))
9496 ;; Read server name with completion.
9497 (setq gnus-nntp-server
9498 (completing-read "NNTP server: "
9499 (cons (list gnus-nntp-server)
9500 gnus-secondary-servers)
9501 nil nil gnus-nntp-server))
9502 (setq gnus-select-method
9503 (list 'nntp gnus-nntp-server)))
9505 (if (and gnus-nntp-server
9506 (stringp gnus-nntp-server)
9507 (not (string= gnus-nntp-server "")))
9508 (setq gnus-select-method
9509 (cond ((or (string= gnus-nntp-server "")
9510 (string= gnus-nntp-server "::"))
9511 (list 'nnspool (system-name)))
9512 ((string-match ":" gnus-nntp-server)
9513 (list 'nnmh gnus-nntp-server))
9515 (list 'nntp gnus-nntp-server))))))
9517 (setq how (car gnus-select-method))
9518 (setq where (car (cdr gnus-select-method)))
9519 (cond ((eq how 'nnspool)
9521 (message "Looking up local news spool..."))
9524 (message "Looking up mh spool..."))
9527 (setq gnus-current-select-method gnus-select-method)
9528 (run-hooks 'gnus-open-server-hook)
9530 ;; gnus-open-server-hook might have opened it
9531 (gnus-server-opened gnus-select-method)
9532 (gnus-open-server gnus-select-method)
9533 (error "%s" (gnus-nntp-message
9534 (format "Cannot open NNTP server on %s"
9536 gnus-select-method)))
9538 (defun gnus-check-news-server (method)
9539 "If the news server is down, start it up again."
9540 (let ((method (if method method gnus-select-method)))
9541 (if (gnus-server-opened method)
9542 ;; Stream is already opened.
9544 ;; Open NNTP server.
9545 (message "Opening server %s on %s..." (car method) (nth 1 method))
9546 (run-hooks 'gnus-open-server-hook)
9548 (or (gnus-server-opened method)
9549 (gnus-open-server method)))))
9551 (defun gnus-nntp-message (&optional message)
9552 "Check the status of the NNTP server.
9553 If the status of the server is clear and MESSAGE is non-nil, MESSAGE
9554 is returned insted of the status string."
9555 (let ((status (gnus-status-message gnus-current-select-method))
9556 (message (or message "")))
9557 (if (and (stringp status) (> (length status) 0))
9560 (defun gnus-get-function (method function)
9561 (let ((func (intern (format "%s-%s" (car method) function))))
9562 (if (not (fboundp func))
9564 (require (car method))
9565 (if (not (fboundp func))
9566 (error "No such function: %s" func))))
9569 ;; Specifying port number suggested by Stephane Laveau <laveau@corse.inria.fr>.
9570 (defun gnus-open-server (method)
9571 (apply (gnus-get-function method 'open-server) (cdr method)))
9573 (defun gnus-close-server (method)
9574 (funcall (gnus-get-function method 'close-server) (nth 1 method)))
9576 (defun gnus-request-list (method)
9577 (funcall (gnus-get-function method 'request-list) (nth 1 method)))
9579 (defun gnus-request-list-newsgroups (method)
9580 (funcall (gnus-get-function method 'request-list-newsgroups) (nth 1 method)))
9582 (defun gnus-server-opened (method)
9583 (funcall (gnus-get-function method 'server-opened) (nth 1 method)))
9585 (defun gnus-status-message (method)
9586 (funcall (gnus-get-function method 'status-message) (nth 1 method)))
9588 (defun gnus-request-group (group &optional dont-check)
9589 (let ((method (gnus-find-method-for-group group)))
9590 (funcall (gnus-get-function method 'request-group)
9591 (gnus-group-real-name group) (nth 1 method) dont-check)))
9593 (defun gnus-retrieve-headers (articles group)
9594 (let ((method (gnus-find-method-for-group group)))
9595 (funcall (gnus-get-function method 'retrieve-headers)
9596 articles (gnus-group-real-name group) (nth 1 method))))
9598 (defun gnus-request-article (article group buffer)
9599 (let ((method (gnus-find-method-for-group group)))
9600 (funcall (gnus-get-function method 'request-article)
9601 article (gnus-group-real-name group) (nth 1 method) buffer)))
9603 (defun gnus-request-head (article group)
9604 (let ((method (gnus-find-method-for-group group)))
9605 (funcall (gnus-get-function method 'request-head)
9606 article (gnus-group-real-name group) (nth 1 method))))
9608 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
9609 (defun gnus-request-post-buffer (post header artbuf)
9610 (let* ((group gnus-newsgroup-name)
9611 (info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
9613 (if (and gnus-post-method
9614 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
9616 (format "%s" (car gnus-current-select-method))
9617 gnus-valid-select-methods)))
9619 gnus-current-select-method)))
9620 (funcall (gnus-get-function method 'request-post-buffer)
9621 post header artbuf (gnus-group-real-name group) info)))
9623 (defun gnus-request-post (method)
9624 (and gnus-post-method
9625 (memq 'post (assoc (format "%s" (car method))
9626 gnus-valid-select-methods))
9627 (setq method gnus-post-method))
9628 (funcall (gnus-get-function method 'request-post)
9631 (defun gnus-request-expire-articles (articles group &optional force)
9632 (let ((method (gnus-find-method-for-group group)))
9633 (funcall (gnus-get-function method 'request-expire-articles)
9634 articles (gnus-group-real-name group) (nth 1 method)
9637 (defun gnus-request-move-article (article group server accept-function)
9638 (let ((method (gnus-find-method-for-group group)))
9639 (funcall (gnus-get-function method 'request-move-article)
9640 article (gnus-group-real-name group)
9641 (nth 1 method) accept-function)))
9643 (defun gnus-request-accept-article (group)
9644 (let ((func (if (symbolp group) group
9645 (car (gnus-find-method-for-group group)))))
9646 (funcall (intern (format "%s-request-accept-article" func))
9647 (gnus-group-real-name group))))
9649 (defun gnus-find-method-for-group (group)
9650 (let ((info (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
9656 (defun gnus-check-backend-function (func group)
9657 (let ((method (if (stringp group) (car (gnus-find-method-for-group group))
9659 (fboundp (intern (format "%s-%s" method func)))))
9661 (defun gnus-methods-using (method)
9662 (let ((valids gnus-valid-select-methods)
9665 (if (memq method (car valids))
9666 (setq outs (cons (car valids) outs)))
9667 (setq valids (cdr valids)))
9671 ;;; Active & Newsrc File Handling
9674 ;; Newsrc related functions.
9675 ;; Gnus internal format of gnus-newsrc-assoc:
9676 ;; (("alt.general" 3 (1 . 1))
9677 ;; ("alt.misc" 3 ((1 . 10) (12 . 15)))
9678 ;; ("alt.test" 7 (1 . 99) (45 57 93)) ...)
9679 ;; The first item is the group name; the second is the subscription
9680 ;; level; the third is either a range of a list of ranges of read
9681 ;; articles, the optional fourth element is a list of marked articles,
9682 ;; the optional fifth element is the select method.
9684 ;; Gnus internal format of gnus-newsrc-hashtb:
9685 ;; (95 ("alt.general" 3 (1 . 1)) ("alt.misc" 3 ((1 . 10) (12 . 15))) ...)
9686 ;; This is the entry for "alt.misc". The first element is the number
9687 ;; of unread articles in "alt.misc". The cdr of this entry is the
9688 ;; element *before* "alt.misc" in gnus-newsrc-assoc, which makes is
9689 ;; trivial to remove or add new elements into gnus-newsrc-assoc
9690 ;; without scanning the entire list. So, to get the actual information
9691 ;; of "alt.misc", you'd say something like
9692 ;; (nth 2 (gnus-gethash "alt.misc" gnus-newsrc-hashtb))
9694 ;; Gnus internal format of gnus-active-hashtb:
9698 ;; The only element in each entry in this hash table is a range of
9699 ;; (possibly) available articles. (Articles in this range may have
9700 ;; been expired or cancelled.)
9702 ;; Gnus internal format of gnus-killed-list and gnus-zombie-list:
9703 ;; ("alt.misc" "alt.test" "alt.general" ...)
9705 (defun gnus-setup-news (&optional rawfile level)
9706 "Setup news information.
9707 If RAWFILE is non-nil, the .newsrc file will also be read.
9708 If LEVEL is non-nil, the news will be set up at level LEVEL."
9709 (let ((init (not (and gnus-newsrc-assoc
9712 ;; Clear some variables to re-initialize news information.
9714 (setq gnus-newsrc-assoc nil
9715 gnus-active-hashtb nil))
9716 ;; Read the acitve file and create `gnus-active-hashtb'.
9717 ;; If `gnus-read-active-file' is nil, then we just create an empty
9718 ;; hash table. The partial filling out of the hash table will be
9719 ;; done in `gnus-get-unread-articles'.
9720 (if (and gnus-read-active-file (not level))
9721 (gnus-read-active-file)
9722 (setq gnus-active-hashtb (make-vector 4095 0)))
9724 ;; Read the newsrc file and create `gnus-newsrc-hashtb'.
9725 (if init (gnus-read-newsrc-file rawfile))
9726 ;; Find the number of unread articles in each non-dead group.
9727 (gnus-get-unread-articles (or level 7))
9728 ;; Find new newsgroups and treat them.
9729 (if (and init gnus-check-new-newsgroups gnus-read-active-file (not level))
9730 (gnus-find-new-newsgroups))
9731 (if (and init gnus-check-bogus-newsgroups
9732 gnus-read-active-file (not level))
9733 (gnus-check-bogus-newsgroups))))
9735 (defun gnus-find-new-newsgroups ()
9736 "Search for new newsgroups and add them.
9737 Each new newsgroup will be treated with `gnus-subscribe-newsgroup-method.'
9738 The `-n' option line from .newsrc is respected."
9740 (if (not gnus-have-read-active-file) (gnus-read-active-file))
9741 (if (not (gnus-check-first-time-used))
9743 group new-newsgroups)
9744 (if (not gnus-killed-hashtb) (gnus-make-hashtable-from-killed))
9745 ;; Go though every newsgroup in `gnus-active-hashtb' and compare
9746 ;; with `gnus-newsrc-hashtb' and `gnus-killed-hashtb'.
9749 (setq group (symbol-name sym))
9750 (if (or (gnus-gethash group gnus-killed-hashtb)
9751 (gnus-gethash group gnus-newsrc-hashtb))
9753 (if (and gnus-newsrc-options-n-yes
9754 (string-match gnus-newsrc-options-n-yes group))
9756 (setq groups (1+ groups))
9757 (gnus-sethash group group gnus-killed-hashtb)
9758 (funcall gnus-subscribe-options-newsgroup-method group))
9759 (if (or (null gnus-newsrc-options-n-no)
9760 (not (string-match gnus-newsrc-options-n-no group)))
9763 (setq groups (1+ groups))
9764 (gnus-sethash group group gnus-killed-hashtb)
9765 (if gnus-subscribe-hierarchical-interactive
9766 (setq new-newsgroups (cons group new-newsgroups))
9767 (funcall gnus-subscribe-newsgroup-method group)))))))
9770 (gnus-subscribe-hierarchical-interactive new-newsgroups))
9771 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
9773 (message "%d new newsgroup%s arrived."
9774 groups (if (> groups 1) "s have" " has"))))))
9776 (defun gnus-check-first-time-used ()
9777 (if (or (file-exists-p gnus-startup-file)
9778 (file-exists-p (concat gnus-startup-file ".el"))
9779 (file-exists-p (concat gnus-startup-file ".eld")))
9781 (message "First time user; subscribing you to default groups")
9782 (let ((groups gnus-default-subscribed-newsgroups)
9786 (setq groups (or groups gnus-backup-default-subscribed-newsgroups))
9789 (setq group (symbol-name sym))
9790 (if (and gnus-newsrc-options-n-yes
9791 (string-match gnus-newsrc-options-n-yes group))
9792 (funcall gnus-subscribe-options-newsgroup-method group)
9793 (and (or (null gnus-newsrc-options-n-no)
9794 (not (string-match gnus-newsrc-options-n-no group)))
9795 (setq gnus-killed-list (cons group gnus-killed-list)))))
9798 (if (gnus-gethash (car groups) gnus-active-hashtb)
9799 (gnus-group-change-level (car groups) 3 9))
9800 (setq groups (cdr groups)))))))
9802 ;; `gnus-group-change-level' is the fundamental function for changing
9803 ;; subscription levels of newsgroups. This might mean just changing
9804 ;; from level 1 to 2, which is pretty trivial, from 2 to 6 or back
9805 ;; again, which subscribes/unsubscribes a group, which is equally
9806 ;; trivial. Changing from 1-7 to 8-9 means that you kill a group, and
9807 ;; from 8-9 to 1-7 means that you remove the group from the list of
9808 ;; killed (or zombie) groups and add them to the (kinda) subscribed
9809 ;; groups. And last but not least, moving from 8 to 9 and 9 to 8,
9810 ;; which is trivial.
9811 ;; ENTRY can either be a string (newsgroup name) or a list (if
9812 ;; FROMKILLED is t, it's a list on the format (NUM INFO-LIST),
9813 ;; otherwise it's a list in the format of the `gnus-newsrc-hashtb'
9815 ;; LEVEL is the new level of the group, OLDLEVEL is the old level and
9816 ;; PREVIOUS is the group (in hashtb entry format) to insert this group
9818 (defun gnus-group-change-level (entry level &optional oldlevel
9819 previous fromkilled)
9820 (let (group info active num)
9821 ;; Glean what info we can from the arguments
9823 (if fromkilled (setq group (nth 1 entry))
9824 (setq group (car (nth 2 entry))))
9826 (if (and (stringp entry)
9829 (setq entry (gnus-gethash entry gnus-newsrc-hashtb)))
9830 (if (and (not oldlevel)
9832 (setq oldlevel (car (cdr (nth 2 entry)))))
9833 (if (stringp previous)
9834 (setq previous (gnus-gethash previous gnus-newsrc-hashtb)))
9837 (format "(gnus-group-change-level %S %S %S %S %S)"
9838 group level oldlevel (car (nth 2 previous)) fromkilled))
9840 ;; Then we remove the newgroup from any old structures, if needed.
9841 ;; If the group was killed, we remove it from the killed or zombie
9842 ;; list. If not, and it is in fact going to be killed, we remove
9843 ;; it from the newsrc hash table and assoc.
9844 (cond ((>= oldlevel 8)
9846 (setq gnus-zombie-list (delete group gnus-zombie-list))
9847 (setq gnus-killed-list (delete group gnus-killed-list))))
9851 (gnus-sethash (car (nth 2 entry))
9852 nil gnus-newsrc-hashtb)
9854 (setcdr (gnus-gethash (car (nth 3 entry))
9857 (setcdr (cdr entry) (cdr (cdr (cdr entry))))))))
9859 ;; Finally we enter (if needed) the list where it is supposed to
9860 ;; go, and change the subscription level. If it is to be killed,
9861 ;; we enter it into the killed or zombie list.
9864 (setq gnus-zombie-list (cons group gnus-zombie-list))
9865 (setq gnus-killed-list (cons group gnus-killed-list))))
9867 ;; If the list is to be entered into the newsrc assoc, and
9868 ;; it was killed, we have to create an entry in the newsrc
9869 ;; hashtb format and fix the pointers in the newsrc assoc.
9874 (setq info (cdr entry))
9875 (setq num (car entry)))
9876 (setq active (gnus-gethash group gnus-active-hashtb))
9877 (setq num (- (1+ (cdr active)) (car active)))
9878 (setq info (list group level (cons 1 (1- (car active))))))
9879 (setq entry (cons info (if previous (cdr (cdr previous))
9880 (cdr gnus-newsrc-assoc))))
9881 (setcdr (if previous (cdr previous) gnus-newsrc-assoc)
9883 (gnus-sethash group (cons num (if previous (cdr previous)
9887 (setcdr (gnus-gethash (car (car (cdr entry)))
9890 ;; It was alive, and it is going to stay alive, so we
9891 ;; just change the level and don't change any pointers or
9892 ;; hash table entries.
9893 (setcar (cdr (car (cdr (cdr entry)))) level))))))
9895 (defun gnus-kill-newsgroup (newsgroup)
9896 "Obsolete function. Kills a newsgroup."
9897 (gnus-group-change-level (gnus-gethash newsgroup gnus-newsrc-hashtb) 9))
9899 (defun gnus-check-bogus-newsgroups (&optional confirm)
9900 "Delete bogus newsgroups.
9901 If CONFIRM is non-nil, the user has to confirm the deletion of every
9903 (let ((newsrc (cdr gnus-newsrc-assoc))
9904 (dead-lists '(gnus-killed-list gnus-zombie-list))
9906 (message "Checking bogus newsgroups...")
9907 (if (not gnus-have-read-active-file) (gnus-read-active-file))
9908 ;; Find all bogus newsgroup that are subscribed.
9910 (setq group (car (car newsrc)))
9911 (if (or (gnus-gethash group gnus-active-hashtb)
9912 (nth 4 (car newsrc))
9915 (format "Delete bogus newsgroup: %s " group)))))
9916 ;; Active newsgroup.
9918 ;; Found a bogus newsgroup.
9919 (setq bogus (cons group bogus)))
9920 (setq newsrc (cdr newsrc)))
9921 ;; Remove all bogus subscribed groups by first killing them, and
9922 ;; then removing them from the list of killed groups.
9924 (gnus-group-change-level
9925 (gnus-gethash (car bogus) gnus-newsrc-hashtb) 9)
9926 (setq gnus-killed-list (delq (car bogus) gnus-killed-list))
9927 (setq bogus (cdr bogus)))
9928 ;; Then we remove all bogus groups from the list of killed and
9929 ;; zombie groups. They are are deleted without confirmation.
9931 (setq killed (symbol-value (car dead-lists)))
9933 (setq group (car killed))
9934 (or (gnus-gethash group gnus-active-hashtb)
9935 ;; The group is bogus.
9936 (setq bogus (cons group bogus)))
9937 (setq killed (cdr killed)))
9939 (set (car dead-lists)
9940 (delq (car bogus) (symbol-value (car dead-lists))))
9941 (setq bogus (cdr bogus)))
9942 (setq dead-lists (cdr dead-lists)))
9943 (message "Checking bogus newsgroups... done")))
9945 ;; Go though `gnus-newsrc-assoc' and compare with `gnus-active-hashtb'
9946 ;; and compute how many unread articles there are in each group.
9947 (defun gnus-get-unread-articles (&optional level)
9948 (let ((newsrc (cdr gnus-newsrc-assoc))
9949 (level (or level 7))
9951 (message "Checking new news...")
9953 (setq info (car newsrc))
9954 (setq group (car info))
9956 ;; Check foreign newsgroups. If the user doesn't want to check
9957 ;; them, or they can't be checked, for instance, if the news
9958 ;; server can't be reached, we just set the number of unread
9959 ;; articles in this newsgroup to t. This means that Gnus
9960 ;; thinks that there are unread articles, but it has no idea how
9963 (and (or (if (numberp gnus-activate-foreign-newsgroups)
9964 (> (nth 1 info) gnus-activate-foreign-newsgroups)
9965 (not gnus-activate-foreign-newsgroups))
9966 (not (gnus-activate-foreign-newsgroup info)))
9968 (gnus-sethash group nil gnus-active-hashtb)
9969 (setcar (gnus-gethash group gnus-newsrc-hashtb) t))))
9971 (if (or (and (> (nth 1 info) level)
9972 (not (car (gnus-gethash group gnus-newsrc-hashtb)))
9973 (setcar (gnus-gethash group gnus-newsrc-hashtb) t))
9974 (not (or (setq active (gnus-gethash group gnus-active-hashtb))
9975 (and (not gnus-read-active-file)
9976 (setq active (gnus-activate-newsgroup
9978 ;; If this is a bogus group, there's not much we can do.
9980 (gnus-get-unread-articles-in-group info active))
9981 (setq newsrc (cdr newsrc)))
9982 (message "Checking new news... done")))
9984 ;; Create a hash table out of the newsrc alist. The `car's of the
9985 ;; alist elements are used as keys.
9986 (defun gnus-make-hashtable-from-newsrc-alist ()
9987 (let ((alist gnus-newsrc-assoc)
9989 (setq gnus-newsrc-hashtb (gnus-make-hashtable (length alist)))
9991 (setq prev (setq gnus-newsrc-assoc
9992 (cons (list "dummy.group" 0 (cons 0 0)) alist))))
9994 (gnus-sethash (car (car alist)) (cons nil prev) gnus-newsrc-hashtb)
9996 (setq alist (cdr alist)))))
9998 (defun gnus-make-hashtable-from-killed ()
9999 "Create a hash table from the killed and zombie lists."
10000 (let ((lists '(gnus-killed-list gnus-zombie-list))
10002 (setq gnus-killed-hashtb
10003 (gnus-make-hashtable
10004 (+ (length gnus-killed-list) (length gnus-zombie-list))))
10006 (setq list (symbol-value (car lists)))
10007 (setq lists (cdr lists))
10009 (gnus-sethash (car list) (car list) gnus-killed-hashtb)
10010 (setq list (cdr list))))))
10012 (defun gnus-get-unread-articles-in-group (info active)
10013 (let* (num srange lowest range group)
10014 ;; Modify the list of read articles according to what articles
10015 ;; are available; then tally the unread articles and add the
10016 ;; number to the group hash table entry.
10017 (setq range (nth 2 info))
10020 (setq num (- (1+ (cdr active)) (car active))))
10021 ((atom (car range))
10022 ;; Fix a single (num . num) range according to the
10023 ;; active hash table.
10024 (if (< (cdr range) (car active)) (setcdr range (car active)))
10025 ;; Compute number of unread articles.
10026 (setq num (max 0 (- (cdr active)
10027 (- (1+ (cdr range)) (car range))))))
10029 ;; The read list is a list of ranges. Fix them according to
10030 ;; the active hash table.
10031 (setq srange range)
10032 (setq lowest (1- (car active)))
10033 (while (and (< (cdr (car srange)) lowest))
10034 (if (and (cdr srange)
10035 (<= (cdr (car srange)) (1+ lowest)))
10037 (setcdr (car srange) (cdr (car (cdr srange))))
10038 (setcdr srange (cdr (cdr srange))))
10039 (setcdr (car srange) lowest)))
10040 ;; Compute the number of unread articles.
10042 (setq num (+ num (- (1+ (cdr (car range)))
10043 (car (car range)))))
10044 (setq range (cdr range)))
10045 (setq num (max 0 (- (cdr active) num)))))
10046 (setcar (gnus-gethash (car info) gnus-newsrc-hashtb) num)
10049 (defun gnus-activate-foreign-newsgroup (info)
10050 (and (gnus-check-news-server (nth 4 info))
10051 (gnus-activate-newsgroup (car info) (gnus-group-real-name (car info)))))
10053 (defun gnus-activate-newsgroup (group &optional real-group-name)
10055 (if (gnus-request-group group)
10057 (set-buffer nntp-server-buffer)
10059 (if (looking-at "[0-9]+ [0-9]+ \\([0-9]+\\) \\([0-9]+\\)")
10060 (gnus-sethash group
10062 (cons (string-to-int (buffer-substring (match-beginning 1)
10065 (buffer-substring (match-beginning 2)
10067 gnus-active-hashtb))))
10070 (defun gnus-update-read-articles
10071 (group unread unselected ticked &optional domarks replied expirable killed
10073 "Update the list of read and ticked articles in GROUP using the
10074 UNREAD and TICKED lists.
10075 Note: UNSELECTED has to be sorted over `<'."
10076 (let* ((active (gnus-gethash group gnus-active-hashtb))
10077 (entry (gnus-gethash group gnus-newsrc-hashtb))
10078 (number (car entry))
10079 (info (nth 2 entry))
10080 (marked (nth 3 info))
10082 (unread (sort (copy-sequence unread) (function <)))
10085 ;; There is no info on this group if it was, in fact,
10086 ;; killed. Gnus stores no information on killed groups, so
10087 ;; there's nothing to be done.
10088 ;; One could store the information somewhere temporarily,
10089 ;; perhaps... Hmmm...
10091 ;; Remove any negative articles numbers.
10092 (while (and unread (< (car unread) 0))
10093 (setq unread (cdr unread)))
10094 (if (not (and (numberp number) (= 0 number)))
10095 (setq unread (nconc unselected unread)))
10096 ;; Set the number of unread articles in gnus-newsrc-hashtb.
10097 (or (eq 'nnvirtual (car gnus-current-select-method))
10098 (setcar entry (length unread)))
10099 ;; Compute the ranges of read articles by looking at the list of
10100 ;; unread articles.
10102 (if (/= (car unread) prev)
10103 (setq read (cons (cons prev (1- (car unread))) read)))
10104 (setq prev (1+ (car unread)))
10105 (setq unread (cdr unread)))
10106 (if (<= prev (cdr active))
10107 (setq read (cons (cons prev (cdr active)) read)))
10108 ;; Enter this list into the group info.
10109 (setcar (cdr (cdr info))
10110 (if (> (length read) 1) (nreverse read) (car read)))
10111 ;; Enter the list of ticked articles.
10112 (gnus-set-marked-articles
10114 (if domarks replied (cdr (assq 'reply marked)))
10115 (if domarks expirable (cdr (assq 'expire marked)))
10116 (if domarks killed (cdr (assq 'killed marked)))
10117 (if domarks dormant (cdr (assq 'dormant marked)))
10118 (if domarks bookmark (cdr (assq 'bookmark marked)))))))
10120 (defun gnus-make-articles-unread (group articles)
10121 "Mark ARTICLES in GROUP as unread."
10122 (let ((info (nth 2 (or (gnus-gethash group gnus-newsrc-hashtb)
10123 (gnus-gethash (concat gnus-foreign-group-prefix
10125 gnus-newsrc-hashtb)))))
10126 (setcar (nthcdr 2 info)
10127 (gnus-remove-from-range (nth 2 info) articles))
10128 (gnus-group-update-group group t)))
10130 (defun gnus-read-active-file ()
10131 "Get active file from NNTP server."
10132 (gnus-group-set-mode-line)
10133 (setq gnus-have-read-active-file t)
10134 ;; Make sure a connection to NNTP server is alive.
10135 (gnus-check-news-server gnus-select-method)
10136 (let ((mesg (format "Reading active file from %s via %s..."
10137 (nth 1 gnus-select-method) (car gnus-select-method))))
10139 (if (gnus-request-list gnus-select-method) ; Get active
10141 (set-buffer nntp-server-buffer)
10142 (gnus-active-to-gnus-format)
10143 (setq gnus-have-read-active-file t)
10144 (message "%s...done" mesg))
10145 (error "Cannot read active file from NNTP server."))))
10147 ;; rewritten by jwz based on ideas from Rick Sladkey <jrs@world.std.com>
10148 ;; Further rewrites by lmi.
10149 (defun gnus-active-to-gnus-format ()
10150 "Convert active file format to internal format.
10151 Lines matching gnus-ignored-newsgroups are ignored."
10152 (let ((cur (current-buffer)))
10153 ;; Delete unnecessary lines.
10154 (goto-char (point-min))
10155 (delete-matching-lines gnus-ignored-newsgroups)
10156 ;; Make large enough hash table.
10157 (setq gnus-active-hashtb
10158 (gnus-make-hashtable (count-lines (point-min) (point-max))))
10159 ;; Store active file in hashtable.
10161 (goto-char (point-min))
10162 (if (or (re-search-forward "\n.\r?$" nil t)
10163 (goto-char (point-max)))
10165 (beginning-of-line)
10166 (narrow-to-region (point-min) (point))))
10167 (goto-char (point-min))
10168 (if (string-match "%[oO]" gnus-group-line-format)
10169 ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
10170 ;; If we want information on moderated groups, we use this
10172 (let ((mod-hashtb (make-vector 7 0))
10174 (while (not (eobp))
10175 (setq group (let ((obarray gnus-active-hashtb))
10177 (setq max (read cur))
10178 (set group (cons (read cur) max))
10179 ;; Enter moderated groups into a list.
10181 (symbol-name (let ((obarray mod-hashtb)) (read cur)))
10183 (setq gnus-moderated-list
10184 (cons (symbol-name group) gnus-moderated-list)))
10186 ;; And if we do not care about moderation, we use this loop,
10187 ;; which is faster.
10189 (while (not (eobp))
10190 ;; group gets set to a symbol interned in gnus-active-hashtb
10192 (setq group (let ((obarray gnus-active-hashtb))
10194 (setq max (read cur))
10195 (set group (cons (read cur) max))
10196 (forward-line 1)))))))
10198 (defun gnus-read-newsrc-file (&optional force)
10199 "Read startup file.
10200 If FORCE is non-nil, the .newsrc file is read."
10201 (setq gnus-current-startup-file (gnus-make-newsrc-file gnus-startup-file))
10202 ;; Reset variables that might be defined in the .newsrc.eld file.
10203 (let ((variables gnus-variable-list))
10205 (set (car variables) nil)
10206 (setq variables (cdr variables))))
10207 (let* ((newsrc-file gnus-current-startup-file)
10208 (quick-file (concat newsrc-file ".el")))
10210 ;; We always load the .newsrc.eld file. If always contains
10211 ;; much information that can not be gotten from the .newsrc
10212 ;; file (ticked articles, killed groups, foreign methods, etc.)
10213 (gnus-read-newsrc-el-file quick-file)
10216 (and (file-newer-than-file-p newsrc-file quick-file)
10217 (file-newer-than-file-p newsrc-file
10218 (concat quick-file "d")))
10219 (not gnus-newsrc-assoc))
10220 ;; We read the .newsrc file. Note that if there if a
10221 ;; .newsrc.eld file exists, it has already been read, and
10222 ;; the `gnus-newsrc-hashtb' has been created. While reading
10223 ;; the .newsrc file, Gnus will only use the information it
10224 ;; can find there for changing the data already read -
10225 ;; ie. reading the .newsrc file will not trash the data
10226 ;; already read (except for read articles).
10228 (message "Reading %s..." newsrc-file)
10229 (set-buffer (find-file-noselect newsrc-file))
10230 (buffer-disable-undo (current-buffer))
10231 (gnus-newsrc-to-gnus-format)
10232 (kill-buffer (current-buffer))
10233 (message "Reading %s... done" newsrc-file)))
10234 (gnus-dribble-read-file))))
10236 (defun gnus-read-newsrc-el-file (file)
10237 (let ((ding-file (concat file "d")))
10238 ;; We always, always read the .eld file.
10239 (message "Reading %s..." ding-file)
10240 (condition-case nil
10241 (load ding-file t t t)
10243 (gnus-make-hashtable-from-newsrc-alist)
10244 (if (not (file-newer-than-file-p file ding-file))
10246 ;; Old format quick file
10247 (message "Reading %s..." file)
10248 ;; The .el file is newer than the .eld file, so we read that one
10250 (gnus-read-old-newsrc-el-file file))))
10252 ;; Parse the old-style quick startup file
10253 (defun gnus-read-old-newsrc-el-file (file)
10254 (let (newsrc killed marked group g m len info)
10256 (let (gnus-killed-assoc gnus-marked-assoc gnus-newsrc-assoc)
10258 (condition-case nil
10261 (setq newsrc gnus-newsrc-assoc
10262 killed gnus-killed-assoc
10263 marked gnus-marked-assoc)))
10264 (setq gnus-newsrc-assoc nil)
10266 (setq group (car newsrc))
10267 (let ((info (nth 2 (gnus-gethash (car group) gnus-newsrc-hashtb))))
10270 (setcar (nthcdr 2 info) (cdr (cdr group)))
10271 (setcar (cdr info) (if (nth 1 group) 3 6))
10272 (setq gnus-newsrc-assoc (cons info gnus-newsrc-assoc)))
10273 (setq gnus-newsrc-assoc
10277 (if (nth 1 group) 3 6) (cdr (cdr group))))
10278 gnus-newsrc-assoc)))
10279 (if (setq m (assoc (car group) marked))
10280 (setcdr (cdr (cdr info)) (cons (list (cons 'tick (cdr m))) nil))))
10281 (setq newsrc (cdr newsrc)))
10282 (setq newsrc killed)
10284 (setcar newsrc (car (car newsrc)))
10285 (setq newsrc (cdr newsrc)))
10286 (setq gnus-killed-list killed))
10287 (setq gnus-newsrc-assoc (nreverse gnus-newsrc-assoc))
10288 (gnus-make-hashtable-from-newsrc-alist)))
10290 (defun gnus-make-newsrc-file (file)
10291 "Make server dependent file name by catenating FILE and server host name."
10292 (let* ((file (expand-file-name file nil))
10293 (real-file (concat file "-" (nth 1 gnus-select-method))))
10294 (if (file-exists-p real-file)
10298 ;; jwz: rewrote this function to be much more efficient, and not be subject
10299 ;; to regexp overflow errors when it encounters very long lines -- the old
10300 ;; behavior was to blow off the rest of the *file* when a line was encountered
10301 ;; that was too long to match!! Now it uses only simple looking-at calls, and
10302 ;; doesn't create as many temporary strings. It also now handles multiple
10303 ;; consecutive options lines (before it only handled the first.)
10304 ;; Tiny rewrite by lmi.
10305 (defun gnus-newsrc-to-gnus-format ()
10306 "Parse current buffer as .newsrc file."
10307 ;; We have to re-initialize these variables (except for
10308 ;; gnus-killed-list) because quick startup file may contain bogus
10310 (setq gnus-newsrc-options nil)
10311 (setq gnus-newsrc-options-n-yes nil)
10312 (setq gnus-newsrc-options-n-no nil)
10313 (setq gnus-newsrc-assoc nil)
10314 (gnus-parse-options-lines)
10315 (gnus-parse-newsrc-body))
10317 (defun gnus-parse-options-lines ()
10318 ;; newsrc.5 seems to indicate that the options line can come anywhere
10319 ;; in the file, and that there can be any number of them:
10321 ;; An options line starts with the word options (left-
10322 ;; justified). Then there are the list of options just as
10323 ;; they would be on the readnews command line. For instance:
10325 ;; options -n all !net.sf-lovers !mod.human-nets -r
10328 ;; A string of lines beginning with a space or tab after the
10329 ;; initial options line will be considered continuation
10332 ;; For now, we only accept it at the beginning of the file.
10334 (goto-char (point-min))
10335 (skip-chars-forward " \t\n")
10336 (setq gnus-newsrc-options nil)
10337 (while (looking-at "^options[ \t]*\\(.*\\)\n")
10338 ;; handle consecutive options lines
10339 (setq gnus-newsrc-options (concat gnus-newsrc-options
10340 (if gnus-newsrc-options "\n\t")
10341 (buffer-substring (match-beginning 1)
10344 (while (looking-at "[ \t]+\\(.*\\)\n")
10345 ;; handle subsequent continuation lines of this options line
10346 (setq gnus-newsrc-options (concat gnus-newsrc-options "\n\t"
10347 (buffer-substring (match-beginning 1)
10350 ;; Gather all "-n" options lines.
10353 (if gnus-newsrc-options
10354 (while (and (string-match "^[ \t]*-n\\([^\n]*\\)$"
10355 gnus-newsrc-options
10357 (setq start (match-end 0)))
10358 (setq result (concat result
10360 (substring gnus-newsrc-options
10361 (match-beginning 1)
10363 (let ((yes-and-no (and result (gnus-parse-n-options result))))
10364 (setq gnus-newsrc-options-n-yes (car yes-and-no))
10365 (setq gnus-newsrc-options-n-no (cdr yes-and-no)))
10368 (defun gnus-parse-newsrc-body ()
10369 ;; Point has been positioned after the options lines. We shouldn't
10370 ;; see any more in here.
10372 (let ((subscribe nil)
10374 (line (1+ (count-lines (point-min) (point))))
10378 (skip-chars-forward " \t")
10379 (while (not (eobp))
10381 ((= (following-char) ?\n)
10382 ;; skip blank lines
10386 (skip-chars-forward "^:!\n")
10387 (if (= (following-char) ?\n)
10388 (error "line %d is unparsable in %s" line (buffer-name)))
10390 (skip-chars-backward " \t")
10392 ;; #### note: we could avoid consing a string here by binding obarray
10393 ;; and reading the newsgroup directly into the gnus-newsrc-hashtb,
10394 ;; then setq'ing newsgroup to symbol-name of that, like we do in
10395 ;; gnus-active-to-gnus-format.
10396 (setq newsgroup (buffer-substring p (point)))
10399 (setq subscribe (= (following-char) ?:))
10400 (setq read-list nil)
10402 (forward-char 1) ; after : or !
10403 (skip-chars-forward " \t")
10404 (while (not (= (following-char) ?\n))
10405 (skip-chars-forward " \t")
10408 ((looking-at "\\([0-9]+\\)-\\([0-9]+\\)") ; a range
10413 ;; faster that buffer-substring/string-to-int
10414 (narrow-to-region (point-min) (match-end 1))
10415 (read (current-buffer)))
10417 (narrow-to-region (point-min) (match-end 2))
10418 (forward-char) ; skip over "-"
10420 (read (current-buffer))
10424 ((looking-at "[0-9]+")
10425 ;; faster that buffer-substring/string-to-int
10426 (narrow-to-region (point-min) (match-end 0))
10427 (setq p (read (current-buffer)))
10429 (setq read-list (cons (cons p p) read-list))
10432 ;; bogus chars in ranges
10435 (goto-char (match-end 0))
10436 (skip-chars-forward " \t")
10437 (cond ((= (following-char) ?,)
10440 ((= (following-char) ?\n)
10443 ;; bogus char after range
10445 ;; if we get here, the parse failed
10447 (end-of-line) ; give up on this line
10449 (message "Ignoring bogus line %d for %s in %s"
10450 line newsgroup (buffer-name))
10453 (let ((info (nth 2 (gnus-gethash newsgroup gnus-newsrc-hashtb))))
10456 (setcar (nthcdr 2 info) (nreverse read-list))
10457 (setcar (cdr info) (if subscribe 3 6))
10458 (setq gnus-newsrc-assoc (cons info gnus-newsrc-assoc)))
10459 (setq gnus-newsrc-assoc
10460 (cons (list newsgroup (if subscribe 3 6)
10461 (nreverse read-list))
10462 gnus-newsrc-assoc))))
10463 (setq gnus-killed-list (cons newsgroup gnus-killed-list)))))
10464 (setq line (1+ line))
10465 (forward-line 1))))
10466 (setq gnus-newsrc-assoc (nreverse gnus-newsrc-assoc))
10467 (gnus-make-hashtable-from-newsrc-alist)
10470 (defun gnus-parse-n-options (options)
10471 "Parse -n NEWSGROUPS options and return a cons of YES and NO regexps."
10474 (yes-or-no nil) ;`!' or not.
10476 ;; Parse each newsgroup description such as "comp.all". Commas
10477 ;; and white spaces can be a newsgroup separator.
10479 (string-match "^[ \t\n,]*\\(!?\\)\\([^- \t\n,][^ \t\n,]*\\)" options)
10481 (substring options (match-beginning 1) (match-end 1)))
10485 (match-beginning 2) (match-end 2))))
10486 (setq options (substring options (match-end 2)))
10487 ;; Rewrite "all" to ".+" not ".*". ".+" requires at least one
10489 (while (string-match "\\(^\\|\\\\[.]\\)all\\(\\\\[.]\\|$\\)" newsgroup)
10491 (concat (substring newsgroup 0 (match-end 1))
10493 (substring newsgroup (match-beginning 2)))))
10494 ;; It is yes or no.
10495 (cond ((string-equal yes-or-no "!")
10496 (setq no (cons newsgroup no)))
10497 ((string-equal newsgroup ".+")) ;Ignore `all'.
10499 (setq yes (cons newsgroup yes))))
10501 ;; Make a cons of regexps from parsing result.
10502 ;; We have to append \(\.\|$\) to prevent matching substring of
10503 ;; newsgroup. For example, "jp.net" should not match with
10505 ;; Fixes for large regexp problems are from yonezu@nak.math.keio.ac.jp.
10508 (apply (function concat)
10510 (lambda (newsgroup)
10511 (concat newsgroup "\\|"))
10513 (car yes) "\\)\\(\\.\\|$\\)"))
10516 (apply (function concat)
10518 (lambda (newsgroup)
10519 (concat newsgroup "\\|"))
10521 (car no) "\\)\\(\\.\\|$\\)")))
10524 (defun gnus-save-newsrc-file ()
10525 "Save to .newsrc FILE."
10526 ;; Note: We cannot save .newsrc file if all newsgroups are removed
10527 ;; from the variable gnus-newsrc-assoc.
10528 (and (or gnus-newsrc-assoc gnus-killed-list)
10529 gnus-current-startup-file
10531 (if (= 0 (save-excursion
10532 (set-buffer gnus-dribble-buffer)
10534 (message "(No changes need to be saved)")
10535 (if gnus-save-newsrc-file
10536 (let ((make-backup-files t)
10537 (version-control nil)
10538 (require-final-newline t)) ;Don't ask even if requested.
10539 (message "Saving %s..." gnus-current-startup-file)
10540 ;; Make backup file of master newsrc.
10541 ;; You can stop or change version control of backup file.
10542 ;; Suggested by jason@violet.berkeley.edu.
10543 (run-hooks 'gnus-save-newsrc-hook)
10544 (gnus-gnus-to-newsrc-format)
10545 (message "Saving %s... done" gnus-current-startup-file)))
10546 ;; Quickly loadable .newsrc.
10547 (set-buffer (get-buffer-create " *Gnus-newsrc*"))
10548 (gnus-add-current-to-buffer-list)
10549 (buffer-disable-undo (current-buffer))
10551 (message "Saving %s.eld..." gnus-current-startup-file)
10552 (gnus-gnus-to-quick-newsrc-format)
10553 (let ((make-backup-files nil)
10554 (version-control nil)
10555 (require-final-newline t)) ;Don't ask even if requested.
10556 (write-region 1 (point-max)
10557 (concat gnus-current-startup-file ".eld")
10559 (kill-buffer (current-buffer))
10560 (message "Saving %s.eld... done" gnus-current-startup-file)
10561 (gnus-dribble-delete-file)))))
10563 (defun gnus-gnus-to-quick-newsrc-format ()
10564 "Insert Gnus variables such as gnus-newsrc-assoc in lisp format."
10565 (insert ";; (ding) Gnus startup file.\n")
10566 (insert ";; Never delete this file - touch .newsrc instead to force Gnus\n")
10567 (insert ";; to read .newsrc.\n")
10568 (let ((variables gnus-variable-list)
10569 (gnus-newsrc-assoc (cdr gnus-newsrc-assoc))
10571 ;; insert lisp expressions.
10573 (setq variable (car variables))
10574 (and (boundp variable)
10575 (symbol-value variable)
10576 (or gnus-save-killed-list
10577 (not (or (eq variable 'gnus-killed-list)
10578 (eq variable 'gnus-zombie-list))))
10579 (insert "(setq " (symbol-name variable) " '"
10580 (prin1-to-string (symbol-value variable))
10582 (setq variables (cdr variables)))))
10584 (defun gnus-gnus-to-newsrc-format ()
10585 (let ((newsrc (cdr gnus-newsrc-assoc))
10588 (set-buffer (create-file-buffer gnus-startup-file))
10589 (buffer-disable-undo (current-buffer))
10591 (if gnus-newsrc-options (insert "options " gnus-newsrc-options "\n"))
10593 (setq group (car newsrc))
10594 (insert (car group) (if (>= (nth 1 group) 6) "!" ":"))
10595 (if (setq ranges (nth 2 group))
10598 (gnus-ranges-to-newsrc-format
10599 (if (atom (car ranges)) (list ranges) ranges))))
10601 (setq newsrc (cdr newsrc)))
10602 (write-region 1 (point-max) gnus-current-startup-file nil 'nomesg)
10603 (kill-buffer (current-buffer)))))
10605 (defun gnus-ranges-to-newsrc-format (ranges)
10606 "Insert ranges of read articles."
10607 (let ((range nil)) ;Range is a pair of BEGIN and END.
10609 (setq range (car ranges))
10610 (setq ranges (cdr ranges))
10611 (cond ((= (car range) (cdr range))
10612 (if (= (car range) 0)
10613 (setq ranges nil) ;No unread articles.
10614 (insert (int-to-string (car range)))
10615 (if ranges (insert ","))
10618 (insert (int-to-string (car range))
10620 (int-to-string (cdr range)))
10621 (if ranges (insert ","))
10625 (defun gnus-read-descriptions-file ()
10626 (message "Reading descriptions file...")
10627 (if (not (gnus-request-list-newsgroups gnus-select-method))
10629 (message "Couldn't read newsgroups descriptions")
10632 (setq gnus-description-hashtb
10633 (gnus-make-hashtable (length gnus-active-hashtb)))
10636 (set-buffer nntp-server-buffer)
10637 (goto-char (point-min))
10638 (delete-non-matching-lines "^[a-zA-Z\\.0-9]+[ \t]")
10639 (goto-char (point-min))
10640 (if (or (search-forward "\n.\n" nil t)
10641 (goto-char (point-max)))
10643 (beginning-of-line)
10644 (narrow-to-region (point-min) (point))))
10645 (goto-char (point-min))
10646 (while (not (eobp))
10647 (setq group (let ((obarray gnus-description-hashtb))
10648 (read (current-buffer))))
10649 (skip-chars-forward " \t")
10650 (set group (buffer-substring
10651 (point) (save-excursion (end-of-line) (point))))
10652 (forward-line 1))))
10653 (message "Reading descriptions file...done")
10658 ;;; gnus.el ends here