-Sun Dec 10 10:38:47 1995 Lars Magne Ingebrigtsen <larsi@narfi.ifi.uio.no>
+Thu Dec 14 10:48:51 1995 Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+ * gnus.el (gnus-summary-toggle-header): Don't do that hook dance.
+
+Thu Dec 14 10:02:08 1995 Lars Magne Ingebrigtsen <larsi@narfi.ifi.uio.no>
+
+ * gnus-xmas.el (gnus-xmas-read-event-char): New function.
+
+ * gnus.el (gnus-summary-last-subject): New function.
+ (gnus-summary-next-article): Understand all key events.
+ (gnus-summary-walk-group-buffer): New function.
+ (gnus-read-event-char): New function.
+
+Wed Dec 13 16:06:29 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-msg.el (gnus-new-mail, gnus-mail-reply): Didn't insert
+ Gcc.
+
+ * gnus-score.el (gnus-score-load-file): Allow `adapt-file' atom.
+ (gnus-score-adaptive): Use it.
+
+ * gnus.el (gnus-group-visible-select-group): New command and
+ keystroke.
+ (gnus-read-save-file-name): Extend the syntax of
+ `gnus-split-methods'.
+ (gnus-article-archive-name): New function.
+ (gnus-split-methods): New default; use function above.
+ (gnus-summary-update-secondary-mark): Update highlighting after
+ setting secondary marks.
+
+ * nnfolder.el (nnfolder-request-group): Don't load all nnfolder
+ groups on startup.
+
+Tue Dec 12 19:48:55 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-group-insert-group-line): Number of marked (etc)
+ didn't work.
+
+Tue Dec 12 19:37:05 1995 Timo Metzemakers <metzemakers@labri.u-bordeaux.fr>
+
+ * gnus.el (gnus-summary-reselect-current-group): Really reselect
+ the group.
+
+Tue Dec 12 10:38:05 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * nnmail.el (nnmail-days-to-time): Would result in short expiry
+ times in groups with veeery long expiry times.
+
+ * gnus-vis.el (gnus-summary-highlight-line): Bind `number'.
+
+ * gnus-score.el (gnus-score-find-bnews): Protect agains bogus
+ score file names.
+
+ * gnus.el (gnus-activate-all-groups): New command and keystroke.
+
+ * gnus-vis.el (gnus-article-prev-button): New command and keystroke.
+
+ * gnus-cache.el (gnus-cache-generate-nov-databases): New command.
+
+ * gnus-score.el (gnus-score-load-file): Accept
+ `thread-mark-and-expunge' atom.
+
+ * gnus.el (gnus-newsgroup-saved): New variable.
+ (gnus-summary-set-saved-mark): New function.
+ (gnus-kill-summary-on-exit): New variable.
+ (gnus-dead-summary-mode): New minor mode.
+ (gnus-deaden-summary, gnus-summary-wake-up-the-dead): New
+ functions.
+ (gnus-summary-catchup-and-goto-next-group): Respect
+ `gnus-auto-select-next', etc.
+ (gnus-article-hide-headers): New implementation.
+ (gnus-article-header-rank): New function.
+ (gnus-article-header-less): Ditto.
+ (gnus-visible-headers, gnus-ignored-headers): Can now be lists of
+ regexps.
+ (gnus-thread-expunge-below): New variable.
+ (gnus-expunge-thread): New variable.
+
+ * gnus-mh.el (gnus-summary-save-in-folder): There is no
+ `mh-search-path'.
+
+Mon Dec 11 07:21:25 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-article-sort-functions): New variable.
+ (gnus-article-sort-by-number, gnus-article-sort-by-author,
+ gnus-article-sort-by-subject, gnus-article-sort-by-date,
+ (gnus-article-sort-by-score): New functions.
+ (gnus-thread-sort-by-number, gnus-thread-sort-by-author,
+ gnus-thread-sort-by-subject, gnus-thread-sort-by-date,
+ gnus-thread-sort-by-score, gnus-summary-sort-by-number,
+ gnus-summary-sort-by-author, gnus-summary-sort-by-subject,
+ gnus-summary-sort-by-date, gnus-summary-sort-by-score,
+ gnus-summary-sort): New implementations.
+ (gnus-summary-mode-line-format): Doc fix.
+ (gnus-insert-pseudo-articles): New variable.
+ (gnus-activate-level): New variable.
+ (gnus-get-unread-articles): Use it.
+
+ * nnkiboze.el (nnkiboze-request-delete-group): New function.
+
+ * gnus.el (gnus-subscribe-killed): New function.
+ (gnus-group-kill-group): Make mass group slaughter faster.
+ (gnus-group-kill-level): New command and keystroke.
+
+ * gnus-cache.el (gnus-cache-generate-active): Messed up the active
+ file.
+
+ * gnus.el (gnus-summary-update-secondary-mark): New function.
+ (gnus-cached-mark): New variable.
+ (gnus-gmt-to-local): Removed function.
+ (gnus-narrow-to-page): New implementation.
+
+ * gnus-cache.el (gnus-cache-enter-article): New command and
+ keystroke.
+ (gnus-cache-remove-article): Ditto.
+ (gnus-passive-cache): New variable.
+ (gnus-cached-article-p): New function.
+
+ * gnus.el (gnus-summary-mode-line-format,
+ gnus-article-mode-line-format, gnus-group-mode-line-format):
+ Include the buffer name in all mode lines.
+
+ * gnus-topic.el (gnus-topic-yank-group, gnus-topic-kill-group):
+ Allow kill/yank inside and in between topics.
+
+ * gnus.el (gnus-request-type): Wouldn't work.
+
+Sun Dec 10 13:16:49 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-topic.el (gnus-topic-insert-topic-line): Remove old text
+ properties before setting new.
+
+ * nnml.el: Understand jka-compr.
+ (nnml-generate-nov-databases): Would make empty group disappear.
+
+ * nnheader.el (nnheader-numerical-short-files): New variable.
+ (nnheader-numerical-full-files): Ditto.
+
+ * gnus-msg.el (gnus-summary-resend-message): Rename old Resent-*
+ headers.
+
+ * gnus-cache.el (gnus-cache-retrieve-headers): Allow fetching of
+ old headers.
+
+ * nnmail.el (nnmail-get-spool-files): Don't ditch procmail
+ symlinks.
+
+ * gnus-msg.el (gnus-inews-insert-signature): Don't insert
+ signature if mail-signature.
+
+ * gnus.el (gnus-group-make-help-group): Find gnus-tut in the etc
+ directory.
+
+Sun Dec 10 12:29:54 1995 Lars Magne Ingebrigtsen <larsi@narfi.ifi.uio.no>
+
+ * gnus.el (gnus-article-date-ut): Bugged out on pseudos.
+
+Sun Dec 10 10:38:47 1995 Lars Magne Ingebrigtsen <larsi@menja.ifi.uio.no>
+
+ * gnus.el: 0.21 is released.
+
* gnus.el (gnus-backlog-shutdown): New function.
(gnus-backlog-buffer): Would return a list the first time called.
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: This is a test version of this group
+Message-ID: <lars-doc0@eyesore.no>
+
+This group will look something like this, but the stuff in here is of
+little worth right now.
+
+For real documentation, you'll have to read the source code. Or you
+can read the info file, which is pretty comprehensive, if somewhat out
+of date.
+
+Gnus is currently in beta.
+
+When you happen upon a bug, please drop me a note.
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: So you want to use the new Gnus
+Message-ID: <lars-doc1@eyesore.no>
+
+Actually, since you are reading this, chances are you are already
+using the new Gnus. Congratulations.
+
+This entire newsgroup you are reading is, in fact, no real newsgroup
+at all, in the traditional sense. It is an example of one of the
+"foreign" select methods that Gnus may use.
+
+The text you are now reading is stored in the "etc" directory with the
+rest of the Emacs sources. You are using the "nndir" backend for
+accessing it. Scary, isn't it?
+
+This isn't the real documentation. `M-x info', `m gnus <RET>' to read
+that. This "newsgroup" is intended as a kinder, gentler way of getting
+people started.
+
+Gnus is a rewrite of GNUS 4.1, written by Masanobu Umeda. The rewrite
+was done by moi, yours truly, your humble servant, Lars Magne
+Ingebrigtsen. If you have a WWW browser, you can investigate to your
+heart's delight at "http://www.ifi.uio.no/~larsi/larsi.html".
+
+Much code (especially the score code) was written by Per Abrahamsen.
+
+;; Copyright (C) 1995 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;; Keywords: news
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: Starting up
+Message-ID: <lars-doc2@eyesore.no>
+
+If you are having problems with Gnus not finding your server, you have
+to set `gnus-select-method'. A "method" is a way of specifying *how*
+the news is to be found, and from *where*.
+
+Say you want to read news from you local, friendly nntp server
+"news.my.local.server".
+
+(setq gnus-select-method '(nntp "news.my.local.server"))
+
+Quite easy, huh?
+
+From the news spool:
+
+(setq gnus-select-method '(nnspool ""))
+
+From your mh-e spool:
+
+(setq gnus-select-method '(nnmh ""))
+
+There's a whole bunch of other methods for reading mail and news, see
+the "Foreign groups" article for that.
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: Where are all the groups, then?
+Message-ID: <lars-doc3@eyesore.no>
+
+If this is the first time you have used a newsreader, you won't have a
+.newsrc file. This means that Gnus will think that all the newsgroups
+on the server are "new", and kill them all.
+
+If you have a .newsrc file, the new groups will be processed with the
+function in the `gnus-subscribe-newsgroup-method' variable, which is
+`gnus-subscribe-zombies' by default.
+
+This means that all the groups have been made into "zombies" - not
+quite dead, but not exactly alive, either.
+
+Jump back to the *Group* buffer, and type `C-c C-z' to list all the
+zombie groups. Look though the list, and subscribe to the groups you
+want to read by pressing `u' on the one you think look interesting.
+
+If all the groups have been killed, type `C-c C-k' to list all the
+killed groups. Subscribe to them the same way.
+
+When you are satisfied, press `M-z' to kill all the zombie groups.
+
+Now you should have a nice list of all groups you are interested in.
+
+(If you later want to subscribe to more groups, press `C-c C-k' to
+list all the kill groups, and repeat. You can also type `U' and be
+prompted for groups to subscribe to.)
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: I want to read my mail!
+Message-ID: <lars-doc4@eyesore.no>
+
+Yes, Virginia, you can read mail with Gnus.
+
+First you have to decide which mail backend you want to use. You have
+nnml, which is a one-file-one-mail backend, which is quite nice, but
+apt to make your systems administrator go crazy and come after you
+with a shotgun.
+
+nnmbox uses a Unix mail box to store mail. Nice, but slow.
+
+nnmh uses mh-e folders, which is also a one-file-one-mail thingie, but
+slower than nnml. (It doesn't support NOV files.)
+
+So if you want to go with nnmbox, you can simply say:
+
+(setq gnus-secondary-select-methods '((nnmbox "")))
+
+(The same for the other methods, kind of.)
+
+You should also set `nnmail-split-methods' to something sensible:
+
+(setq nnmail-split-methods
+ '(("mail.junk" "From:.*Lars")
+ ("mail.misc "")))
+
+This will put all mail from me in you junk mail group, and the rest in
+"mail.misc".
+
+These groups will be subscribe the same way as the normal groups, so
+you will probably find them among the zombie groups after you set
+these variables and re-start Gnus.
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: Foreign newsgroups
+Message-ID: <lars-doc5@eyesore.no>
+
+These are groups that do not come from `gnus-select-method'.
+
+Say you want to read "alt.furniture.couches" from "news.funet.fi". You
+can then either type `B news.funet.fi <RET>' to browse that server and
+subscribe to that group, or you can type
+`M-a alt.furniture.couches<RET>nntp<RET>news.funet.fi<RET>', if you
+like to type a lot.
+
+If you want to read a directory as a newsgroup, you can create an
+nndir group, much the same way. There's a shorthand for that,
+though. If, for instance, you want to read the (ding) list archives,
+you could type `D /ftp <RET>'.
+
+There's lots more to know about foreign groups, but you have to read
+the info pages to find out more.
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: Low level changes in GNUS, or, Wrong type argument: stringp, nil
+Message-ID: <lars-doc6@eyesore.no>
+
+Gnus really isn't GNUS, even though it looks like it. If you scrape
+the surface, you'll find that most things have changed.
+
+This means that old code that relies on GNUS internals will fail.
+
+In particular, `gnus-newsrc-hashtb', `gnus-newsrc-assoc',
+`gnus-killed-list', the `nntp-header-' macros and the display formats
+have all changed. If you have some code lying around that depend on
+these, or change these, you'll have to re-write your code.
+
+Old hilit19 code does not work at all. In fact, you should probably
+remove all hihit code from all the Gnus hooks
+(`gnus-group-prepare-hook', `gnus-summary-prepare-hook' and
+`gnus-summary-article-hook'). (Well, at the very least the first
+two.) Gnus provides various integrated functions for highlighting,
+which are both faster and more accurated.
+
+There is absolutely no chance, whatsoever, of getting Gnus to work
+with Emacs 18.
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: Bugs & stuff
+Message-ID: <lars-doc7@eyesore.no>
+
+If you want to report a bug, please type `M-x gnus-bug'. This will
+give me a precice overview of your Gnus and Emacs version numbers,
+along with a look at all Gnus variables you have changed.
+
+Du not expect a reply back, but your bug should be fixed in the next
+version. If the bug persists, please re-submit your bug report.
+
+When a bug occurs, I need a recipe for how to trigger the bug. You
+have to tell me exactly what you do to uncover the bug, and you should
+(setq debug-on-error t) and send me the backtrace along with the bug
+report.
+
+If I am not able to reproduce the bug, I won't be able to fix it.
+
+I would, of course, prefer that you locate the bug, fix it, and mail
+me the patches, but one can't have everything.
+
+If you have any questions on usage, the "ding@ifi.uio.no" mailing list
+is where to post the questions.
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: How do I re-scan my mail groups?
+Message-ID: <lars-doc8@eyesore.no>
+
+Reading the active file from the nntp server is a drag.
+
+Just press `M-g' on the mail groups, and they will be re-scanned.
+
+You can also re-scan all the mail groups by putting them on level 1
+(`1 S'), and saying `1 g' to re-scan all level 1 groups.
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: How do I set up virtual newsgroups?
+Message-ID: <lars-doc9@eyesore.no>
+
+Virtual newsgroups are collections of other newsgroups. Why people
+want this is beyond me, but here goes:
+
+Create the group by saying
+
+`M-a my.virtual.newsgroup<RET>nnvirtual<RET>^rec\.aquaria\.*<RET>'
+
+This will create the group "nnvirtual:my.virtual.newsgroup", which
+will collect all articles from all the groups in the "rec.aquaria"
+hierarchy.
+
+If you want to edit the regular expression, just type `M-e' on the
+group line.
+
+Note that all the groups that are part of the virtual group have to be
+alive. This means that the cannot, absolutely not, be zombie or
+killed. They can be unsubscribed; that's no problem.
+
+You can combine groups from different servers in the same virtual
+newsgroup, something that may actually be useful. Say you have the
+group "comp.headers" on the server "news.server.no" and the same group
+on "news.server.edu". If people have posted articles with Distribution
+headers that stop propagation of their articles, combining these two
+newsgroups into one virtual newsgroup should give you a better view of
+what's going on.
+
+One caveat, though: The virtual group article numbers from the first
+source group (group A) will always be lower than the article numbers
+from the second (group B). This means that Gnus will believe that
+articles from group A are older than articles from group B. Threading
+will lessen these problems, but it might be a good idea to sort the
+threads over the date of the articles to get a correct feel for the
+flow of the groups:
+
+(setq gnus-thread-sort-functions '(gnus-thread-sort-by-date))
+
+If you only want this in virtual groups, you could say something along
+the lines of:
+
+(setq gnus-select-group-hook
+ (lambda ()
+ (if (eq 'nnvirtual (car (gnus-find-method-for-group
+ gnus-newsgroup-name)))
+ (progn
+ (make-local-variable 'gnus-thread-sort-functions)
+ (setq gnus-thread-sort-functions '(gnus-thread-sort-by-date))))))
+
+
+From lars Thu Feb 23 23:20:38 1995
+From: larsi@ifi.uio.no (ding)
+Date: Fri Feb 24 13:40:45 1995
+Subject: I want to kiboze everything in sight!
+Message-ID: <lars-doc10@eyesore.no>
+
+The nnkiboze backend collects articles that you are interested in from
+groups you are interested in. Below is a description for how you can
+gather all posts from me, and all posts about Gnus in the gnu.emacs
+hierarchy in one handy, easy to read group.
+
+Gnus will let you eat up all available machine resources, grinding
+everything to a halt. Including your nntp server. Who says Gnus isn't
+friendly?
+
+You want to do this, of course.
+
+Create an nnkiboze group the normal way:
+
+`M-a my.group<RET>nnkiboze<RET>^gnu.emacs.*<RET>'
+
+You now have a shiny new group that you can't enter. How...
+practical.
+
+But just wait, it gets worse.
+
+You now have to create a score file.
+
+`C-x C-f ~/News/nnkiboze:my.group.SCORE<RET>'
+
+Put something like the following in this file:
+
+(setq gnus-score-alist
+ '(("from"
+ ("Ingebrigtsen" nil 1000)
+ ("lmi" nil 5000))
+ ("subject"
+ ("Gnus" nil 10000))
+ (touched)))
+
+Save the file, and go to a shell window.
+
+Type:
+
+$ emacs -batch -l ~/.emacs -l nnkiboze -f nnkiboze-generate-groups
+
+Wait a few hours, and all articles from me, or articles about Gnus,
+will appear, as if by magic, in the nnkiboze group. You really want
+that, of course.
+
+Gnus actually grabs the headers of all the groups that supply the
+nnkiboze group with articles, so this isn't very kind. Pleasy only do
+it at night (`at' is a fine command), and please, *please*, limit the
+number of groups that supply articles to the group. If you specify ""
+as the address of this group, nnkiboze will ask for headers from *all*
+groups, and this is megs and megs and megs of data.
+
+Use it, don't abuse it. Be nice.
+
+
+
+
;;; Internal variables.
(defvar gnus-cache-buffer nil)
-(defvar gnus-group-alist nil)
(defvar gnus-cache-active-hashtb nil)
(defvar gnus-cache-active-altered nil)
(defun gnus-cache-close ()
"Shut down the cache."
(gnus-cache-write-active)
+ (gnus-cache-save-buffers)
(setq gnus-cache-active-hashtb nil))
(defun gnus-cache-save-buffers ()
(setq gnus-cache-buffer nil))))
(defun gnus-cache-possibly-enter-article
- (group article headers ticked dormant unread)
- (let ((number (mail-header-number headers))
- file dir)
- (if (or (not (vectorp headers)) ; This might be a dummy article.
- (< number 0) ; Reffed article.
- (and gnus-uncacheable-groups
- (string-match gnus-uncacheable-groups group))
- (not (gnus-cache-member-of-class
- gnus-cache-enter-articles ticked dormant unread))
- (file-exists-p (setq file (gnus-cache-file-name group article))))
- () ; Do nothing.
- ;; Possibly create the cache directory.
- (or (file-exists-p (setq dir (file-name-directory file)))
- (gnus-make-directory dir))
- ;; Save the article in the cache.
- (if (file-exists-p file)
- t ; The article already is saved.
- (let ((gnus-use-cache nil))
- (gnus-summary-select-article))
- (save-excursion
- (set-buffer gnus-original-article-buffer)
- (save-restriction
- (widen)
- (write-region (point-min) (point-max) file nil 'quiet))
- (gnus-cache-change-buffer group)
- (set-buffer (cdr gnus-cache-buffer))
- (goto-char (point-max))
- (forward-line -1)
- (while (condition-case ()
- (and (not (bobp))
- (> (read (current-buffer)) number))
- (error
- ;; The line was malformed, so we just remove it!!
- (gnus-delete-line)
- t))
- (forward-line -1))
- (if (bobp)
- (if (not (eobp))
- (progn
- (beginning-of-line)
- (if (< (read (current-buffer)) number)
- (forward-line 1)))
- (beginning-of-line))
- (forward-line 1))
- (beginning-of-line)
- ;; [number subject from date id references chars lines xref]
- (insert (format "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n"
- (mail-header-number headers)
- (mail-header-subject headers)
- (mail-header-from headers)
- (mail-header-date headers)
- (mail-header-id headers)
- (or (mail-header-references headers) "")
- (or (mail-header-chars headers) "")
- (or (mail-header-lines headers) "")
- (or (mail-header-xref headers) ""))))
- ;; Update the active info.
- (gnus-cache-update-active group number)
- t))))
+ (group article headers ticked dormant unread &optional force)
+ (when (or force (not (eq gnus-use-cache 'passive)))
+ (let ((number (mail-header-number headers))
+ file dir)
+ (when (and (vectorp headers) ; This might be a dummy article.
+ (> number 0) ; Reffed article.
+ (or (not gnus-uncacheable-groups)
+ (not (string-match gnus-uncacheable-groups group)))
+ (or force
+ (gnus-cache-member-of-class
+ gnus-cache-enter-articles ticked dormant unread))
+ (not (file-exists-p (setq file (gnus-cache-file-name
+ group article)))))
+ ;; Possibly create the cache directory.
+ (or (file-exists-p (setq dir (file-name-directory file)))
+ (gnus-make-directory dir))
+ ;; Save the article in the cache.
+ (if (file-exists-p file)
+ t ; The article already is saved.
+ (let ((gnus-use-cache nil))
+ (gnus-summary-select-article))
+ (save-excursion
+ (set-buffer gnus-original-article-buffer)
+ (save-restriction
+ (widen)
+ (write-region (point-min) (point-max) file nil 'quiet))
+ (gnus-cache-change-buffer group)
+ (set-buffer (cdr gnus-cache-buffer))
+ (goto-char (point-max))
+ (forward-line -1)
+ (while (condition-case ()
+ (and (not (bobp))
+ (> (read (current-buffer)) number))
+ (error
+ ;; The line was malformed, so we just remove it!!
+ (gnus-delete-line)
+ t))
+ (forward-line -1))
+ (if (bobp)
+ (if (not (eobp))
+ (progn
+ (beginning-of-line)
+ (if (< (read (current-buffer)) number)
+ (forward-line 1)))
+ (beginning-of-line))
+ (forward-line 1))
+ (beginning-of-line)
+ ;; [number subject from date id references chars lines xref]
+ (insert (format "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n"
+ (mail-header-number headers)
+ (mail-header-subject headers)
+ (mail-header-from headers)
+ (mail-header-date headers)
+ (mail-header-id headers)
+ (or (mail-header-references headers) "")
+ (or (mail-header-chars headers) "")
+ (or (mail-header-lines headers) "")
+ (or (mail-header-xref headers) ""))))
+ ;; Update the active info.
+ (gnus-cache-update-active group number)
+ (push number gnus-newsgroup-cached)
+ t)))))
(defun gnus-cache-enter-remove-article (article)
"Mark ARTICLE for later possible removal."
(defun gnus-cache-possibly-remove-articles ()
"Possibly remove some of the removable articles."
- (let ((articles gnus-cache-removeable-articles)
- (cache-articles (gnus-cache-articles-in-group gnus-newsgroup-name))
- article)
- (gnus-cache-change-buffer gnus-newsgroup-name)
- (while articles
- (if (memq (setq article (pop articles)) cache-articles)
- ;; The article was in the cache, so we see whether we are
- ;; supposed to remove it from the cache.
- (gnus-cache-possibly-remove-article
- article (memq article gnus-newsgroup-marked)
- (memq article gnus-newsgroup-dormant)
- (or (memq article gnus-newsgroup-unreads)
- (memq article gnus-newsgroup-unselected))))))
- ;; The overview file might have been modified, save it
- ;; safe because we're only called at group exit anyway
- (gnus-cache-save-buffers))
+ (unless (eq gnus-use-cache 'passive)
+ (let ((articles gnus-cache-removeable-articles)
+ (cache-articles gnus-newsgroup-cached)
+ article)
+ (gnus-cache-change-buffer gnus-newsgroup-name)
+ (while articles
+ (if (memq (setq article (pop articles)) cache-articles)
+ ;; The article was in the cache, so we see whether we are
+ ;; supposed to remove it from the cache.
+ (gnus-cache-possibly-remove-article
+ article (memq article gnus-newsgroup-marked)
+ (memq article gnus-newsgroup-dormant)
+ (or (memq article gnus-newsgroup-unreads)
+ (memq article gnus-newsgroup-unselected))))))
+ ;; The overview file might have been modified, save it
+ ;; safe because we're only called at group exit anyway.
+ (gnus-cache-save-buffers)))
(defun gnus-cache-request-article (article group)
"Retrieve ARTICLE in GROUP from the cache."
(> (cdr cache-active) (cdr active))
(setcdr active (cdr cache-active)))))
-(defun gnus-cache-retrieve-headers (articles group)
+(defun gnus-cache-retrieve-headers (articles group &optional fetch-old)
"Retrieve the headers for ARTICLES in GROUP."
- (let* ((cached (gnus-cache-articles-in-group group))
+ (let* ((cached
+ (setq gnus-newsgroup-cached (gnus-cache-articles-in-group group)))
(articles (gnus-sorted-complement articles cached))
(cache-file (gnus-cache-file-name group ".overview"))
type)
;; We first retrieve all the headers that we don't have in
;; the cache.
(let ((gnus-use-cache nil))
- (setq type (and articles (gnus-retrieve-headers articles group))))
+ (setq type (and articles
+ (gnus-retrieve-headers articles group fetch-old))))
(gnus-cache-save-buffers)
;; Then we insert the cached headers.
(save-excursion
(gnus-cache-braid-heads group cached)
type)))))
+(defun gnus-cache-enter-article (n)
+ "Enter the next N articles into the cache.
+If not given a prefix, use the process marked articles instead.
+Returns the list of articles entered."
+ (interactive "P")
+ (gnus-set-global-variables)
+ (let ((articles (gnus-summary-work-articles n))
+ article out)
+ (while articles
+ (setq article (pop articles))
+ (when (gnus-cache-possibly-enter-article
+ gnus-newsgroup-name article (gnus-summary-article-header article)
+ nil nil nil t)
+ (push article out))
+ (gnus-summary-remove-process-mark article)
+ (gnus-summary-update-secondary-mark article))
+ (gnus-summary-position-point)
+ (nreverse out)))
+
+(defun gnus-cache-remove-article (n)
+ "Remove the next N articles from the cache.
+If not given a prefix, use the process marked articles instead.
+Returns the list of articles removed."
+ (interactive "P")
+ (gnus-set-global-variables)
+ (let ((articles (gnus-summary-work-articles n))
+ article out)
+ (while articles
+ (setq article (pop articles))
+ (when (gnus-cache-possibly-remove-article article nil nil nil t)
+ (push article out))
+ (gnus-summary-remove-process-mark article)
+ (gnus-summary-update-secondary-mark article))
+ (gnus-summary-position-point)
+ (nreverse out)))
+
+(defun gnus-cached-article-p (article)
+ "Say whether ARTICLE is cached in the current group."
+ (memq article gnus-newsgroup-cached))
;;; Internal functions.
(if (stringp article) article (int-to-string article))))
(defun gnus-cache-possibly-remove-article
- (article ticked dormant unread)
+ (article ticked dormant unread &optional force)
+ "Possibly remove ARTICLE from the cache."
(let ((file (gnus-cache-file-name gnus-newsgroup-name article)))
- (if (or (not (file-exists-p file))
- (not (gnus-cache-member-of-class
- gnus-cache-remove-articles ticked dormant unread)))
- nil
+ (when (and (file-exists-p file)
+ (or force
+ (gnus-cache-member-of-class
+ gnus-cache-remove-articles ticked dormant unread)))
(save-excursion
(delete-file file)
(set-buffer (cdr gnus-cache-buffer))
(search-forward (concat "\n" (int-to-string article) "\t")
(point-max) t))
(delete-region (progn (beginning-of-line) (point))
- (progn (forward-line 1) (point))))))))
+ (progn (forward-line 1) (point)))))
+ (setq gnus-newsgroup-cached
+ (delq article gnus-newsgroup-cached))
+ t)))
(defun gnus-cache-articles-in-group (group)
+ "Return a sorted list of cached articles in GROUP."
(let ((dir (file-name-directory (gnus-cache-file-name group 1)))
articles)
- (if (not (file-exists-p dir))
- nil
- (setq articles (directory-files dir nil "^[0-9]+$" t))
- (if (not articles)
- nil
- (sort (mapcar (function (lambda (name)
- (string-to-int name)))
- articles)
- '<)))))
+ (when (file-exists-p dir)
+ (sort (mapcar (lambda (name) (string-to-int name))
+ (directory-files dir nil "^[0-9]+$" t))
+ '<))))
(defun gnus-cache-braid-nov (group cached)
(let ((cache-buf (get-buffer-create " *gnus-cache*"))
(mapatoms
(lambda (sym)
(when (and sym (boundp sym))
- (insert (symbol-name sym) " " (cdr (symbol-value sym))
- " " (car (symbol-value sym)) " y\n")))
+ (insert (format "%s %d %d y\n"
+ (symbol-name sym) (cdr (symbol-value sym))
+ (car (symbol-value sym))))))
gnus-cache-active-hashtb)
(write-region
(point-min) (point-max) gnus-cache-active-file nil 'silent))
;;;###autoload
(defun gnus-cache-generate-active (&optional directory)
"Generate the cache active file."
+ (interactive)
(let* ((top (null directory))
(directory (or directory (expand-file-name gnus-cache-directory)))
(files (directory-files directory 'full))
?/ ?.)))
nums alphs)
(when top
+ (gnus-message 5 "Generating the cache active file...")
(setq gnus-cache-active-hashtb (gnus-make-hashtable 123)))
;; Separate articles from all other files and directories.
(while files
;; Go through all the other files.
(while alphs
(when (and (file-directory-p (car alphs))
- (not (string-match "^\\.\\.?$" (file-name-nondirectory (car alphs)))))
+ (not (string-match "^\\.\\.?$"
+ (file-name-nondirectory (car alphs)))))
;; We descend directories.
(gnus-cache-generate-active (car alphs)))
(setq alphs (cdr alphs)))
;; Write the new active file.
(when top
- (gnus-cache-write-active t))))
+ (gnus-cache-write-active t)
+ (gnus-message 5 "Generating the cache active file...done"))))
+
+;;;###autoload
+(defun gnus-cache-generate-nov-databases (dir)
+ "Generate NOV files recursively starting in DIR."
+ (interactive (list gnus-cache-directory))
+ (gnus-cache-close)
+ (require 'nnml)
+ (let ((nnml-generate-active-function 'identity))
+ (nnml-generate-nov-databases dir)))
(provide 'gnus-cache)
(summary-highlight
article-highlight
summary-menu group-menu article-menu
- browse-menu server-menu))
+ browse-menu server-menu
+ page-marker))
(name . gnus-visual)
(type . toggle))
((tag . "WWW Browser")
t))))
(errbuf (get-buffer-create " *Gnus rcvstore*"))
;; Find the rcvstore program.
- (rcvstore
- (expand-file-name
- (mh-search-path
- (if mh-lib (cons mh-lib exec-path) exec-path)
- "rcvstore"))))
+ (exec-path (if mh-lib (cons mh-lib exec-path) exec-path)))
(gnus-eval-in-buffer-window
gnus-original-article-buffer
(save-restriction
(widen)
(unwind-protect
(call-process-region
- (point-min) (point-max) rcvstore nil errbuf nil folder)
+ (point-min) (point-max) "rcvstore" nil errbuf nil folder)
(set-buffer errbuf)
(if (zerop (buffer-size))
(message "Article saved in folder: %s" folder)
"Return non-nil if GROUP (and ARTICLE) come from a news server."
(or (gnus-member-of-valid 'post group) ; Ordinary news group.
(and (gnus-member-of-valid 'post-mail group) ; Combined group.
- (eq (gnus-request-type group article) 'post))))
+ (eq (gnus-request-type group article) 'news))))
(defun gnus-inews-news (&optional use-group-method)
"Send a news message.
(interactive "sResend message to: ")
(gnus-summary-select-article)
(save-excursion
- (let (resent)
+ (let (resent beg)
;; We first set up a normal mail buffer.
(nnheader-set-temp-buffer " *Gnus resend*")
;; This code from sendmail.el
(insert mail-header-separator "\n")
;; Insert our usual headers.
(gnus-inews-narrow-to-headers)
- (let ((headers '(From Date To)))
- (gnus-inews-insert-headers headers))
+ (gnus-inews-insert-headers '(From Date To))
(goto-char (point-min))
;; Rename them all to "Resent-*".
(while (re-search-forward "^[A-Za-z]" nil t)
(widen)
(forward-line)
(delete-region (point) (point-max))
+ (setq beg (point))
;; Insert the message to be resent.
(insert-buffer-substring gnus-original-article-buffer)
(goto-char (point-min))
(search-forward "\n\n")
(forward-char -1)
(insert mail-header-separator)
+ ;; Rename all old ("Also-")Resent headers.
+ (while (re-search-backward "^\\(Also-\\)?Resent-" beg t)
+ (beginning-of-line)
+ (insert "Also-"))
;; Send it.
(mail-send)
(kill-buffer (current-buffer)))))
(pop-to-buffer gnus-mail-buffer)
(erase-buffer)
(gnus-mail-setup 'new to subject)
+ (gnus-inews-insert-gcc)
(run-hooks 'gnus-mail-hook)))
(defun gnus-mail-reply (&optional yank to-address followup)
(setq gnus-in-reply-to message-of)
(auto-save-mode auto-save-default)
+ (gnus-inews-insert-gcc)
(if (and follow-to (listp follow-to))
(progn
(insert "\n\n")
(gnus-inews-insert-bfcc)
+ (gnus-inews-insert-gcc)
(gnus-inews-insert-signature)
(and gnus-post-prepare-function
(gnus-functionp gnus-post-prepare-function)
(insert to))))
(gnus-inews-insert-bfcc)
-
+ (gnus-inews-insert-gcc)
+
;; Now the headers should be ok, so we do the yanking.
(goto-char (point-min))
(re-search-forward
(insert gnus-author-copy))))
(defun gnus-inews-insert-gcc ()
- (let* ((group gnus-outgoing-message-group)
- (gcc (cond
- ((gnus-functionp group)
- (funcall group))
- ((or (stringp group) (list group))
- group))))
- (when gcc
- (insert "Gcc: "
- (if (stringp group) group
- (mapconcat 'identity group " "))
- "\n"))))
+ (save-excursion
+ (save-restriction
+ (gnus-inews-narrow-to-headers)
+ (let* ((group gnus-outgoing-message-group)
+ (gcc (cond
+ ((gnus-functionp group)
+ (funcall group))
+ ((or (stringp group) (list group))
+ group))))
+ (when gcc
+ (insert "Gcc: "
+ (if (stringp group) group
+ (mapconcat 'identity group " "))
+ "\n"))))))
;;; Handling rejected (and postponed) news.
(exclude-files (gnus-score-get 'exclude-files alist))
(orphan (car (gnus-score-get 'orphan alist)))
(adapt (gnus-score-get 'adapt alist))
+ (thread-mark-and-expunge
+ (car (gnus-score-get 'thread-mark-and-expunge alist)))
+ (adapt-file (car (gnus-score-get 'adapt-file)))
(local (gnus-score-get 'local alist))
(eval (car (gnus-score-get 'eval alist))))
;; We do not respect eval and files atoms from global score
(setq lists (apply 'append lists
(mapcar (lambda (file)
(gnus-score-load-file file))
- files))))
+ (if adapt-file (cons adapt-file files)
+ files)))))
(and eval (not global) (eval eval))
;; We then expand any exclude-file directives.
(setq gnus-scores-exclude-files
(t
;;(setq gnus-newsgroup-adaptive gnus-use-adaptive-scoring)
gnus-default-adaptive-score-alist)))
+ (setq gnus-thread-expunge-below
+ (or thread-mark-and-expunge gnus-thread-expunge-below))
(setq gnus-summary-mark-below
(or mark mark-and-expunge gnus-summary-mark-below))
(setq gnus-summary-expunge-below
- (or expunge mark-and-expunge gnus-summary-expunge-below)))
+ (or expunge mark-and-expunge gnus-summary-expunge-below))
+ (setq gnus-newsgroup-adaptive-score-file
+ (or adapt-file gnus-newsgroup-adaptive-score-file)))
(setq gnus-current-score-file file)
(setq gnus-score-alist alist)
lists))
(setq elem (cdr elem)))
(setq malist (cdr malist)))
;; We change the score file to the adaptive score file.
- (gnus-score-load-file (gnus-score-file-name
- gnus-newsgroup-name gnus-adaptive-file-suffix))
+ (gnus-score-load-file
+ (or gnus-newsgroup-adaptive-score-file
+ (gnus-score-file-name
+ gnus-newsgroup-name gnus-adaptive-file-suffix)))
;; The we score away.
(while data
(setq elem (cdr (assq (gnus-data-mark (car data)) alist)))
(insert (car sfiles))
(goto-char (point-min))
;; First remove the suffix itself.
- (re-search-forward (concat "." score-regexp))
- (replace-match "" t t)
- (goto-char (point-min))
- (if (looking-at (regexp-quote kill-dir))
- ;; If the file name was just "SCORE", `klen' is one character
- ;; too much.
- (delete-char (min (1- (point-max)) klen))
- (goto-char (point-max))
- (search-backward "/")
- (delete-region (1+ (point)) (point-min)))
- ;; If short file names were used, we have to translate slashes.
- (goto-char (point-min))
- (while (re-search-forward "[/:]" nil t)
- (replace-match "." t t))
- ;; Cludge to get rid of "nntp+" problems.
- (goto-char (point-min))
- (and (looking-at "nn[a-z]+\\+")
- (progn
- (search-forward "+")
- (forward-char -1)
- (insert "\\")))
- ;; Translate "all" to ".*".
- (while (search-forward "all" nil t)
- (replace-match ".*" t t))
- (goto-char (point-min))
- ;; Deal with "not."s.
- (if (looking-at "not.")
- (progn
- (setq not-match t)
- (setq regexp (buffer-substring 5 (point-max))))
- (setq regexp (buffer-substring 1 (point-max)))
- (setq not-match nil))
- ;; Finally - if this resulting regexp matches the group name,
- ;; we add this score file to the list of score files
- ;; applicable to this group.
- (if (or (and not-match
- (not (string-match regexp group)))
- (and (not not-match)
- (string-match regexp group)))
- (setq ofiles (cons (car sfiles) ofiles)))
+ (when (re-search-forward (concat "." score-regexp) nil t)
+ (replace-match "" t t)
+ (goto-char (point-min))
+ (if (looking-at (regexp-quote kill-dir))
+ ;; If the file name was just "SCORE", `klen' is one character
+ ;; too much.
+ (delete-char (min (1- (point-max)) klen))
+ (goto-char (point-max))
+ (search-backward "/")
+ (delete-region (1+ (point)) (point-min)))
+ ;; If short file names were used, we have to translate slashes.
+ (goto-char (point-min))
+ (while (re-search-forward "[/:]" nil t)
+ (replace-match "." t t))
+ ;; Cludge to get rid of "nntp+" problems.
+ (goto-char (point-min))
+ (and (looking-at "nn[a-z]+\\+")
+ (progn
+ (search-forward "+")
+ (forward-char -1)
+ (insert "\\")))
+ ;; Translate "all" to ".*".
+ (while (search-forward "all" nil t)
+ (replace-match ".*" t t))
+ (goto-char (point-min))
+ ;; Deal with "not."s.
+ (if (looking-at "not.")
+ (progn
+ (setq not-match t)
+ (setq regexp (buffer-substring 5 (point-max))))
+ (setq regexp (buffer-substring 1 (point-max)))
+ (setq not-match nil))
+ ;; Finally - if this resulting regexp matches the group name,
+ ;; we add this score file to the list of score files
+ ;; applicable to this group.
+ (if (or (and not-match
+ (not (string-match regexp group)))
+ (and (not not-match)
+ (string-match regexp group)))
+ (setq ofiles (cons (car sfiles) ofiles))))
(setq sfiles (cdr sfiles)))
(kill-buffer (current-buffer))
;; Slight kludge here - the last score file returned should be
(add-text-properties
(point)
(prog1 (1+ (point))
- (eval gnus-topic-line-format-spec))
+ (eval gnus-topic-line-format-spec)
+ (gnus-group-remove-excess-properties))
(list 'gnus-topic name
'gnus-topic-level level
- 'gnus-topic-visible visiblep))
- (gnus-group-remove-excess-properties)))
+ 'gnus-topic-visible visiblep))))
(defun gnus-topic-previous-topic (topic)
"Return the previous topic on the same level as TOPIC."
(defun gnus-topic-kill-group (&optional n discard)
"Kill the next N groups."
(interactive "P")
- (if (not (gnus-group-topic-p))
- (gnus-group-kill-group n discard)
- (let ((topic (gnus-group-topic-name)))
- (gnus-topic-remove-topic nil t)
- (push (gnus-topic-find-topology topic nil nil gnus-topic-topology)
- gnus-topic-killed-topics))))
+ (if (gnus-group-topic-p)
+ (let ((topic (gnus-group-topic-name)))
+ (gnus-topic-remove-topic nil t)
+ (push (gnus-topic-find-topology topic nil nil gnus-topic-topology)
+ gnus-topic-killed-topics))
+ ;; We first kill the groups the normal way...
+ (let ((killed (gnus-group-kill-group n discard))
+ group alist)
+ ;; Then we remove the killed groups from the topics they belong to.
+ (when (stringp killed)
+ (setq killed (list killed)))
+ (while killed
+ (when (setq alist (assoc (gnus-group-topic (setq group (pop killed)))
+ gnus-topic-alist))
+ (setcdr alist (delete group (cdr alist))))))))
(defun gnus-topic-yank-group (&optional arg)
"Yank the last topic."
(interactive "p")
- (if (null gnus-topic-killed-topics)
- (gnus-group-yank-group arg)
- (let ((previous (gnus-group-parent-topic))
- (item (nth 1 (pop gnus-topic-killed-topics))))
- (gnus-topic-create-topic
- (car item) (gnus-topic-parent-topic previous) previous))))
+ (if gnus-topic-killed-topics
+ (let ((previous (gnus-group-parent-topic))
+ (item (nth 1 (pop gnus-topic-killed-topics))))
+ (gnus-topic-create-topic
+ (car item) (gnus-topic-parent-topic previous) previous))
+ ;; We first yank the groups the normal way...
+ (let* ((topic (gnus-group-parent-topic))
+ (prev (gnus-group-group-name))
+ (alist (assoc topic gnus-topic-alist))
+ (yanked (gnus-group-yank-group arg))
+ group)
+ ;; Then we enter the yanked groups in the topics they belong to.
+ (when (stringp yanked)
+ (setq yanked (list yanked)))
+ (if (not prev)
+ (nconc alist yanked)
+ (setq alist (cdr alist))
+ (while (cdr alist)
+ (when (equal (car (cdr alist)) prev)
+ (setcdr alist (nconc yanked (cdr alist)))
+ (setq alist nil))
+ (setq alist (cdr alist)))))))
(defun gnus-topic-hide-topic ()
"Hide all subtopics under the current topic."
(setq files (gnus-uu-unpack-files files)))
(gnus-uu-add-file (mapcar (lambda (file) (cdr (assq 'name file))) files))
(setq files (nreverse (gnus-uu-get-actions files)))
- (or not-insert (gnus-summary-insert-pseudos files save))))
+ (or not-insert (not gnus-insert-pseudo-articles)
+ (gnus-summary-insert-pseudos files save))))
;; Return a list of files in dir.
(defun gnus-uu-scan-directory (dir)
(end (progn (end-of-line) (point)))
;; now find out where the line starts and leave point there.
(beg (progn (beginning-of-line) (point)))
- (score (or (cdr (assq (or (gnus-summary-article-number)
- gnus-current-article)
+ (article (gnus-summary-article-number))
+ (score (or (cdr (assq (or article gnus-current-article)
gnus-newsgroup-scored))
gnus-summary-default-score 0))
(default gnus-summary-default-score)
(fun (get-text-property (point) 'gnus-callback)))
(if fun (funcall fun data))))
-;; Suggested by Arne Elofsson <arne@hodgkin.mbi.ucla.edu>
-(defun gnus-article-next-button ()
- "Move point to next button."
- (interactive)
- (if (get-text-property (point) 'gnus-callback)
- (goto-char (next-single-property-change (point) 'gnus-callback
- nil (point-max))))
- (let ((pos (next-single-property-change (point) 'gnus-callback)))
- (if pos
- (goto-char pos)
- (setq pos (next-single-property-change (point-min) 'gnus-callback))
- (if pos
- (goto-char pos)
- (error "No buttons found")))))
+(defun gnus-article-prev-button (n)
+ "Move point to N buttons backward.
+If N is negative, move forward instead."
+ (interactive "p")
+ (gnus-article-next-button (- n)))
+
+(defun gnus-article-next-button (n)
+ "Move point to N buttons forward.
+If N is negative, move backward instead."
+ (interactive "p")
+ (let ((function (if (< n 0) 'prev-single-property-change
+ 'next-single-property-change))
+ (limit (if (< n 0) (point-min) (point-max))))
+ (setq n (abs n))
+ (while (and (not (= limit (point)))
+ (> n 0))
+ ;; Skip past the current button.
+ (when (get-text-property (point) 'gnus-callback)
+ (goto-char (funcall function (point) 'gnus-callback nil limit)))
+ ;; Go to the next (or previous) button.
+ (funcall function (point) 'gnus-callback nil limit)
+ (decf n))
+ (unless (zerop n)
+ (gnus-message 5 "No more buttons"))
+ n))
(defun gnus-article-highlight (&optional force)
"Highlight current article.
"Browse ADDRESS."
(funcall browse-url-browser-function address))
+;;; Next/prev buttons in the article buffer.
+
+(defvar gnus-next-page-line-format "%{%(Next page...%)%}\n")
+(defvar gnus-prev-page-line-format "%{%(Previous page...%)%}\n")
+
+(defvar gnus-prev-page-map nil)
+(unless gnus-prev-page-map
+ (setq gnus-prev-page-map (make-sparse-keymap))
+ (define-key gnus-prev-page-map "\n" 'gnus-article-prev-page))
+
+(defun gnus-insert-prev-page-button ()
+ (let ((buffer-read-only nil))
+ (gnus-remove-text-with-property 'gnus-prev)
+ (gnus-eval-format gnus-prev-page-line-format nil
+ `(gnus-prev t local-map ,gnus-prev-page-map))))
+
+(defvar gnus-next-page-map nil)
+(unless gnus-next-page-map
+ (setq gnus-next-page-map (make-sparse-keymap))
+ (define-key gnus-next-page-map "\n" 'gnus-article-next-page))
+
+(defun gnus-insert-next-page-button ()
+ (let ((buffer-read-only nil))
+ (gnus-remove-text-with-property 'gnus-next)
+ (gnus-eval-format gnus-next-page-line-format nil
+ `(gnus-next t local-map ,gnus-next-page-map))))
+
;;; Compatibility Functions:
(or (fboundp 'rassoc)
(easy-menu-add gnus-article-article-menu)
(easy-menu-add gnus-article-treatment-menu))
+(defun gnus-xmas-read-event-char ()
+ "Get the next event."
+ (let ((event (next-event)))
+ (while (timeout-event-p event)
+ (setq event (next-event)))
+ (cons (and (key-press-event-p event)
+ (numberp (event-key event))
+ (event-to-character event))
+ event)))
(defun gnus-xmas-define ()
(setq gnus-mouse-2 [button2])
(fset 'set-text-properties 'gnus-xmas-set-text-properties)
(or (boundp 'standard-display-table) (setq standard-display-table nil))
- (or (boundp 'read-event) (fset 'read-event 'next-command-event))
(defvar gnus-mouse-face-prop 'highlight)
(defun gnus-xmas-redefine ()
-
-
+ "Redefine lots of Gnus functions for XEmacs."
(fset 'gnus-summary-make-display-table (lambda () nil))
(fset 'gnus-visual-turn-off-edit-menu 'identity)
(fset 'gnus-highlight-selected-summary
(fset 'gnus-article-add-button 'gnus-xmas-article-add-button)
(fset 'gnus-window-top-edge 'gnus-xmas-window-top-edge)
(fset 'set-text-properties 'gnus-xmas-set-text-properties)
+ (fset 'gnus-read-event-char 'gnus-xmas-read-event-char)
+ (fset 'gnus-group-startup-message 'gnus-xmas-group-startup-message)
(or (fboundp 'appt-select-lowest-window)
(fset 'appt-select-lowest-window
(setq path (cdr path))))
gnus-xmas-glyph-directory)))
-(defun gnus-xmas-group-startup (&optional x y)
+(defun gnus-xmas-group-startup-message (&optional x y)
"Insert startup message in current buffer."
;; Insert the message.
+ (gnus-xmas-find-glyph-directory)
(erase-buffer)
- (if (featurep 'xpm)
- (progn
- (set-glyph-property gnus-xmas-logo 'image "~/tmp/gnus.xpm")
- (set-glyph-image gnus-xmas-logo "~/tmp/gnus.xpm" 'global 'x)
-
- (insert " ")
- (set-extent-begin-glyph (make-extent (point) (point)) gnus-xmas-logo)
- (insert "
- Gnus * A newsreader for Emacsen
- A Praxis Release * larsi@ifi.uio.no")
- (goto-char (point-min))
- (while (not (eobp))
- (insert (make-string (/ (max (- (window-width) (or x 35)) 0) 2)
- ? ))
- (forward-line 1))
- (goto-char (point-min))
- ;; +4 is fuzzy factor.
- (insert-char ?\n (/ (max (- (window-height) (or y 24)) 0) 2)))
-
- (insert
- (format "
- %s
- A newsreader
- for GNU Emacs
-
- Based on GNUS
- written by
- Masanobu UMEDA
-
- A Praxis Release
- larsi@ifi.uio.no
+ (let ((file (and gnus-xmas-glyph-directory
+ (concat
+ (file-name-as-directory gnus-xmas-glyph-directory)
+ "gnus.xpm"))))
+ (if (and (featurep 'xpm)
+ file (file-exists-p file))
+ (progn
+ (set-glyph-property gnus-xmas-logo 'image file)
+ (set-glyph-image gnus-xmas-logo file 'global 'x)
+
+ (insert " ")
+ (set-extent-begin-glyph (make-extent (point) (point)) gnus-xmas-logo)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (insert (make-string (/ (max (- (window-width) (or x 35)) 0) 2)
+ ? ))
+ (forward-line 1))
+ (goto-char (point-min))
+ (let* ((pheight (+ 20 (count-lines (point-min) (point-max))))
+ (wheight (window-height))
+ (rest (- wheight pheight)))
+ (insert (make-string (max 0 (* 2 (/ rest 3))) ?\n))))
+
+ (insert
+ (format " %s
+ _ ___ _ _
+ _ ___ __ ___ __ _ ___
+ __ _ ___ __ ___
+ _ ___ _
+ _ _ __ _
+ ___ __ _
+ __ _
+ _ _ _
+ _ _ _
+ _ _ _
+ __ ___
+ _ _ _ _
+ _ _
+ _ _
+ _ _
+ _
+ __
+
"
- gnus-version))
- ;; And then hack it.
- ;; 18 is the longest line.
- (indent-rigidly (point-min) (point-max)
- (/ (max (- (window-width) (or x 28)) 0) 2))
+ ""))
+ ;; And then hack it.
+ (gnus-indent-rigidly (point-min) (point-max)
+ (/ (max (- (window-width) (or x 46)) 0) 2))
+ (goto-char (point-min))
+ (forward-line 1)
+ (let* ((pheight (count-lines (point-min) (point-max)))
+ (wheight (window-height))
+ (rest (- wheight pheight)))
+ (insert (make-string (max 0 (* 2 (/ rest 3))) ?\n))))
+ ;; Fontify some.
+ (goto-char (point-min))
+ (and (search-forward "Praxis" nil t)
+ (put-text-property (match-beginning 0) (match-end 0) 'face 'bold))
(goto-char (point-min))
- ;; +4 is fuzzy factor.
- (insert-char ?\n (/ (max (- (window-height) (or y 12)) 0) 2)))
-
- ;; Fontify some.
- (goto-char (point-min))
- (search-forward "Praxis")
- (put-text-property (match-beginning 0) (match-end 0) 'face 'bold)
- (goto-char (point-min)))
+ (let* ((mode-string (gnus-group-set-mode-line)))
+ (setq mode-line-buffer-identification
+ (list (concat gnus-version (substring (car mode-string) 4))))
+ (set-buffer-modified-p t))))
+
;;; The toolbar.
(defvar gnus-asynchronous nil
"*If non-nil, Gnus will supply backends with data needed for async article fetching.")
+(defvar gnus-kill-summary-on-exit t
+ "*If non-nil, kill the summary buffer when you exit from it.
+If nil, the summary will become a \"*Dead Summary*\" buffer, and
+it will be killed sometime later.")
+
(defvar gnus-large-newsgroup 200
"*The number of articles which indicates a large newsgroup.
If the number of articles in a newsgroup is greater than this value,
The function is called with NEWSGROUP, HEADERS, and optional
LAST-FILE.")
-(defvar gnus-split-methods nil
+(defvar gnus-split-methods
+ '((gnus-article-archive-name))
"*Variable used to suggest where articles are to be saved.
-The syntax of this variable is the same as `nnmail-split-methods'.
-
For instance, if you would like to save articles related to Gnus in
the file \"gnus-stuff\", and articles related to VM in \"vm-stuff\",
you could set this variable to something like:
'((\"^Subject:.*gnus\\|^Newsgroups:.*gnus\" \"gnus-stuff\")
- (\"^Subject:.*vm\\|^Xref:.*vm\" \"vm-stuff\"))")
+ (\"^Subject:.*vm\\|^Xref:.*vm\" \"vm-stuff\"))
+
+This variable is an alist where the where the key is the match and the
+value is a list of possible files to save in if the match is non-nil.
+
+If the match is a string, it is used as a regexp match on the
+article. If the match is a symbol, that symbol will be funcalled
+from the buffer of the article to be saved with the newsgroup as the
+parameter. If it is a list, it will be evaled in the same buffer.
+
+If this form or function returns a string, this string will be used as
+a possible file name; and if it returns a non-nil list, that list will
+be used as possible file names.")
(defvar gnus-save-score nil
"*If non-nil, save group scoring info.")
"*If non-nil, use some adaptive scoring scheme.")
(defvar gnus-use-cache nil
- "*If non-nil, Gnus will cache (some) articles locally.")
+ "*If nil, Gnus will ignore the article cache.
+If `passive', it will allow entering (and reading) articles
+explicitly entered into the cache. If anything else, use the
+cache to the full extent of the law.")
(defvar gnus-keep-backlog nil
"*If non-nil, Gnus will keep read articles for later re-retrieval.
(defvar gnus-level-default-unsubscribed 6
"*New unsubscribed groups will be unsubscribed at this level.")
+(defvar gnus-activate-level (1+ gnus-level-subscribed)
+ "*Groups higher than this level won't be activated on startup.
+Setting this variable to something log might save lots of time when
+you have many groups that you aren't interested in.")
+
(defvar gnus-activate-foreign-newsgroups 4
"*If nil, Gnus will not check foreign newsgroups at startup.
If it is non-nil, it should be a number between one and nine. Foreign
(defvar gnus-ignored-headers
"^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:"
"*All headers that match this regexp will be hidden.
+This variable can also be a list of regexps of headers to be ignored.
If `gnus-visible-headers' is non-nil, this variable will be ignored.")
(defvar gnus-visible-headers "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Resent-"
"*All headers that do not match this regexp will be hidden.
+This variable can also be a list of regexp of headers to remain visible.
If this variable is non-nil, `gnus-ignored-headers' will be ignored.")
(defvar gnus-sorted-header-list
`gnus-subscribe-alphabetically' inserts new groups in strict
alphabetic order; `gnus-subscribe-hierarchically' inserts new groups
in hierarchical newsgroup order; `gnus-subscribe-interactively' asks
-for your decision.")
+for your decision; `gnus-subscribe-killed' kills all new groups.")
;; Suggested by a bug report by Hallvard B Furuseth.
;; <h.b.furuseth@usit.uio.no>.
"*Mark used for articles that are caught up.")
(defvar gnus-replied-mark ?A
"*Mark used for articles that have been replied to.")
+(defvar gnus-cached-mark ?*
+ "*Mark used for articles that are in the cache.")
+(defvar gnus-saved-mark ?S
+ "*Mark used for articles that have been saved to.")
(defvar gnus-process-mark ?#
"*Process mark.")
(defvar gnus-ancient-mark ?O
If nil, all files that use the same viewing command will be given as a
list of parameters to that command.")
+(defvar gnus-insert-pseudo-articles t
+ "*If non-nil, insert pseudo-articles when decoding articles.")
+
(defvar gnus-group-line-format "%M%S%p%P%5y: %(%g%)\n"
"*Format of group lines.
It works along the same lines as a normal formatting string,
%S The subject")
-(defvar gnus-summary-mode-line-format "Gnus %G/%A %Z"
- "*The format specification for the summary mode line.")
-
-(defvar gnus-article-mode-line-format "Gnus %G/%A %S"
- "*The format specification for the article mode line.")
+(defvar gnus-summary-mode-line-format "Gnus: %%b [%A] %Z"
+ "*The format specification for the summary mode line.
+It works along the same lines as a normal formatting string,
+with some simple extensions:
+
+%G Group name
+%p Unprefixed group name
+%A Current article number
+%V Gnus version
+%U Number of unread articles in the group
+%e Number of unselected articles in the group
+%Z A string with unread/unselected article counts
+%g Shortish group name
+%S Subject of the current article
+%u User-defined spec
+%s Current score file name
+%d Number of dormant articles
+%r Number of articles that have been marked as read in this session
+%E Number of articles expunged by the score files")
+
+(defvar gnus-article-mode-line-format "Gnus: %%b %S"
+ "*The format specification for the article mode line.
+See `gnus-summary-mode-line-format' for a closer description.")
+
+(defvar gnus-group-mode-line-format "Gnus: %%b {%M:%S}"
+ "*The format specification for the group mode line.
+It works along the same lines as a normal formatting string,
+with some simple extensions:
-(defvar gnus-group-mode-line-format "Gnus List of groups {%M:%S} "
- "*The format specification for the group mode line.")
+%S The native news server.
+%M The native select method.")
(defvar gnus-valid-select-methods
'(("nntp" post address prompt-address)
If this variable is nil, screen refresh may be quicker.")
;; Added by Keinonen Kari <kk85613@cs.tut.fi>.
-(defvar gnus-mode-non-string-length 25
+(defvar gnus-mode-non-string-length nil
"*Max length of mode-line non-string contents.
If this is nil, Gnus will take space as is needed, leaving the rest
of the modeline intact.")
This variable is local to each summary buffer and usually set by the
score file.")
+(defvar gnus-article-sort-functions '(gnus-article-sort-by-number)
+ "*List of functions used for sorting articles in the summary buffer.")
+
(defvar gnus-thread-sort-functions '(gnus-thread-sort-by-number)
"*List of functions used for sorting threads in the summary buffer.
By default, threads are sorted by article number.
Some functions you can use are `+', `max', or `min'.")
+(defvar gnus-summary-expunge-below nil
+ "All articles that have a score less than this variable will be expunged.")
+
+(defvar gnus-thread-expunge-below nil
+ "All threads that have a total score less than this variable will be expunged.
+See `gnus-thread-score-function' for en explanation of what a
+\"thread score\" is.")
+
(defvar gnus-auto-subscribed-groups
"^nnml\\|^nnfolder\\|^nnmbox\\|^nnmh\\|^nnbabyl"
"*All new groups that match this regexp will be subscribed automatically.
(defvar gnus-inhibit-hiding nil)
(defvar gnus-topic-indentation "")
+(defvar gnus-inhibit-limiting nil)
(defvar gnus-article-mode-map nil)
(defvar gnus-dribble-buffer nil)
(defvar gnus-nocem-hashtb nil)
(defvar gnus-current-score-file nil)
+(defvar gnus-newsgroup-adaptive-score-file nil)
(defvar gnus-scores-exclude-files nil)
(defvar gnus-opened-servers nil)
(defvar gnus-summary-display-table nil)
(defconst gnus-group-line-format-alist
- `((?M gnus-tmp-marked ?c)
+ `((?M gnus-tmp-marked-mark ?c)
(?S gnus-tmp-subscribed ?c)
(?L gnus-tmp-level ?d)
(?N gnus-tmp-number ?s)
(?E gnus-newsgroup-expunged-tally ?d)
(?s (gnus-current-score-file-nondirectory) ?s))))
+(defconst gnus-article-mode-line-format-alist
+ gnus-summary-mode-line-format-alist)
+
(defconst gnus-group-mode-line-format-alist
(` ((?S gnus-tmp-news-server ?s)
(?M gnus-tmp-news-method ?s)
"gnus-bug@ifi.uio.no (The Gnus Bugfixing Girls + Boys)"
"The mail address of the Gnus maintainers.")
-(defconst gnus-version "September Gnus v0.21"
+(defconst gnus-version "September Gnus v0.22"
"Version number for this version of Gnus.")
(defvar gnus-info-nodes
(defvar gnus-newsgroup-killed nil
"List of ranges of articles that have been through the scoring process.")
+(defvar gnus-newsgroup-cached nil
+ "List of articles that come from the article cache.")
+
+(defvar gnus-newsgroup-saved nil
+ "List of articles that have been saved.")
+
(defvar gnus-newsgroup-kill-headers nil)
(defvar gnus-newsgroup-replied nil
(defvar gnus-summary-mark-positions nil)
(defvar gnus-group-mark-positions nil)
-(defvar gnus-summary-expunge-below nil)
(defvar gnus-reffed-article-number nil)
;;; Let the byte-compiler know that we know about this variable.
(defvar gnus-cache-removeable-articles nil)
+(defvar gnus-dead-summary nil)
+
(defconst gnus-summary-local-variables
'(gnus-newsgroup-name
gnus-newsgroup-begin gnus-newsgroup-end
gnus-newsgroup-last-folder gnus-newsgroup-last-file
gnus-newsgroup-auto-expire gnus-newsgroup-unreads
gnus-newsgroup-unselected gnus-newsgroup-marked
- gnus-newsgroup-reads
+ gnus-newsgroup-reads gnus-newsgroup-saved
gnus-newsgroup-replied gnus-newsgroup-expirable
gnus-newsgroup-processable gnus-newsgroup-killed
gnus-newsgroup-bookmarks gnus-newsgroup-dormant
gnus-summary-mark-below gnus-newsgroup-active gnus-scores-exclude-files
gnus-newsgroup-history gnus-newsgroup-ancient
(gnus-newsgroup-adaptive . gnus-use-adaptive-scoring)
+ gnus-newsgroup-adaptive-score-file
(gnus-newsgroup-expunged-tally . 0)
- gnus-cache-removeable-articles
+ gnus-cache-removeable-articles gnus-newsgroup-cached
gnus-newsgroup-data gnus-newsgroup-data-reverse
gnus-newsgroup-limit gnus-newsgroup-limits)
"Variables that are buffer-local to the summary buffers.")
gnus-server-make-menu-bar gnus-article-make-menu-bar
gnus-browse-make-menu-bar gnus-highlight-selected-summary
gnus-summary-highlight-line gnus-carpal-setup-buffer
- gnus-article-add-button)
+ gnus-article-add-button gnus-insert-next-page-button
+ gnus-insert-prev-page-button)
("gnus-vis" :interactive t
gnus-article-push-button gnus-article-press-button
gnus-article-highlight gnus-article-highlight-some
gnus-article-hide gnus-article-hide-signature
gnus-article-highlight-headers gnus-article-highlight-signature
gnus-article-add-buttons gnus-article-add-buttons-to-head
- gnus-article-next-button)
+ gnus-article-next-button gnus-article-prev-button)
("gnus-demon" gnus-demon-add-nocem gnus-demon-add-scanmail
gnus-demon-add-disconnection gnus-demon-add-handler
gnus-demon-remove-handler)
("gnus-cache" gnus-cache-possibly-enter-article gnus-cache-save-buffers
gnus-cache-possibly-remove-articles gnus-cache-request-article
gnus-cache-retrieve-headers gnus-cache-possibly-alter-active
- gnus-cache-enter-remove-article
+ gnus-cache-enter-remove-article gnus-cached-article-p
gnus-cache-open gnus-cache-close)
- ("gnus-cache" :interactive t gnus-jog-cache)
+ ("gnus-cache" :interactive t gnus-jog-cache gnus-cache-enter-article
+ gnus-cache-remove-article)
("gnus-score" :interactive t
gnus-summary-increase-score gnus-summary-lower-score
gnus-score-flush-cache gnus-score-close
("gnus-msg" (gnus-summary-send-map keymap)
gnus-mail-yank-original gnus-mail-send-and-exit
gnus-sendmail-setup-mail gnus-article-mail
- gnus-inews-message-id gnus-news-mail gnus-mail-reply)
+ gnus-inews-message-id gnus-new-mail gnus-mail-reply)
("gnus-msg" :interactive t
gnus-group-post-news gnus-group-mail gnus-summary-post-news
gnus-summary-followup gnus-summary-followup-with-original
(defvar gnus-tmp-subject-or-nil)
(defvar gnus-tmp-subject)
(defvar gnus-tmp-marked)
+(defvar gnus-tmp-marked-mark)
(defvar gnus-tmp-subscribed)
(defvar gnus-tmp-process-marked)
(defvar gnus-tmp-number-of-unread)
(gnus-byte-code 'gnus-summary-dummy-line-format-spec))
(defun gnus-group-line-format-spec ()
- (insert gnus-tmp-marked gnus-tmp-subscribed
+ (insert gnus-tmp-marked-mark gnus-tmp-subscribed
gnus-tmp-process-marked
gnus-topic-indentation
(format "%5s: " gnus-tmp-number-of-unread))
(defvar gnus-group-line-format-spec
(gnus-byte-code 'gnus-group-line-format-spec))
-(defun gnus-summary-mode-line-format-spec ()
- (format "Gnus %s/%d %s" gnus-tmp-group-name
- gnus-tmp-article-number gnus-tmp-unread-and-unselected))
-(defvar gnus-summary-mode-line-format-spec
- (gnus-byte-code 'gnus-summary-mode-line-format-spec))
-
-(defun gnus-group-mode-line-format-spec ()
- (format "Gnus List of groups {%s:%s} "
- gnus-tmp-news-method gnus-tmp-news-server))
-(defvar gnus-group-mode-line-format-spec
- (gnus-byte-code 'gnus-group-mode-line-format-spec))
-
-(defun gnus-article-mode-line-format-spec ()
- (format "Gnus %s/%d %s" gnus-tmp-group-name
- gnus-tmp-article-number gnus-tmp-subject))
-(defvar gnus-article-mode-line-format-spec
- (gnus-byte-code 'gnus-article-mode-line-format-spec))
-
(defvar gnus-old-specs
- '((article-mode . "Gnus %G/%A %S")
- (group-mode . "Gnus List of groups {%M:%S} ")
- (summary-mode . "Gnus %G/%A %Z")
- (group . "%M%S%p%5y: %(%g%)\n")
+ '((group . "%M%S%p%5y: %(%g%)\n")
(summary-dummy . "* : : %S\n")
(summary . "%U%R%z%I%(%[%4L: %-20,20n%]%) %s\n")))
+(defvar gnus-article-mode-line-format-spec nil)
+(defvar gnus-summary-mode-line-format-spec nil)
+(defvar gnus-group-mode-line-format-spec nil)
+
;;; Phew. All that gruft is over, fortunately.
\f
(goto-char (point-min))
(while (re-search-forward "%[-0-9]*\\(,[0-9]+\\)?\\([^0-9]\\)\\(.\\)?"
nil t)
- (setq spec (string-to-char (match-string 2)))
- ;; First check if there are any specs that look anything like
- ;; "%12,12A", ie. with a "max width specification". These have
- ;; to be treated specially.
- (if (setq beg (match-beginning 1))
- (setq max-width
- (string-to-int
- (buffer-substring (1+ (match-beginning 1)) (match-end 1))))
- (setq max-width 0)
- (setq beg (match-beginning 2)))
- ;; Find the specification from `spec-alist'.
- (unless (setq elem (cdr (assq spec spec-alist)))
- (setq elem '("*" ?s)))
- ;; Treat user defined format specifiers specially.
- (when (eq (car elem) 'gnus-tmp-user-defined)
- (setq elem
- (list
- (list (intern (concat "gnus-user-format-function-"
- (match-string 3)))
- 'gnus-tmp-header) ?s))
- (delete-region (match-beginning 3) (match-end 3)))
- (if (not (zerop max-width))
- (let ((el (car elem)))
- (cond ((= (car (cdr elem)) ?c)
- (setq el (list 'char-to-string el)))
- ((= (car (cdr elem)) ?d)
- (numberp el) (setq el (list 'int-to-string el))))
- (setq flist (cons (gnus-max-width-function el max-width)
- flist))
- (setq newspec ?s))
- (setq flist (cons (car elem) flist))
+ (if (= (setq spec (string-to-char (match-string 2))) ?%)
+ (setq newspec "%"
+ beg (1+ (match-beginning 0)))
+ ;; First check if there are any specs that look anything like
+ ;; "%12,12A", ie. with a "max width specification". These have
+ ;; to be treated specially.
+ (if (setq beg (match-beginning 1))
+ (setq max-width
+ (string-to-int
+ (buffer-substring
+ (1+ (match-beginning 1)) (match-end 1))))
+ (setq max-width 0)
+ (setq beg (match-beginning 2)))
+ ;; Find the specification from `spec-alist'.
+ (unless (setq elem (cdr (assq spec spec-alist)))
+ (setq elem '("*" ?s)))
+ ;; Treat user defined format specifiers specially.
+ (when (eq (car elem) 'gnus-tmp-user-defined)
+ (setq elem
+ (list
+ (list (intern (concat "gnus-user-format-function-"
+ (match-string 3)))
+ 'gnus-tmp-header) ?s))
+ (delete-region (match-beginning 3) (match-end 3)))
+ (if (not (zerop max-width))
+ (let ((el (car elem)))
+ (cond ((= (car (cdr elem)) ?c)
+ (setq el (list 'char-to-string el)))
+ ((= (car (cdr elem)) ?d)
+ (numberp el) (setq el (list 'int-to-string el))))
+ (setq flist (cons (gnus-max-width-function el max-width)
+ flist))
+ (setq newspec ?s))
+ (setq flist (cons (car elem) flist)))
(setq newspec (car (cdr elem))))
;; Remove the old specification (and possibly a ",12" string).
(delete-region beg (match-end 2))
(cons 'insert result)))
(or (car result) ""))))
+(defun gnus-eval-format (format &optional alist props)
+ "Eval the format variable FORMAT, using ALIST.
+If INSERT, insert the result."
+ (let ((form (gnus-parse-format format alist props)))
+ (if props
+ (add-text-properties (point) (progn (eval form) (point)) props)
+ (eval form))))
+
+(defun gnus-remove-text-with-property (prop)
+ "Delete all text in the current buffer with text property PROP."
+ (save-excursion
+ (goto-char (point-min))
+ (while (not (eobp))
+ (when (get-text-property (point) prop)
+ (delete-char 1))
+ (goto-char (next-single-property-change (point) prop nil (point-max))))))
+
(defun gnus-set-work-buffer ()
(if (get-buffer gnus-work-buffer)
(progn
(substring groupkey (match-beginning 1) (match-end 1)))))
(gnus-subscribe-newsgroup newgroup before))))
-(defun gnus-subscribe-interactively (newsgroup)
- "Subscribe new NEWSGROUP interactively.
+(defun gnus-subscribe-interactively (group)
+ "Subscribe the new GROUP interactively.
It is inserted in hierarchical newsgroup order if subscribed. If not,
it is killed."
- (if (gnus-y-or-n-p (format "Subscribe new newsgroup: %s " newsgroup))
- (gnus-subscribe-hierarchically newsgroup)
- (setq gnus-killed-list (cons newsgroup gnus-killed-list))))
+ (if (gnus-y-or-n-p (format "Subscribe new newsgroup: %s " group))
+ (gnus-subscribe-hierarchically group)
+ (push group gnus-killed-list)))
+
+(defun gnus-subscribe-zombies (group)
+ "Make the new GROUP into a zombie group."
+ (push group gnus-zombie-list))
-(defun gnus-subscribe-zombies (newsgroup)
- "Make new NEWSGROUP a zombie group."
- (setq gnus-zombie-list (cons newsgroup gnus-zombie-list)))
+(defun gnus-subscribe-killed (group)
+ "Make the new GROUP a killed group."
+ (push group gnus-killed-list))
(defun gnus-subscribe-newsgroup (newsgroup &optional next)
"Subscribe new NEWSGROUP.
(suppress-keymap gnus-group-mode-map)
(define-key gnus-group-mode-map " " 'gnus-group-read-group)
(define-key gnus-group-mode-map "=" 'gnus-group-select-group)
+ (define-key gnus-group-mode-map "\M- " 'gnus-group-unhidden-select-group)
(define-key gnus-group-mode-map "\r" 'gnus-group-select-group)
(define-key gnus-group-mode-map "\M-\r" 'gnus-group-quick-select-group)
(define-key gnus-group-mode-map "j" 'gnus-group-jump-to-group)
(define-key gnus-group-mode-map "\C-c\C-b" 'gnus-bug)
(define-key gnus-group-mode-map "\C-c\C-s" 'gnus-group-sort-groups)
(define-key gnus-group-mode-map "t" 'gnus-topic-mode)
+ (define-key gnus-group-mode-map "\C-c\M-g" 'gnus-activate-all-groups)
(define-key gnus-group-mode-map "#" 'gnus-group-mark-group)
(define-key gnus-group-mode-map "\M-#" 'gnus-group-unmark-group)
(define-key gnus-group-sub-map "k" 'gnus-group-kill-group)
(define-key gnus-group-sub-map "y" 'gnus-group-yank-group)
(define-key gnus-group-sub-map "w" 'gnus-group-kill-region)
+ (define-key gnus-group-sub-map "\C-k" 'gnus-group-kill-level)
(define-key gnus-group-sub-map "z" 'gnus-group-kill-all-zombies))
(defun gnus-group-mode ()
(goto-char (point-min))
(let* ((mode-string (gnus-group-set-mode-line)))
(setq mode-line-buffer-identification
- (concat gnus-version (substring mode-string 4)))
+ (list (concat gnus-version (substring (car mode-string) 4))))
(set-buffer-modified-p t)))
(defun gnus-group-startup-message-old (&optional x y)
(if gnus-tmp-method
(format "(%s:%s)" (car gnus-tmp-method)
(car (cdr gnus-tmp-method))) ""))
- (gnus-tmp-marked
+ (gnus-tmp-marked-mark
(if (and (numberp number)
(zerop number)
(cdr (assq 'tick gnus-tmp-marked)))
gnus-unread ,(if (numberp number)
(string-to-int gnus-tmp-number-of-unread)
t)
- gnus-marked ,gnus-tmp-marked
+ gnus-marked ,gnus-tmp-marked-mark
gnus-level ,gnus-tmp-level))
;; Allow XEmacs to remove front-sticky text properties.
(gnus-group-remove-excess-properties)))
(when (> (length mode-string) max-len)
(setq mode-string (substring mode-string 0 (- max-len 4))))
(prog1
- (setq mode-line-buffer-identification mode-string)
+ (setq mode-line-buffer-identification (list mode-string))
(set-buffer-modified-p t)))))
(defun gnus-group-group-name ()
gnus-summary-expunge-below)
(gnus-group-read-group all t)))
+(defun gnus-group-visible-select-group (&optional all)
+ "Select the current group without hiding any articles."
+ (interactive "P")
+ (let ((gnus-inhibit-limiting t))
+ (gnus-group-read-group all t)))
+
;;;###autoload
(defun gnus-fetch-group (group)
"Start Gnus if necessary and enter GROUP.
(gnus-group-read-group t t group)
(error nil)
(quit nil))
-; (debug (current-buffer))
(not (equal (current-buffer) cur))))
(defun gnus-group-jump-to-group (group)
(defun gnus-group-make-help-group ()
"Create the Gnus documentation group."
(interactive)
- (let ((path (cons (concat installation-directory "etc/") load-path))
+ (let ((path load-path)
(name (gnus-group-prefixed-name "gnus-help" '(nndoc "gnus-help")))
- file)
+ file dir)
(and (gnus-gethash name gnus-newsrc-hashtb)
(error "Documentation group already exists"))
- (while (and path
- (not (file-exists-p
- (setq file (concat (file-name-as-directory (car path))
- "gnus-tut.txt")))))
- (setq path (cdr path)))
- (if (not path)
+ (while path
+ (setq dir (file-name-as-directory (expand-file-name (pop path)))
+ file nil)
+ (when (or (file-exists-p (setq file (concat dir "gnus-tut.txt")))
+ (file-exists-p
+ (setq file (concat (file-name-directory
+ (directory-file-name dir))
+ "etc/gnus-tut.txt"))))
+ (setq path nil)))
+ (if (not file)
(message "Couldn't find doc group")
(gnus-group-make-group
(gnus-group-real-name name)
The killed newsgroups can be yanked by using \\[gnus-group-yank-group].
However, only groups that were alive can be yanked; already killed
groups or zombie groups can't be yanked.
-The return value is the name of the (last) group that was killed."
+The return value is the name of the group that was killed, or a list
+of groups killed."
(interactive "P")
(let ((buffer-read-only nil)
(groups (gnus-group-process-prefix n))
- group entry level)
- (if (or t (< (length groups) 10))
+ group entry level out)
+ (if (< (length groups) 10)
;; This is faster when there are few groups.
(while groups
- (setq group (car groups)
- groups (cdr groups))
+ (push (setq group (pop groups)) out)
(gnus-group-remove-mark group)
(setq level (gnus-group-group-level))
(gnus-delete-line)
(if entry entry group) gnus-level-killed (if entry nil level)))
;; If there are lots and lots of groups to be killed, we use
;; this thing instead.
- ;; !!! Not written.
- )
-
+ (let (entry)
+ (setq groups (nreverse groups))
+ (while groups
+ (gnus-group-remove-mark (car groups))
+ (gnus-delete-line)
+ (setq entry (gnus-gethash (pop groups) gnus-newsrc-hashtb))
+ (push (cons (car entry) (nth 2 entry))
+ gnus-list-of-killed-groups)
+ (setcdr (cdr entry) (cdr (cdr (cdr entry)))))
+ (gnus-make-hashtable-from-newsrc-alist)))
+
(gnus-group-position-point)
- group))
+ (if (< (length out) 2) (car out) (nreverse out))))
(defun gnus-group-yank-group (&optional arg)
"Yank the last newsgroups killed with \\[gnus-group-kill-group],
inserting it before the current newsgroup. The numeric ARG specifies
-how many newsgroups are to be yanked. The name of the (last)
-newsgroup yanked is returned."
+how many newsgroups are to be yanked. The name of the newsgroup yanked
+is returned, or (if several groups are yanked) a list of yanked groups
+is returned."
(interactive "p")
- (if (not arg) (setq arg 1))
- (let (info group prev)
- (while (>= (setq arg (1- arg)) 0)
- (if (not (setq info (car gnus-list-of-killed-groups)))
+ (setq arg (or arg 1))
+ (let (info group prev out)
+ (while (>= (decf arg) 0)
+ (if (not (setq info (pop gnus-list-of-killed-groups)))
(error "No more newsgroups to yank"))
- (setq group (nth 1 info))
+ (push (setq group (nth 1 info)) out)
;; Find which newsgroup to insert this one before - search
;; backward until something suitable is found. If there are no
;; other newsgroups in this buffer, just make this newsgroup the
info (nth 2 info) gnus-level-killed
(and prev (gnus-gethash prev gnus-newsrc-hashtb))
t)
- (gnus-group-insert-group-line-info group)
- (setq gnus-list-of-killed-groups
- (cdr gnus-list-of-killed-groups)))
+ (gnus-group-insert-group-line-info group))
(forward-line -1)
(gnus-group-position-point)
- group))
+ (if (< (length out) 2) (car out) (nreverse out))))
+
+(defun gnus-group-kill-level (level)
+ "Kill all groups that is on a certain LEVEL."
+ (interactive "nKill all groups on level: ")
+ (cond
+ ((= level gnus-level-zombie)
+ (setq gnus-killed-list
+ (nconc gnus-zombie-list gnus-killed-list))
+ (setq gnus-zombie-list nil))
+ ((and (< level gnus-level-zombie)
+ (> level 0)
+ (or gnus-expert-user
+ (gnus-yes-or-no-p
+ (format
+ "Do you really want to kill all groups on level %d? "
+ level))))
+ (let* ((prev gnus-newsrc-alist)
+ (alist (cdr prev)))
+ (while alist
+ (if (= (gnus-info-level level) level)
+ (setcdr prev (cdr alist))
+ (setq prev alist))
+ (setq alist (cdr alist)))
+ (gnus-make-hashtable-from-newsrc-alist)
+ (gnus-group-list-groups)))
+ (t
+ (error "Can't kill; illegal level: %d" level))))
(defun gnus-group-list-all-groups (&optional arg)
"List all newsgroups with level ARG or lower.
(setq groups (cdr groups)))
(goto-char (point-min))))
+(defun gnus-activate-all-groups (level)
+ "Activate absolutely all groups."
+ (interactive (list 7))
+ (let ((gnus-activate-level level)
+ (gnus-activate-foreign-newsgroups level))
+ (gnus-group-get-new-news)))
+
(defun gnus-group-get-new-news (&optional arg)
"Get newly arrived articles.
If ARG is a number, it specifies which levels you are interested in
(if (and gnus-read-active-file (not arg))
(progn
(gnus-read-active-file)
- (gnus-get-unread-articles (or arg (1+ gnus-level-subscribed))))
+ (gnus-get-unread-articles arg))
(let ((gnus-read-active-file (if arg nil gnus-read-active-file)))
- (gnus-get-unread-articles (or arg (1+ gnus-level-subscribed)))))
+ (gnus-get-unread-articles arg)))
(gnus-group-list-groups))
(defun gnus-group-get-new-news-this-group (&optional n)
(defun gnus-group-clear-dribble ()
"Clear all information from the dribble buffer."
(interactive)
- (gnus-dribble-clear))
+ (gnus-dribble-clear)
+ (gnus-message 7 "Cleared dribble buffer"))
(defun gnus-group-exit ()
"Quit reading news after updating .newsrc.eld and .newsrc.
(erase-buffer))
(gnus-browse-mode)
(setq mode-line-buffer-identification
- (format
- "Gnus Browse Server {%s:%s}" (car method) (car (cdr method))))
+ (list
+ (format
+ "Gnus: %%b {%s:%s}" (car method) (car (cdr method)))))
(save-excursion
(set-buffer nntp-server-buffer)
(let ((cur (current-buffer)))
(define-key gnus-summary-mode-map "v" 'gnus-summary-verbose-headers)
(define-key gnus-summary-mode-map "\C-c\C-b" 'gnus-bug)
+ (define-key gnus-summary-mode-map "*" 'gnus-cache-enter-article)
+ (define-key gnus-summary-mode-map "\M-*" 'gnus-cache-remove-article)
;; Sort of orthogonal keymap
(define-prefix-command 'gnus-summary-mark-map)
(define-key gnus-summary-limit-map "v" 'gnus-summary-limit-to-score)
(define-key gnus-summary-limit-map "D" 'gnus-summary-limit-include-dormant)
(define-key gnus-summary-limit-map "d" 'gnus-summary-limit-exclude-dormant)
+; (define-key gnus-summary-limit-map "t" 'gnus-summary-limit-exclude-thread)
(define-key gnus-summary-mark-map "E" 'gnus-summary-limit-include-expunged)
(define-key gnus-summary-limit-map "c"
'gnus-summary-limit-exclude-childless-dormant)
'(progn
(gnus-summary-skip-intangible)
(or (get-text-property (point) 'gnus-number)
- (progn
- (forward-line -1)
- gnus-newsgroup-end))))
+ (gnus-summary-last-subject))))
(defmacro gnus-summary-article-header (&optional number)
`(gnus-data-header (gnus-data-find
(if (< gnus-tmp-score gnus-summary-default-score)
gnus-score-below-mark gnus-score-over-mark)))
(gnus-tmp-replied (cond (gnus-tmp-process gnus-process-mark)
+ ((memq gnus-tmp-current gnus-newsgroup-cached)
+ gnus-cached-mark)
(gnus-tmp-replied gnus-replied-mark)
+ ((memq gnus-tmp-current gnus-newsgroup-saved)
+ gnus-saved-mark)
(t gnus-unread-mark)))
(gnus-tmp-from (mail-header-from gnus-tmp-header))
(gnus-tmp-name
;; This summary buffer exists already, so we just select it.
((not new-group)
(gnus-set-global-variables)
- (gnus-kill-buffer kill-buffer)
+ (gnus-kill-or-deaden-summary kill-buffer)
(gnus-configure-windows 'summary 'force)
(gnus-set-mode-line 'summary)
(gnus-summary-position-point)
(and (eq major-mode 'gnus-summary-mode)
(not (equal (current-buffer) kill-buffer))
(kill-buffer (current-buffer)))
- (gnus-kill-buffer kill-buffer)
+ (gnus-kill-or-deaden-summary kill-buffer)
(if (not quit-config)
(progn
(set-buffer gnus-group-buffer)
;; This newsgroup is empty.
(gnus-summary-catchup-and-exit nil t) ;Without confirmations.
(gnus-message 6 "No unread news")
- (gnus-kill-buffer kill-buffer)
+ (gnus-kill-or-deaden-summary kill-buffer)
;; Return nil from this function.
nil)
;; Hide conversation thread subtrees. We cannot do this in
;; If we are in async mode, we send some info to the backend.
(when gnus-newsgroup-async
(gnus-request-asynchronous gnus-newsgroup-name gnus-newsgroup-data))
- (gnus-kill-buffer kill-buffer)
+ (gnus-kill-or-deaden-summary kill-buffer)
(when (get-buffer-window gnus-group-buffer)
;; Gotta use windows, because recenter does wierd stuff if
;; the current buffer ain't the displayed window.
(list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207" ;
(vector thread) 2))
+(defsubst gnus-article-sort-by-number (h1 h2)
+ "Sort articles by article number."
+ (< (mail-header-number h1)
+ (mail-header-number h2)))
+
(defun gnus-thread-sort-by-number (h1 h2)
"Sort threads by root article number."
- (< (mail-header-number (gnus-thread-header h1))
- (mail-header-number (gnus-thread-header h2))))
+ (gnus-article-sort-by-number
+ (gnus-thread-header h1) (gnus-thread-header h2)))
-(defun gnus-thread-sort-by-author (h1 h2)
- "Sort threads by root author."
+(defsubst gnus-article-sort-by-author (h1 h2)
+ "Sort articles by root author."
(string-lessp
(let ((extract (funcall
gnus-extract-address-components
- (mail-header-from (gnus-thread-header h1)))))
+ (mail-header-from h1))))
(or (car extract) (cdr extract)))
(let ((extract (funcall
gnus-extract-address-components
- (mail-header-from (gnus-thread-header h2)))))
+ (mail-header-from h2))))
(or (car extract) (cdr extract)))))
+(defun gnus-thread-sort-by-author (h1 h2)
+ "Sort threads by root author."
+ (gnus-article-sort-by-author
+ (gnus-thread-header h1) (gnus-thread-header h2)))
+
+(defsubst gnus-article-sort-by-subject (h1 h2)
+ "Sort articles by root subject."
+ (string-lessp
+ (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
+ (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
+
(defun gnus-thread-sort-by-subject (h1 h2)
"Sort threads by root subject."
+ (gnus-article-sort-by-subject
+ (gnus-thread-header h1) (gnus-thread-header h2)))
+
+(defsubst gnus-article-sort-by-date (h1 h2)
+ "Sort articles by root article date."
(string-lessp
- (downcase (gnus-simplify-subject-re
- (mail-header-subject (gnus-thread-header h1))))
- (downcase (gnus-simplify-subject-re
- (mail-header-subject (gnus-thread-header h2))))))
+ (gnus-sortable-date (mail-header-date h1))
+ (gnus-sortable-date (mail-header-date h2))))
(defun gnus-thread-sort-by-date (h1 h2)
"Sort threads by root article date."
- (string-lessp
- (gnus-sortable-date (mail-header-date (gnus-thread-header h1)))
- (gnus-sortable-date (mail-header-date (gnus-thread-header h2)))))
+ (gnus-article-sort-by-date
+ (gnus-thread-header h1) (gnus-thread-header h2)))
-(defun gnus-thread-sort-by-score (h1 h2)
- "Sort threads by root article score.
+(defsubst gnus-article-sort-by-score (h1 h2)
+ "Sort articles by root article score.
Unscored articles will be counted as having a score of zero."
- (> (or (cdr (assq (mail-header-number (gnus-thread-header h1))
+ (> (or (cdr (assq (mail-header-number h1)
gnus-newsgroup-scored))
gnus-summary-default-score 0)
- (or (cdr (assq (mail-header-number (gnus-thread-header h2))
+ (or (cdr (assq (mail-header-number h2)
gnus-newsgroup-scored))
gnus-summary-default-score 0)))
+(defun gnus-thread-sort-by-score (h1 h2)
+ "Sort threads by root article score."
+ (gnus-article-sort-by-score
+ (gnus-thread-header h1) (gnus-thread-header h2)))
+
(defun gnus-thread-sort-by-total-score (h1 h2)
"Sort threads by the sum of all scores in the thread.
Unscored articles will be counted as having a score of zero."
gnus-tmp-replied
(cond ((memq number gnus-newsgroup-processable)
gnus-process-mark)
+ ((memq number gnus-newsgroup-cached)
+ gnus-cached-mark)
((memq number gnus-newsgroup-replied)
gnus-replied-mark)
(t gnus-unread-mark))
(types '((marked . tick) (replied . reply)
(expirable . expire) (killed . killed)
(bookmarks . bookmark) (dormant . dormant)
- (scored . score)))
+ (scored . score) (saved . save)))
(uncompressed '(score bookmark))
marks var articles article mark)
;; All articles have to be subsets of the active articles.
(cond
;; Adjust "simple" lists.
- ((memq mark '(tick dormant expirable reply killed))
+ ((memq mark '(tick dormant expirable reply killed save))
(while articles
(when (or (< (setq article (pop articles)) min) (> article max))
(set var (delq article (symbol-value var))))))
(let ((types '((marked . tick) (replied . reply)
(expirable . expire) (killed . killed)
(bookmarks . bookmark) (dormant . dormant)
- (scored . score)))
+ (scored . score) (saved . save)))
(info (gnus-get-info gnus-newsgroup-name))
(uncompressed '(score bookmark killed))
var type list newmarked symbol)
;; Pad the mode string a bit.
(setq mode-string (format (format "%%-%ds" max-len) mode-string))))
;; Update the mode line.
- (setq mode-line-buffer-identification mode-string)
+ (setq mode-line-buffer-identification (list mode-string))
(set-buffer-modified-p t))))
(defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
(not (gnus-data-unread-p (car arts))))
(setq arts (cdr arts)))
(when (setq result
- (if unread
- (progn
- (while arts
- (when (gnus-data-unread-p (car arts))
- (setq result (car arts)
- arts nil))
- (setq arts (cdr arts)))
- result)
- (car arts)))
+ (if unread
+ (progn
+ (while arts
+ (when (gnus-data-unread-p (car arts))
+ (setq result (car arts)
+ arts nil))
+ (setq arts (cdr arts)))
+ result)
+ (car arts)))
(goto-char (gnus-data-pos result))
(gnus-data-number result)))))
(let ((current-subject (gnus-summary-article-number))
(group gnus-newsgroup-name))
(setq gnus-newsgroup-begin nil)
- (gnus-summary-exit t)
+ (gnus-summary-exit)
;; We have to adjust the point of group mode buffer because the
;; current point was moved to the next unread newsgroup by
;; exiting.
;; Make all changes in this group permanent.
(gnus-summary-update-info)
(set-buffer buf)
- (and gnus-use-cache (gnus-cache-possibly-remove-articles))
+ (when gnus-use-cache
+ (gnus-cache-possibly-remove-articles)
+ (gnus-cache-save-buffers))
;; Make sure where I was, and go to next newsgroup.
(set-buffer gnus-group-buffer)
(or quit-config
(gnus-group-next-unread-group 1)))
(if temporary
nil ;Nothing to do.
- ;; We set all buffer-local variables to nil. It is unclear why
- ;; this is needed, but if we don't, buffer-local variables are
- ;; not garbage-collected, it seems. This would the lead to en
- ;; ever-growing Emacs.
- (set-buffer buf)
- (gnus-summary-clear-local-variables)
- ;; We clear the global counterparts of the buffer-local
- ;; variables as well, just to be on the safe side.
- (gnus-configure-windows 'group 'force)
- (gnus-summary-clear-local-variables)
- ;; Return to group mode buffer.
- (if (eq mode 'gnus-summary-mode)
- (gnus-kill-buffer buf))
- (if (get-buffer gnus-article-buffer)
- (bury-buffer gnus-article-buffer))
+ (if (not gnus-kill-summary-on-exit)
+ (gnus-deaden-summary)
+ ;; We set all buffer-local variables to nil. It is unclear why
+ ;; this is needed, but if we don't, buffer-local variables are
+ ;; not garbage-collected, it seems. This would the lead to en
+ ;; ever-growing Emacs.
+ (set-buffer buf)
+ (gnus-summary-clear-local-variables)
+ ;; We clear the global counterparts of the buffer-local
+ ;; variables as well, just to be on the safe side.
+ (gnus-configure-windows 'group 'force)
+ (gnus-summary-clear-local-variables)
+ ;; Return to group mode buffer.
+ (if (eq mode 'gnus-summary-mode)
+ (gnus-kill-buffer buf)))
(setq gnus-current-select-method gnus-select-method)
(pop-to-buffer gnus-group-buffer)
;; Clear the current group name.
(when (or no-questions
gnus-expert-user
(gnus-y-or-n-p "Do you really wanna quit reading this group? "))
- (gnus-close-group group)
- (gnus-summary-clear-local-variables)
- (set-buffer gnus-group-buffer)
- (gnus-summary-clear-local-variables)
+ (if (not gnus-kill-summary-on-exit)
+ (gnus-deaden-summary)
+ (gnus-close-group group)
+ (gnus-summary-clear-local-variables)
+ (set-buffer gnus-group-buffer)
+ (gnus-summary-clear-local-variables)
+ (when (get-buffer gnus-summary-buffer)
+ (kill-buffer gnus-summary-buffer)))
;; Return to the group buffer.
(gnus-configure-windows 'group 'force)
;; Clear the current group name.
(setq gnus-newsgroup-name nil)
- (when (get-buffer gnus-summary-buffer)
- (kill-buffer gnus-summary-buffer))
- (when (get-buffer gnus-article-buffer)
- (bury-buffer gnus-article-buffer))
(when (equal (gnus-group-group-name) group)
(gnus-group-next-unread-group 1))
(when quit-config
(gnus-set-global-variables))
(gnus-configure-windows (cdr quit-config)))))))
+;;; Dead summaries.
+
+(defvar gnus-dead-summary-mode-map nil)
+
+(if gnus-dead-summary-mode-map
+ nil
+ (setq gnus-dead-summary-map (make-keymap))
+ (suppress-keymap gnus-dead-summary-map)
+ (substitute-key-definition
+ 'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-map)
+ (let ((keys '("\C-d" "\r" "\177")))
+ (while keys
+ (define-key gnus-dead-summary-map
+ (pop keys) 'gnus-summary-wake-up-the-dead))))
+
+(defvar gnus-dead-summary-mode nil
+ "Minor mode for Gnus summary buffers.")
+
+(defun gnus-dead-summary-mode (&optional arg)
+ "Minor mode for Gnus summary buffers."
+ (interactive "P")
+ (when (eq major-mode 'gnus-summary-mode)
+ (make-local-variable 'gnus-dead-summary-mode)
+ (setq gnus-dead-summary-mode
+ (if (null arg) (not gnus-dead-summary-mode)
+ (> (prefix-numeric-value arg) 0)))
+ (when gnus-dead-summary-mode
+ (unless (assq 'gnus-dead-summary-mode minor-mode-alist)
+ (push '(gnus-dead-summary-mode " Dead") minor-mode-alist))
+ (unless (assq 'gnus-dead-summary-mode minor-mode-map-alist)
+ (push (cons 'gnus-dead-summary-mode gnus-dead-summary-mode-map)
+ minor-mode-map-alist)))))
+
+(defun gnus-deaden-summary ()
+ "Make the current summary buffer into a dead summary buffer."
+ ;; Kill any previous dead summary buffer.
+ (when (and gnus-dead-summary
+ (buffer-name gnus-dead-summary))
+ (save-excursion
+ (set-buffer gnus-dead-summary)
+ (when gnus-dead-summary-mode
+ (kill-buffer (current-buffer)))))
+ ;; Make this the current dead summary.
+ (setq gnus-dead-summary (current-buffer))
+ (gnus-dead-summary-mode 1)
+ (let ((name (buffer-name)))
+ (when (string-match "Summary" name)
+ (rename-buffer
+ (concat (substring name 0 (match-beginning 0)) "Dead "
+ (substring name (match-beginning 0))) t))))
+
+(defun gnus-kill-or-deaden-summary (buffer)
+ "Kill or deaden the summary BUFFER."
+ (cond (gnus-kill-summary-on-exit
+ (gnus-kill-buffer buffer))
+ ((and (get-buffer buffer)
+ (buffer-name (get-buffer buffer)))
+ (save-excursion
+ (set-buffer buffer)
+ (gnus-deaden-summary)))))
+
+(defun gnus-summary-wake-up-the-dead (&rest args)
+ "Wake up the dead summary buffer."
+ (interactive)
+ (gnus-dead-summary-mode -1)
+ (let ((name (buffer-name)))
+ (when (string-match "Dead " name)
+ (rename-buffer
+ (concat (substring name 0 (match-beginning 0))
+ (substring name (match-end 0))) t)))
+ (gnus-message 3 "This dead summary is now alive again"))
+
;; Suggested by Andrew Eskilsson <pi92ae@pt.hk-r.se>.
(defun gnus-summary-fetch-faq (&optional faq-dir)
"Fetch the FAQ for the current group.
;; For some reason, the group window gets selected. We change
;; it back.
(select-window (get-buffer-window (current-buffer)))
- ;; Keep just the event type of CMD.
- ;(and (listp cmd) (setq cmd (car cmd)))
;; Select next unread newsgroup automagically.
(cond
((not gnus-auto-select-next)
"exiting"))
(gnus-summary-next-group nil group backward)))
(t
- (let ((keystrokes '(?\C-n ?\C-p))
- key)
- (while (or (null key) (memq key keystrokes))
- (gnus-message
- 7 "No more%s articles%s" (if unread " unread" "")
- (if (and group
- (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
- (format " (Type %s for %s [%s])"
- (single-key-description cmd) group
- (car (gnus-gethash group gnus-newsrc-hashtb)))
- (format " (Type %s to exit %s)"
- (single-key-description cmd)
- gnus-newsgroup-name)))
- ;; Confirm auto selection.
- (let* ((event (read-char-exclusive)))
- (setq key (if (listp event) (car event) event))
- (if (memq key keystrokes)
- (let ((obuf (current-buffer)))
- (switch-to-buffer gnus-group-buffer)
- (and group
- (gnus-group-jump-to-group group))
- (condition-case ()
- (cond ((= key ?\C-n)
- (gnus-group-next-unread-group 1))
- ((= key ?\C-p)
- (gnus-group-prev-unread-group 1)))
- (error (ding) nil))
- (setq group (gnus-group-group-name))
- (switch-to-buffer obuf)))))
- (if (equal key cmd)
- (if (or (not group)
- (gnus-ephemeral-group-p gnus-newsgroup-name))
- (gnus-summary-exit)
- (gnus-summary-next-group nil group backward))
- (execute-kbd-macro (char-to-string key)))))))))))
+ (gnus-summary-walk-group-buffer
+ gnus-newsgroup-name cmd unread backward))))))))
+
+(defun gnus-summary-walk-group-buffer (from-group cmd unread backward)
+ (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1))
+ (?\C-p (gnus-group-prev-unread-group 1))))
+ keve key group ended)
+ (while (not ended)
+ (save-excursion
+ (set-buffer gnus-group-buffer)
+ (setq group (gnus-group-group-name)))
+ (gnus-message
+ 7 "No more%s articles%s" (if unread " unread" "")
+ (if (and group
+ (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
+ (format " (Type %s for %s [%s])"
+ (single-key-description cmd) group
+ (car (gnus-gethash group gnus-newsrc-hashtb)))
+ (format " (Type %s to exit %s)"
+ (single-key-description cmd)
+ gnus-newsgroup-name)))
+ ;; Confirm auto selection.
+ (setq key (car (setq keve (gnus-read-event-char))))
+ (setq ended t)
+ (cond
+ ((assq key keystrokes)
+ (let ((obuf (current-buffer)))
+ (switch-to-buffer gnus-group-buffer)
+ (and group
+ (gnus-group-jump-to-group group))
+ (eval (car (cdr (assq key keystrokes))))
+ (setq group (gnus-group-group-name))
+ (switch-to-buffer obuf))
+ (setq ended nil))
+ ((equal key cmd)
+ (if (or (not group)
+ (gnus-ephemeral-group-p gnus-newsgroup-name))
+ (gnus-summary-exit)
+ (gnus-summary-next-group nil group backward)))
+ (t
+ (push (cdr keve) unread-command-events))))))
+
+(defun gnus-read-event-char ()
+ "Get the next event."
+ (let ((event (read-event)))
+ (cons (and (numberp event) event) event)))
(defun gnus-summary-next-unread-article ()
"Select unread article after current one."
(error "No unread articles"))
(gnus-summary-position-point)))
+(defun gnus-summary-last-subject ()
+ "Go to the last displayed subject line in the group."
+ (let ((article (gnus-data-number (car (gnus-data-list t)))))
+ (when article
+ (gnus-summary-goto-subject article))))
+
(defun gnus-summary-goto-article (article &optional all-headers force)
"Fetch ARTICLE and display it if it exists.
If ALL-HEADERS is non-nil, no header lines are hidden."
This entails weeding out unwanted dormants, low-scored articles,
fetch-old-headers verbiage, and so on."
;; Most groups have nothing to remove.
- (if (and (null gnus-newsgroup-dormant)
- (not (eq gnus-fetch-old-headers 'some))
- (null gnus-summary-expunge-below))
+ (if (or gnus-inhibit-limiting
+ (and (null gnus-newsgroup-dormant)
+ (not (eq gnus-fetch-old-headers 'some))
+ (null gnus-summary-expunge-below)
+ (null gnus-thread-expunge-below)))
() ; Do nothing.
- (setq gnus-newsgroup-limits
- (cons gnus-newsgroup-limit gnus-newsgroup-limits))
+ (push gnus-newsgroup-limit gnus-newsgroup-limits)
(setq gnus-newsgroup-limit nil)
(mapatoms
(lambda (node)
- (if (null (car (symbol-value node)))
- (let ((nodes (cdr (symbol-value node))))
- (while nodes
- (gnus-summary-limit-children (car nodes))
- (setq nodes (cdr nodes))))))
+ (unless (car (symbol-value node))
+ ;; These threads have no parents -- they are roots.
+ (let ((nodes (cdr (symbol-value node))))
+ (while nodes
+ (if (and gnus-thread-expunge-below
+ (< (gnus-thread-total-score (car nodes))
+ gnus-thread-expunge-below))
+ (gnus-expunge-thread (pop nodes))
+ (gnus-summary-limit-children (pop nodes)))))))
gnus-newsgroup-dependencies)
(when (not gnus-newsgroup-limit)
(setq gnus-newsgroup-limit (pop gnus-newsgroup-limits)))
;; working its way up towards the root.
(let ((children
(if (cdr thread)
- (apply '+ (mapcar (lambda (th)
- (gnus-summary-limit-children th))
+ (apply '+ (mapcar 'gnus-summary-limit-children
(cdr thread)))
0))
(number (mail-header-number (car thread)))
(setq gnus-newsgroup-limit (cons number gnus-newsgroup-limit))
1)))
+(defun gnus-expunge-thread (thread)
+ "Mark all articles in THREAD as read."
+ (let* ((number (mail-header-number (car thread))))
+ (incf gnus-newsgroup-expunged-tally)
+ ;; We also mark as read here, if that's wanted.
+ (setq gnus-newsgroup-unreads
+ (delq number gnus-newsgroup-unreads))
+ (if gnus-newsgroup-auto-expire
+ (push number gnus-newsgroup-expirable)
+ (push (cons number gnus-low-score-mark)
+ gnus-newsgroup-reads)))
+ ;; Go recursively through all subthreads.
+ (mapcar 'gnus-expunge-thread (cdr thread)))
+
;; Summary article oriented commands
(defun gnus-summary-refer-parent-article (n)
(goto-char (point-min))
(setq e (1- (or (search-forward "\n\n" nil t) (point-max)))))
(insert-buffer-substring gnus-original-article-buffer 1 e)
- (let ((hook (delq 'gnus-article-hide-headers-if-wanted
- (delq 'gnus-article-hide-headers
- (copy-sequence gnus-article-display-hook))))
- (gnus-inhibit-hiding t))
- (run-hooks 'hook))
+ (let ((gnus-inhibit-hiding t))
+ (run-hooks 'gnus-article-display-hook))
(if (or (not hidden) (and (numberp arg) (< arg 0)))
(gnus-article-hide-headers)))))
(dormant . gnus-newsgroup-dormant)
(expire . gnus-newsgroup-expirable)
(bookmark . gnus-newsgroup-bookmarks)
+ (save . gnus-newsgroup-saved)
(reply . gnus-newsgroup-replied)))
(to-article (cdr art-group)))
(dormant . gnus-newsgroup-dormant)
(expire . gnus-newsgroup-expirable)
(bookmark . gnus-newsgroup-bookmarks)
+ (save . gnus-newsgroup-saved)
(reply . gnus-newsgroup-replied)))
(to-article (cdr art-group)))
"Mark ARTICLE replied and update the summary line."
(setq gnus-newsgroup-replied (cons article gnus-newsgroup-replied))
(let ((buffer-read-only nil))
- (if (gnus-summary-goto-subject article)
- (progn
- (gnus-summary-update-mark gnus-replied-mark 'replied)
- t))))
+ (when (gnus-summary-goto-subject article)
+ (gnus-summary-update-secondary-mark article))))
(defun gnus-summary-set-bookmark (article)
"Set a bookmark in current article."
(setq gnus-newsgroup-processable
(cons article
(delq article gnus-newsgroup-processable)))
- (let ((buffer-read-only nil))
- (if (gnus-summary-goto-subject article)
- (progn
- (gnus-summary-show-thread)
- (gnus-summary-update-mark gnus-process-mark 'replied)
- t))))
+ (when (gnus-summary-goto-subject article)
+ (gnus-summary-show-thread)
+ (gnus-summary-update-secondary-mark article)))
(defun gnus-summary-remove-process-mark (article)
"Remove the process mark from ARTICLE and update the summary line."
(setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable))
- (let ((buffer-read-only nil))
- (if (gnus-summary-goto-subject article)
- (progn
- (gnus-summary-show-thread)
- (gnus-summary-update-mark ? 'replied)
- (if (memq article gnus-newsgroup-replied)
- (gnus-summary-update-mark gnus-replied-mark 'replied))
- t))))
+ (when (gnus-summary-goto-subject article)
+ (gnus-summary-show-thread)
+ (gnus-summary-update-secondary-mark article)))
+
+(defun gnus-summary-set-saved-mark (article)
+ "Set the process mark on ARTICLE and update the summary line."
+ (push article gnus-newsgroup-saved)
+ (when (gnus-summary-goto-subject article)
+ (gnus-summary-update-secondary-mark article)))
(defun gnus-summary-mark-forward (n &optional mark no-expire)
"Mark N articles as read forwards.
(gnus-summary-update-mark mark 'unread)
t))))
+(defun gnus-summary-update-secondary-mark (article)
+ "Update the secondary (read, process, cache) mark."
+ (gnus-summary-update-mark
+ (cond ((memq article gnus-newsgroup-processable)
+ gnus-process-mark)
+ ((memq article gnus-newsgroup-cached)
+ gnus-cached-mark)
+ ((memq article gnus-newsgroup-replied)
+ gnus-replied-mark)
+ ((memq article gnus-newsgroup-saved)
+ gnus-saved-mark)
+ (t gnus-unread-mark))
+ 'replied)
+ (when (gnus-visual-p 'summary-highlight 'highlight)
+ (run-hooks 'gnus-summary-update-hook))
+ t)
+
(defun gnus-summary-update-mark (mark type)
(beginning-of-line)
(let ((forward (cdr (assq type gnus-summary-mark-positions)))
(interactive "P")
(gnus-set-global-variables)
(gnus-summary-catchup all)
- (gnus-summary-next-group))
+ (gnus-summary-next-article t))
;; Thread-based commands.
"Sort summary buffer by article number.
Argument REVERSE means reverse order."
(interactive "P")
- (gnus-set-global-variables)
- (gnus-summary-sort
- ;; `gnus-summary-article-number' is a macro, and `sort-subr' wants
- ;; a function, so we wrap it.
- (cons (lambda () (gnus-summary-article-number))
- 'gnus-thread-sort-by-number) reverse))
+ (gnus-summary-sort 'number reverse))
(defun gnus-summary-sort-by-author (&optional reverse)
"Sort summary buffer by author name alphabetically.
If case-fold-search is non-nil, case of letters is ignored.
Argument REVERSE means reverse order."
(interactive "P")
- (gnus-set-global-variables)
- (gnus-summary-sort
- (cons
- (lambda ()
- (let* ((header (gnus-summary-article-header))
- extract)
- (if (not (vectorp header))
- ""
- (setq extract (funcall gnus-extract-address-components
- (mail-header-from header)))
- (concat (or (car extract) (cdr extract))
- "\r" (mail-header-subject header)))))
- 'gnus-thread-sort-by-author)
- reverse))
+ (gnus-summary-sort 'author reverse))
(defun gnus-summary-sort-by-subject (&optional reverse)
"Sort summary buffer by subject alphabetically. `Re:'s are ignored.
If case-fold-search is non-nil, case of letters is ignored.
Argument REVERSE means reverse order."
(interactive "P")
- (gnus-set-global-variables)
- (gnus-summary-sort
- (cons
- (lambda ()
- (let* ((header (gnus-summary-article-header))
- extract)
- (if (not (vectorp header))
- ""
- (setq extract (funcall gnus-extract-address-components
- (mail-header-from header)))
- (concat
- (downcase (gnus-simplify-subject (gnus-summary-article-subject) t))
- "\r" (or (car extract) (cdr extract))))))
- 'gnus-thread-sort-by-subject)
- reverse))
+ (gnus-summary-sort 'subject reverse))
(defun gnus-summary-sort-by-date (&optional reverse)
"Sort summary buffer by date.
Argument REVERSE means reverse order."
(interactive "P")
- (gnus-set-global-variables)
- (gnus-summary-sort
- (cons
- (lambda ()
- (gnus-sortable-date
- (mail-header-date
- (gnus-summary-article-header))))
- 'gnus-thread-sort-by-date)
- reverse))
+ (gnus-summary-sort 'date reverse))
(defun gnus-summary-sort-by-score (&optional reverse)
"Sort summary buffer by score.
Argument REVERSE means reverse order."
(interactive "P")
- (gnus-set-global-variables)
- (gnus-summary-sort
- (cons (lambda () (gnus-summary-article-score))
- 'gnus-thread-sort-by-score)
- (not reverse)))
+ (gnus-summary-sort 'score reverse))
(defun gnus-summary-sort (predicate reverse)
- "Sort summary buffer by PREDICATE. REVERSE means reverse order.
-PREDICATE is a cons of `(unthreaded-func . threaded-func)'."
- (let (buffer-read-only)
- (if (not gnus-show-threads)
- ;; We do untreaded sorting...
- (progn
- (goto-char (point-min))
- (sort-subr reverse 'forward-line 'end-of-line (car predicate))
- (gnus-data-compute-positions))
- ;; ... or we do threaded sorting.
- (let ((gnus-thread-sort-functions (list (cdr predicate)))
- (gnus-summary-prepare-hook nil))
- ;; We do that by simply regenerating the threads.
- (gnus-summary-prepare)
- ;; Hide subthreads if needed.
- (when gnus-thread-hide-subtree
- (gnus-summary-hide-all-threads))))
- ;; If in async mode, we send some info to the backend.
- (when gnus-newsgroup-async
- (gnus-request-asynchronous
- gnus-newsgroup-name gnus-newsgroup-data))))
+ "Sort summary buffer by PREDICATE. REVERSE means reverse order."
+ (gnus-set-global-variables)
+ (let* ((gnus-thread-sort-functions
+ (list (intern (format "gnus-thread-sort-by-%s" predicate))))
+ (gnus-article-sort-functions
+ (list (intern (format "gnus-article-sort-by-%s" predicate))))
+ (buffer-read-only)
+ (gnus-summary-prepare-hook nil))
+ ;; We do the sorting by regenerating the threads.
+ (gnus-summary-prepare)
+ ;; Hide subthreads if needed.
+ (when (and gnus-show-threads gnus-thread-hide-subtree)
+ (gnus-summary-hide-all-threads)))
+ ;; If in async mode, we send some info to the backend.
+ (when gnus-newsgroup-async
+ (gnus-request-asynchronous
+ gnus-newsgroup-name gnus-newsgroup-data)))
(defun gnus-sortable-date (date)
"Make sortable string by string-lessp from DATE.
;; Summary saving commands.
-(defun gnus-summary-save-article (&optional n)
+(defun gnus-summary-save-article (&optional n not-saved)
"Save the current article using the default saver function.
If N is a positive number, save the N next articles.
If N is a negative number, save the N previous articles.
(interactive "P")
(gnus-set-global-variables)
(let ((articles (gnus-summary-work-articles n))
- file)
+ file header article)
(while articles
- (let ((header (gnus-summary-article-header (car articles))))
- (if (vectorp header)
- (progn
- (save-window-excursion
- (gnus-summary-select-article t nil nil (car articles)))
- (or gnus-save-all-headers
- ;; Remove headers accoring to `gnus-saved-headers'.
- (let ((gnus-visible-headers
- (or gnus-saved-headers gnus-visible-headers)))
- (gnus-article-hide-headers t)))
- ;; Remove any X-Gnus lines.
- (save-excursion
- (save-restriction
- (set-buffer gnus-article-buffer)
- (let ((buffer-read-only nil))
- (goto-char (point-min))
- (narrow-to-region (point) (or (search-forward "\n\n" nil t)
- (point-max)))
- (while (re-search-forward "^X-Gnus" nil t)
- (beginning-of-line)
- (delete-region (point)
- (progn (forward-line 1) (point))))
- (widen))))
- (save-window-excursion
- (if gnus-default-article-saver
- (setq file (funcall
- gnus-default-article-saver
- (cond
- ((not gnus-prompt-before-saving)
- 'default)
- ((eq gnus-prompt-before-saving 'always)
- nil)
- (t file))))
- (error "No default saver is defined."))))
+ (setq header (gnus-summary-article-header
+ (setq article (pop articles))))
+ (if (not (vectorp header))
+ ;; This is a pseudo-article.
(if (assq 'name header)
(gnus-copy-file (cdr (assq 'name header)))
- (gnus-message 1 "Article %d is unsaveable" (car articles)))))
- (gnus-summary-remove-process-mark (car articles))
- (setq articles (cdr articles)))
+ (gnus-message 1 "Article %d is unsaveable" article))
+ ;; This is a real article.
+ (save-window-excursion
+ (gnus-summary-select-article t nil nil article))
+ (unless gnus-save-all-headers
+ ;; Remove headers accoring to `gnus-saved-headers'.
+ (let ((gnus-visible-headers
+ (or gnus-saved-headers gnus-visible-headers)))
+ (gnus-article-hide-headers t)))
+ ;; Remove any X-Gnus lines.
+ (save-excursion
+ (set-buffer gnus-article-buffer)
+ (save-restriction
+ (let ((buffer-read-only nil))
+ (gnus-narrow-to-headers)
+ (while (re-search-forward "^X-Gnus" nil t)
+ (gnus-delete-line)))))
+ (save-window-excursion
+ (if (not gnus-default-article-saver)
+ (error "No default saver is defined.")
+ (setq file (funcall
+ gnus-default-article-saver
+ (cond
+ ((not gnus-prompt-before-saving)
+ 'default)
+ ((eq gnus-prompt-before-saving 'always)
+ nil)
+ (t file))))))
+ (gnus-summary-remove-process-mark article)
+ (unless not-saved
+ (gnus-summary-set-saved-mark article))))
(gnus-summary-position-point)
n))
(interactive "P")
(gnus-set-global-variables)
(let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
- (gnus-summary-save-article arg))
+ (gnus-summary-save-article arg t))
(gnus-configure-windows 'pipe))
(defun gnus-summary-save-article-mail (&optional arg)
(defun gnus-read-save-file-name (prompt default-name)
(let ((methods gnus-split-methods)
- split-name method)
+ split-name method result match)
;; Let the split methods have their say.
(when gnus-split-methods
(save-excursion
(set-buffer gnus-original-article-buffer)
- (gnus-narrow-to-headers)
- (while methods
- (goto-char (point-min))
- (setq method (pop methods))
- (when (cond ((stringp (car method))
- (condition-case ()
- (re-search-forward (car method) nil t)
- (error nil)))
- ((gnus-functionp (car method))
- (funcall (car method)))
- ((consp (car method))
- (eval (car method))))
- (setq split-name (cons (nth 1 methods) split-name))))
- (widen)))
+ (save-restriction
+ (gnus-narrow-to-headers)
+ (while methods
+ (goto-char (point-min))
+ (setq method (pop methods))
+ (setq match (pop method))
+ (when (cond
+ ((stringp match)
+ ;; Regular expression.
+ (condition-case ()
+ (re-search-forward match nil t)
+ (error nil)))
+ ((gnus-functionp match)
+ (save-restriction
+ (widen)
+ (setq result (funcall match gnus-newsgroup-name))))
+ ((consp match)
+ (save-restriction
+ (widen)
+ (setq result (eval match)))))
+ (setq split-name (append (cdr methods) split-name))
+ (cond ((stringp result)
+ (push result split-name))
+ ((consp result)
+ (setq split-name (append result split-name)))))))))
(cond
- ;; No split name was found
+ ;; No split name was found.
((null split-name)
(read-file-name
(concat prompt " (default " (file-name-nondirectory default-name) ") ")
(car (car split-name))
result)))))))
+(defun gnus-article-archive-name (group)
+ "Return the first instance of an \"Archive-name\" in the current buffer."
+ (let ((case-fold-search t))
+ (when (re-search-forward "archive-name: *\\([^ \n\t]+\\)[ \t]*$" nil t)
+ (match-string 1))))
+
(defun gnus-summary-save-in-rmail (&optional filename)
"Append this article to Rmail file.
Optional argument FILENAME specifies file name.
(define-key gnus-article-mode-map gnus-mouse-2 'gnus-article-push-button)
(define-key gnus-article-mode-map "\r" 'gnus-article-press-button)
(define-key gnus-article-mode-map "\t" 'gnus-article-next-button)
+ (define-key gnus-article-mode-map "\M-\t" 'gnus-article-prev-button)
(define-key gnus-article-mode-map "\C-c\C-b" 'gnus-bug)
;; Duplicate almost all summary keystrokes in the article mode map.
(defun gnus-article-hide-headers (&optional delete)
"Hide unwanted headers and possibly sort them as well."
(interactive "P")
+ ;; This function might be inhibited.
(unless gnus-inhibit-hiding
(save-excursion
(set-buffer gnus-article-buffer)
(save-restriction
- (let ((sorted gnus-sorted-header-list)
- (buffer-read-only nil)
+ (let ((buffer-read-only nil)
+ (ignored (when (not (stringp gnus-visible-headers))
+ (cond ((stringp gnus-ignored-headers)
+ gnus-ignored-headers)
+ ((listp gnus-ignored-headers)
+ (mapconcat 'identity gnus-ignored-headers
+ "\\|")))))
+ (visible (cond ((stringp gnus-visible-headers)
+ gnus-visible-headers)
+ ((listp gnus-visible-headers)
+ (mapconcat 'identity gnus-visible-headers
+ "\\|"))))
want-list beg want-l)
;; First we narrow to just the headers.
(widen)
;; Hide any "From " lines at the beginning of (mail) articles.
(while (looking-at "From ")
(forward-line 1))
- (or (bobp)
- (add-text-properties (point-min) (point) gnus-hidden-properties))
+ (unless (bobp)
+ (add-text-properties (point-min) (point) gnus-hidden-properties))
;; Then treat the rest of the header lines.
(narrow-to-region
(point)
(beginning-of-line)
;; We add the headers we want to keep to a list and delete
;; them from the buffer.
- (if (or (and (stringp gnus-visible-headers)
- (looking-at gnus-visible-headers))
- (and (not (stringp gnus-visible-headers))
- (stringp gnus-ignored-headers)
- (not (looking-at gnus-ignored-headers))))
+ (if (or (and visible (looking-at visible))
+ (and ignored (not (looking-at ignored))))
(progn
- (setq beg (point))
- (forward-line 1)
- ;; Be sure to get multi-line headers...
- (re-search-forward "^[^ \t]*:" nil t)
- (beginning-of-line)
- (setq want-list
- (cons (buffer-substring beg (point)) want-list))
- (delete-region beg (point))
- (goto-char beg))
+ (push (buffer-substring
+ (setq beg (point))
+ (progn
+ (forward-line 1)
+ ;; Be sure to get multi-line headers...
+ (re-search-forward "^[^ \t]*:" nil t)
+ (beginning-of-line)
+ (point)))
+ want-list)
+ (delete-region beg (point)))
(forward-line 1)))
- ;; Next we perform the sorting by looking at
- ;; `gnus-sorted-header-list'.
+ ;; Sort the headers that we want to display.
+ (setq want-list (sort want-list 'gnus-article-header-less))
(goto-char (point-min))
- (while (and sorted want-list)
- (setq want-l want-list)
- (while (and want-l
- (not (string-match (car sorted) (car want-l))))
- (setq want-l (cdr want-l)))
- (if want-l
- (progn
- (insert (car want-l))
- (setq want-list (delq (car want-l) want-list))))
- (setq sorted (cdr sorted)))
- ;; Any headers that were not matched by the sorted list we
- ;; just tack on the end of the visible header list.
(while want-list
- (insert (car want-list))
- (setq want-list (cdr want-list)))
- ;; And finally we make the unwanted headers invisible.
+ (insert (pop want-list)))
+ ;; We make the unwanted headers invisible.
(if delete
- (delete-region (point) (point-max))
+ (delete-region (point-min) (point-max))
;; Suggested by Sudish Joseph <joseph@cis.ohio-state.edu>.
(add-text-properties
(point) (point-max) gnus-hidden-properties)))))))
+(defsubst gnus-article-header-rank (header)
+ "Give the rank of the string HEADER as given by `gnus-sorted-header-list'."
+ (let ((list gnus-sorted-header-list)
+ (i 0))
+ (while list
+ (when (string-match (car list) header)
+ (setq list nil))
+ (setq list (cdr list))
+ (incf i))
+ i))
+
+(defun gnus-article-header-less (h1 h2)
+ "Say whether string H1 is \"less\" than string H2."
+ (< (gnus-article-header-rank h1)
+ (gnus-article-header-rank h2)))
+
;; Written by Per Abrahamsen <amanda@iesd.auc.dk>.
(defun gnus-article-treat-overstrike ()
"Translate overstrikes into bold text."
If TYPE is `local', convert to local time; if it is `lapsed', output
how much time has lapsed since DATE."
(interactive (list 'ut t))
- (let ((date (mail-header-date (or gnus-current-headers
- (gnus-summary-article-header) "")))
- (date-regexp "^Date: \\|^X-Sent: ")
- (inhibit-point-motion-hooks t))
+ (let* ((header (or gnus-current-headers
+ (gnus-summary-article-header) ""))
+ (date (and (vectorp header) (mail-header-date header)))
+ (date-regexp "^Date: \\|^X-Sent: ")
+ (inhibit-point-motion-hooks t))
(when (and date (not (string= date "")))
(save-excursion
(set-buffer gnus-article-buffer)
(insert "\^_")))
(defun gnus-narrow-to-page (&optional arg)
- "Make text outside current page invisible except for page delimiter.
-A numeric arg specifies to move forward or backward by that many pages,
-thus showing a page other than the one point was originally in."
+ "Narrow the article buffer to a page.
+If given a numerical ARG, move forward ARG pages."
(interactive "P")
(setq arg (if arg (prefix-numeric-value arg) 0))
(save-excursion
- (forward-page -1) ;Beginning of current page.
+ (set-buffer gnus-article-buffer)
+ (goto-char (point-min))
(widen)
- (if (> arg 0)
- (forward-page arg)
- (if (< arg 0)
- (forward-page (1- arg))))
- ;; Find the end of the page.
- (forward-page)
- ;; If we stopped due to end of buffer, stay there.
- ;; If we stopped after a page delimiter, put end of restriction
- ;; at the beginning of that line.
- ;; These are commented out.
- ;; (if (save-excursion (beginning-of-line)
- ;; (looking-at page-delimiter))
- ;; (beginning-of-line))
- (narrow-to-region (point)
- (progn
- ;; Find the top of the page.
- (forward-page -1)
- ;; If we found beginning of buffer, stay there.
- ;; If extra text follows page delimiter on same line,
- ;; include it.
- ;; Otherwise, show text starting with following line.
- (if (and (eolp) (not (bobp)))
- (forward-line 1))
- (point)))))
-
-(defun gnus-gmt-to-local ()
- "Rewrite Date header described in GMT to local in current buffer.
-Intended to be used with gnus-article-prepare-hook."
- (save-excursion
- (save-restriction
- (widen)
- (goto-char (point-min))
- (narrow-to-region (point-min)
- (progn (search-forward "\n\n" nil 'move) (point)))
- (goto-char (point-min))
- (if (re-search-forward "^Date:[ \t]\\(.*\\)$" nil t)
- (let ((buffer-read-only nil)
- (date (buffer-substring-no-properties
- (match-beginning 1) (match-end 1))))
- (delete-region (match-beginning 1) (match-end 1))
- (insert
- (timezone-make-date-arpa-standard
- date nil (current-time-zone))))))))
+ (when
+ (cond ((< arg 0)
+ (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
+ ((> arg 0)
+ (re-search-forward page-delimiter nil 'move arg)))
+ (goto-char (match-end 0)))
+ (when (and (gnus-visual-p 'page-marker)
+ (not (bolp)))
+ (gnus-insert-prev-page-button))
+ (narrow-to-region
+ (point)
+ (if (re-search-forward page-delimiter nil 'move)
+ (prog1 (match-beginning 0)
+ (when (and (gnus-visual-p 'page-marker)
+ (not (bolp)))
+ (gnus-insert-next-page-button)))
+ (point)))))
;; Article mode commands
(gnus-narrow-to-page -1) ;Go to previous page.
(goto-char (point-max))
(recenter -1))
- (scroll-down lines)))
+ (condition-case ()
+ (scroll-down lines)
+ (error nil))))
(defun gnus-article-refer-article ()
"Read article specified by message-id around point."
If FETCH-OLD, retrieve all headers (or some subset thereof) in the group."
(let ((method (gnus-find-method-for-group group)))
(if (and gnus-use-cache (numberp (car articles)))
- (gnus-cache-retrieve-headers articles group)
+ (gnus-cache-retrieve-headers articles group fetch-old)
(funcall (gnus-get-function method 'retrieve-headers)
articles (gnus-group-real-name group) (nth 1 method)
fetch-old))))
(defun gnus-request-type (group &optional article)
"Return the type (`post' or `mail') of GROUP (and ARTICLE)."
(let ((method (gnus-find-method-for-group group)))
- (if (not (gnus-check-backend-function 'request-type method))
+ (if (not (gnus-check-backend-function 'request-type (car method)))
'unknown
(funcall (gnus-get-function method 'request-type)
(gnus-group-real-name group) article))))
;; Find the number of unread articles in each non-dead group.
(let ((gnus-read-active-file (and (not level) gnus-read-active-file)))
- (gnus-get-unread-articles (or level (1+ gnus-level-subscribed))))
+ (gnus-get-unread-articles level))
(if (and init gnus-check-bogus-newsgroups
gnus-read-active-file (not level)
;; and compute how many unread articles there are in each group.
(defun gnus-get-unread-articles (&optional level)
(let* ((newsrc (cdr gnus-newsrc-alist))
- (level (or level (1+ gnus-level-subscribed)))
+ (level (or level gnus-activate-level (1+ gnus-level-subscribed)))
(foreign-level
(min
(cond ((and gnus-activate-foreign-newsgroups
;;; Interface functions
(defun nndoc-retrieve-headers (articles &optional newsgroup server fetch-old)
- (nndoc-possibly-change-buffer newsgroup server)
- (save-excursion
- (set-buffer nntp-server-buffer)
- (erase-buffer)
- (let (article entry)
- (if (stringp (car articles))
- 'headers
- (while articles
- (setq entry (cdr (assq (setq article (pop articles))
- nndoc-dissection-alist)))
- (insert (format "221 %d Article retrieved.\n" article))
- (insert-buffer-substring
- nndoc-current-buffer (car entry) (nth 1 entry))
- (goto-char (point-max))
- (or (= (char-after (1- (point))) ?\n) (insert "\n"))
- (insert (format "Lines: %d\n" (nth 4 entry)))
- (insert ".\n"))
-
- ;; Fold continuation lines.
- (goto-char (point-min))
- (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
- (replace-match " " t t))
- 'headers))))
+ (when (nndoc-possibly-change-buffer newsgroup server)
+ (save-excursion
+ (set-buffer nntp-server-buffer)
+ (erase-buffer)
+ (let (article entry)
+ (if (stringp (car articles))
+ 'headers
+ (while articles
+ (setq entry (cdr (assq (setq article (pop articles))
+ nndoc-dissection-alist)))
+ (insert (format "221 %d Article retrieved.\n" article))
+ (insert-buffer-substring
+ nndoc-current-buffer (car entry) (nth 1 entry))
+ (goto-char (point-max))
+ (or (= (char-after (1- (point))) ?\n) (insert "\n"))
+ (insert (format "Lines: %d\n" (nth 4 entry)))
+ (insert ".\n"))
+
+ ;; Fold continuation lines.
+ (goto-char (point-min))
+ (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
+ (replace-match " " t t))
+ 'headers)))))
(defun nndoc-open-server (server &optional defs)
(nnheader-init-server-buffer)
(if (stringp nndoc-address)
(insert-file-contents nndoc-address)
(insert-buffer-substring nndoc-address)))))
- (save-excursion
- (set-buffer nndoc-current-buffer)
- (nndoc-set-delims)
- (nndoc-dissect-buffer))
- t))
-
+ (when nndoc-current-buffer
+ (save-excursion
+ (set-buffer nndoc-current-buffer)
+ (nndoc-set-delims)
+ (nndoc-dissect-buffer))
+ t)))
;; MIME (RFC 1341) digest hack by Ulrik Dickow <dickow@nbi.dk>.
(defun nndoc-guess-digest-type ()
(defun nndoc-rnews-body-end ()
(save-excursion
(and (re-search-backward nndoc-article-begin nil t)
- (goto-char (+ (point) (string-to-int (match-string 1)))))))
-
+ (goto-char (+ (point) (string-to-int (match-string 1)))))))
(provide 'nndoc)
(defun nnfolder-request-group (group &optional server dont-check)
(save-excursion
(nnmail-activate 'nnfolder)
- (nnfolder-possibly-change-group group)
- (and (assoc group nnfolder-group-alist)
- (progn
- (if dont-check
- t
- (let* ((active (assoc group nnfolder-group-alist))
- (group (car active))
- (range (car (cdr active)))
- (minactive (car range))
- (maxactive (cdr range)))
- ;; I've been getting stray 211 lines in my nnfolder active
- ;; file. So, let's make sure that doesn't happen. -SLB
- (set-buffer nntp-server-buffer)
- (erase-buffer)
- (if (not active)
- ()
- (insert (format "211 %d %d %d %s\n"
- (1+ (- maxactive minactive))
- minactive maxactive group))
- t)))))))
+ (when (assoc group nnfolder-group-alist)
+ (if dont-check
+ t
+ (let* ((active (assoc group nnfolder-group-alist))
+ (group (car active))
+ (range (car (cdr active)))
+ (minactive (car range))
+ (maxactive (cdr range)))
+ (set-buffer nntp-server-buffer)
+ (erase-buffer)
+ (when active
+ (insert (format "211 %d %d %d %s\n"
+ (1+ (- maxactive minactive))
+ minactive maxactive group))
+ t))))))
(defun nnfolder-request-scan (&optional group server)
(nnmail-get-new-mail
(buffer-disable-undo (current-buffer))
(erase-buffer))
+(defvar jka-compr-compression-info-list)
+(defvar nnheader-numerical-files
+ (if (boundp 'jka-compr-compression-info-list)
+ (concat "\\([0-9]+\\)\\("
+ (mapconcat (lambda (i) (aref i 0))
+ jka-compr-compression-info-list "\\|")
+ "\\)?")
+ "[0-9]+$")
+ "Regexp that match numerical files.")
+
+(defvar nnheader-numerical-short-files (concat "^" nnheader-numerical-files)
+ "Regexp that matches numerical file names.")
+
+(defvar nnheader-numerical-full-files (concat "/" nnheader-numerical-files)
+ "Regexp that matches numerical full file paths.")
+
+(defun nnheader-file-to-number (file)
+ (if (not (boundp 'jka-compr-compression-info-list))
+ (string-to-int file)
+ (string-match nnheader-numerical-short-files file)
+ (match-string 1 file)))
+
+(defun nnheader-directory-articles (dir)
+ (mapcar 'nnheader-file-to-number
+ (directory-files dir nil nnheader-numerical-short-files t)))
+
(provide 'nnheader)
;;; nnheader.el ends here
(defalias 'nnkiboze-request-post 'nntp-request-post)
+(defun nnkiboze-request-delete-group (group &optional force server)
+ (nnkiboze-possibly-change-newsgroups group)
+ (when force
+ (let ((files (list (nnkiboze-nov-file-name)
+ (concat nnkiboze-directory group ".newsrc")
+ (nnkiboze-score-file group))))
+ (while files
+ (and (file-exists-p (car files))
+ (file-writable-p (car files))
+ (delete-file (car files)))
+ (setq files (cdr files)))))
+ (setq nnkiboze-current-group nil))
+
\f
;;; Internal functions.
(defun nnmail-days-to-time (days)
"Convert DAYS into time."
- (let ((seconds (round (* days 60 60 24)))
+ (let ((seconds (* 1.0 days 60 60 24))
(rest (expt 2 16)))
- (list (/ seconds rest) (% seconds rest))))
+ (list (round (/ seconds rest))
+ ;;;!!!Error error error. I'm not a mathematician, though.
+ (condition-case ()
+ (% (round seconds) rest)
+ (error 0)))))
(defun nnmail-time-since (time)
"Return the time since DATE."
(file-truename nnmail-crash-box))) 0))
(list nnmail-crash-box))))
;; Remove any directories that inadvertantly match the procmail
- ;; suffix, which might happen if the suffix is "". We also
- ;; ditch symlinks.
+ ;; suffix, which might happen if the suffix is "".
(while p
- (and (or (file-directory-p (car p))
- (file-symlink-p (car p)))
- (setq procmails (delete (car p) procmails)))
+ (when (file-directory-p (car p))
+ (setq procmails (delete (car p) procmails)))
(setq p (cdr p)))
;; Return the list of spools.
(append
(defvar nnml-group-alist nil)
(defvar nnml-active-timestamp nil)
+(defvar nnml-generate-active-function 'nnml-generate-active-info)
+
\f
;; Server variables.
nnml-group-alist))
(nnml-possibly-create-directory group)
(nnml-possibly-change-directory group)
- (let ((articles (mapcar
- (lambda (file)
- (string-to-int file))
- (directory-files
- nnml-current-directory nil "^[0-9]+$"))))
+ (let ((articles
+ (nnheader-directory-articles nnml-current-directory )))
(and articles
(progn
(setcar active (apply 'min articles))
(defun nnml-request-expire-articles (articles newsgroup &optional server force)
(nnml-possibly-change-directory newsgroup)
(let* ((active-articles
- (mapcar
- (function
- (lambda (name)
- (string-to-int name)))
- (directory-files nnml-current-directory nil "^[0-9]+$" t)))
+ (nnheader-directory-articles nnml-current-directory))
(max-article (and active-articles (apply 'max active-articles)))
(is-old t)
article rest mod-time number)
(defun nnml-request-delete-group (group &optional force server)
(nnml-possibly-change-directory group)
- ;; Delete all articles in GROUP.
- (if (not force)
- () ; Don't delete the articles.
+ (when force
+ ;; Delete all articles in GROUP.
(let ((articles
(directory-files
nnml-current-directory t
- (concat "^[0-9]+$\\|" (regexp-quote nnml-nov-file-name) "$"))))
+ (concat nnheader-numerical-short-files
+ "\\|" (regexp-quote nnml-nov-file-name) "$")))
+ article)
(while articles
- (and (file-writable-p (car articles))
- (progn
- (and gnus-verbose-backends
- (message (message "Deleting article %s in %s..."
- (car articles) group)))
- (funcall nnmail-delete-file-function (car articles))))
- (setq articles (cdr articles))))
+ (setq article (pop articles))
+ (when (file-writable-p article)
+ (when gnus-verbose-backends
+ (message "Deleting article %s in %s..." article group))
+ (funcall nnmail-delete-file-function article))))
;; Try to delete the directory itself.
(condition-case ()
(delete-directory nnml-current-directory)
(setq nnml-nov-buffer-alist (cdr nnml-nov-buffer-alist)))))
;;;###autoload
-(defun nnml-generate-nov-databases (dir)
+(defun nnml-generate-nov-databases ()
"Generate nov databases in all nnml directories."
- (interactive
- (progn
- (setq nnml-group-alist nil)
- (list nnml-directory)))
+ (interactive)
+ ;; Read the active file to make sure we don't re-use articles
+ ;; numbers in empty groups.
+ (nnmail-activate 'nnml)
(nnml-open-server (or nnml-current-server ""))
- (let ((dirs (directory-files dir t nil t)))
+ (setq nnml-directory (expand-file-name nnml-directory))
+ ;; Recurse down the directories.
+ (nnml-generate-nov-databases-1 nnml-directory)
+ ;; Save the active file.
+ (nnmail-save-active nnml-group-alist nnml-active-file))
+
+(defun nnml-generate-nov-databases-1 (dir)
+ (setq dir (file-name-as-directory dir))
+ ;; We descend recursively
+ (let ((dirs (directory-files dir t nil t))
+ dir)
(while dirs
- (if (and (not (string-match "/\\.\\.$" (car dirs)))
- (not (string-match "/\\.$" (car dirs)))
- (file-directory-p (car dirs)))
- (nnml-generate-nov-databases (car dirs)))
- (setq dirs (cdr dirs))))
+ (setq dir (pop dirs))
+ (when (and (not (string-match "/\\.\\.?$" dir))
+ (file-directory-p dir))
+ (nnml-generate-nov-databases-1 dir))))
+ ;; Do this directory.
(let ((files (sort
(mapcar
- (function
- (lambda (name)
- (string-to-int name)))
+ (lambda (name) (string-to-int name))
(directory-files dir nil "^[0-9]+$" t))
- (function <)))
- (nov (concat dir "/" nnml-nov-file-name))
- (nov-buffer (get-buffer-create "*nov*"))
+ '<)))
+ (when files
+ (funcall nnml-generate-active-function dir)
+ ;; Generate the nov file.
+ (nnml-generate-nov-file dir files))))
+
+(defun nnml-generate-active-info (dir)
+ ;; Update the active info for this group.
+ (let ((group (nnmail-replace-chars-in-string
+ (substring dir (length nnml-directory))
+ ?/ ?.)))
+ (setq nnml-group-alist (delq (assoc group nnml-group-alist)))
+ (push (list group
+ (cons (car files)
+ (let ((f files))
+ (while (cdr f) (setq f (cdr f)))
+ (car f))))
+ nnml-group-alist)))
+
+(defun nnml-generate-nov-file (dir files)
+ (let ((nov (concat dir "/" nnml-nov-file-name))
+ (nov-buffer (get-buffer-create " *nov*"))
nov-line chars)
- (if files
- (setq nnml-group-alist
- (cons (list (nnmail-replace-chars-in-string
- (substring (expand-file-name dir)
- (length (expand-file-name
- nnml-directory)))
- ?/ ?.)
- (cons (car files)
- (let ((f files))
- (while (cdr f) (setq f (cdr f)))
- (car f))))
- nnml-group-alist)))
- (if files
- (save-excursion
- (set-buffer nntp-server-buffer)
- (if (file-exists-p nov)
- (funcall nnmail-delete-file-function nov))
- (save-excursion
- (set-buffer nov-buffer)
- (buffer-disable-undo (current-buffer))
- (erase-buffer))
- (while files
- (erase-buffer)
- (insert-file-contents (concat dir "/" (int-to-string (car files))))
- (narrow-to-region
- (goto-char (point-min))
- (save-excursion
- (search-forward "\n\n" nil t)
- (setq chars (- (point-max) (point)))
- (point)))
- (when (and (not (= 0 chars)) ; none of them empty files...
- (not (= (point-min) (point-max))))
- (setq nov-line (nnml-make-nov-line chars))
- (save-excursion
- (set-buffer nov-buffer)
- (goto-char (point-max))
- (insert (int-to-string (car files)) nov-line)))
- (widen)
- (setq files (cdr files)))
+ (save-excursion
+ ;; Init the nov buffer.
+ (set-buffer nov-buffer)
+ (buffer-disable-undo (current-buffer))
+ (erase-buffer)
+ (set-buffer nntp-server-buffer)
+ ;; Delete the old NOV file.
+ (when (file-exists-p nov)
+ (funcall nnmail-delete-file-function nov))
+ (while files
+ (erase-buffer)
+ (insert-file-contents (concat dir "/" (int-to-string (car files))))
+ (narrow-to-region
+ (goto-char (point-min))
+ (progn
+ (search-forward "\n\n" nil t)
+ (setq chars (- (point-max) (point)))
+ (1- (point))))
+ (when (and (not (= 0 chars)) ; none of them empty files...
+ (not (= (point-min) (point-max))))
+ (goto-char (point-min))
+ (setq nov-line (nnml-make-nov-line chars))
(save-excursion
(set-buffer nov-buffer)
- (write-region 1 (point-max) (expand-file-name nov) nil
- 'nomesg)
- (kill-buffer (current-buffer)))))
- (nnmail-save-active nnml-group-alist nnml-active-file)))
+ (goto-char (point-max))
+ (insert (int-to-string (car files)) nov-line)))
+ (widen)
+ (setq files (cdr files)))
+ (save-excursion
+ (set-buffer nov-buffer)
+ (write-region 1 (point-max) (expand-file-name nov) nil
+ 'nomesg)
+ (kill-buffer (current-buffer))))))
(defun nnml-nov-delete-article (group article)
(save-excursion
reading."
(nntp-send-command "^.*\r?\n" "MODE READER"))
+(defun nntp-send-nosy-authinfo ()
+ "Send the AUTHINFO to the nntp server.
+This function is supposed to be called from `nntp-server-opened-hook'.
+It will prompt for a password."
+ (nntp-send-command "^.*\r?\n" "AUTHINFO USER"
+ (read-string "NNTP user name: "))
+ (nntp-send-command "^.*\r?\n" "AUTHINFO PASS"
+ (read-string "NNTP password: ")))
+
(defun nntp-send-authinfo ()
"Send the AUTHINFO to the nntp server.
This function is supposed to be called from `nntp-server-opened-hook'.
+Wed Dec 13 16:13:56 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Selecting a Group): Addition.
+ (Saving Articles): Addition.
+ (Score File Format): Addition.
+
+Tue Dec 12 11:04:14 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Other Marks): Addition; change.
+ (Exiting the Summary Buffer): Addition.
+ (Hiding Headers): Addition.
+ (Score File Format): Addition.
+ (Article Caching): Addition.
+ (Misc Group Stuff): Addition.
+
+Mon Dec 11 09:52:34 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Persistent Articles): New.
+ (Other Marks): Addition.
+ (Topic Variables): Deletia.
+ (New Groups): Addition.
+ (Subscription Commands): Addition.
+ (Selecting a Group): Addition.
+ (Viewing Files): Addition.
+ (Group Levels): Addition.
+
+Sun Dec 10 13:51:16 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (The Active File): Change.
+
Sat Dec 9 16:55:29 1995 Lars Ingebrigtsen <lars@eyesore.no>
* gnus.texi (nndoc): Change.
clean:
rm -f gnus.*.bak gnus.ky gnus.cp gnus.aux gnus.fn gnus.dvi gnus.cps gnus.kys gnus.log gnus.pg gnus.tp gnus.vr gnus.toc
+
+makeinfo:
+ makeinfo -o gnus.info gnus.texi
You can click on buttons instead of using the keyboard
(@pxref{Buttons}).
+@item
+Gnus can use NoCeM files to weed out spam (@pxref{NoCeM}).
+
@end itemize
This is, of course, just a @emph{short} overview of the @emph{most}
@item
@code{nn}-like pick-and-read summary interface.
@item
-NoCeM support.
-@item
Frame configuration.
@item
-Floating point group levels and group bubbling.
-@item
Automatic re-scan of incoming mail.
@item
Buttonize more stuff in the article buffer.
A better and simpler method for specifying mail composing methods.
@item
Marks for saved, forwarded, etc articles.
-@item
-Speed up caching and adaptive scoring.
@item
Gather thread by filling in missing Message-IDs.
@item
@item
Allow posting through mail-to-news gateways.
@item
-Speed up massive group massacres.
-@item
@code{jka-compr} isn't fully supported.
@item
-Create better digests.
-@item
Do better word-wrap on cited text.
@item
Better X-Face support with X-Face databases and stuff.
@item
-Support SELF-DISCIPLINE pins.
-@item
Really do unbinhexing.
@item
-Listing of all active groups.
-@item
XEmacs toolbar.
@item
-Do the X-Receipt-To thing.
-@item
Don't kill summary buffers upon exit from the groups.
@item
Allow adaption on secondary marks.
up-to-the-second todo list is located, so if you're really curious, you
could point your Web browser over that-a-way.
+
@node Terminology
@chapter Terminology
Subscribe new groups interactively. This means that Gnus will ask
you about @strong{all} new groups.
+@item gnus-subscribe-killed
+@vindex gnus-subscribe-killed
+Kill all new groups.
+
@item gnus-subscribe-zombies
@vindex gnus-subscribe-zombies
Make all new groups zombies. You can browse the zombies later (with
Before examining the active file, Gnus deletes all lines that match the
regexp @code{gnus-ignored-newsgroups}. This is done primarily to reject
any groups with bogus names, but you can use this variable to make Gnus
-ignore hierarchies you aren't ever interested in. This variable is
-@code{nil} by default, and will slow down active file handling somewhat
-if you set it to anything else.
+ignore hierarchies you aren't ever interested in.
+@c This variable is
+@c @code{nil} by default, and will slow down active file handling somewhat
+@c if you set it to anything else.
@vindex gnus-read-active-file
The active file can be rather Huge, so if you have a slow network, you
@table @samp
@item S
-Default news server.
+The native news server.
@item M
-Default select method.
+The native select method.
@end table
@node Group Maneuvering
expunging. This might be useful if you're in a real hurry and have to
enter some humongous groups.
+@item M-SPACE
+@kindex M-RET (Group)
+@findex gnus-group-visible-select-group
+This is yet one more command that does the same as the one above, but
+this one does it without expunging and hiding dormants
+(@code{gnus-group-visible-select-group}).
+
@item c
@kindex c (Group)
@findex gnus-group-catchup-current
By default, sorting is done on article numbers. Ready-made sorting
functions include @code{gnus-thread-sort-by-number},
@code{gnus-thread-sort-by-author}, @code{gnus-thread-sort-by-subject},
-@code{gnus-thread-sort-by-date}, @code{gnus-thread-sort-by-score},
+@code{gnus-thread-sort-by-date}, @code{gnus-thread-sort-by-score}, and
@code{gnus-thread-sort-by-total-score}.
Each function takes two threads and return non-@code{nil} if the first
functions might be @code{max}, @code{min}, or squared means, or whatever
tickles you fancy.
+@findex gnus-article-sort-functions
+@findex gnus-article-sort-by-date
+@findex gnus-article-sort-by-score
+@findex gnus-article-sort-by-subject
+@findex gnus-article-sort-by-author
+@findex gnus-article-sort-by-number
+If you are using an unthreaded display for some strange reason or other,
+you have to fiddle with the @code{gnus-article-sort-functions}
+variable. It is very similar to the @code{gnus-thread-sort-functions},
+except that is uses slightly different functions for article
+comparison. Available functions are @code{gnus-article-sort-by-number},
+@code{gnus-article-sort-by-author}, @code{gnus-article-sort-by-subject},
+@code{gnus-article-sort-by-date}, and @code{gnus-article-sort-by-score}.
+
@node Subscription Commands
@section Subscription Commands
@kindex S z (Group)
@findex gnus-group-kill-all-zombies
Kill all zombie groups (@code{gnus-group-kill-all-zombies}).
+@item S C-k
+@kindex S C-k (Group)
+@findex gnus-group-kill-level
+Kill all groups on a certain level (@code{gnus-group-kill-level}).
+These groups can't be yanked back after killing, so this command should
+be used with some caution. The only thing where this command comes in
+really handy is when you have a @file{.newsrc} with lots of unsubscribed
+groups that you want to get rid off. @kbd{S C-k} on level @code{7} will
+kill off all unsubscribed groups that do not have message numbers in the
+@file{.newsrc} file.
+
@end table
Also @xref{Group Levels}.
give a level prefix to @kbd{g} or @kbd{l}, all subsequent commands will
use this level as the "work" level.
+@vindex gnus-activate-level
+Gnus will normally just activate groups that are on level
+@code{gnus-activate-level} or less. If you don't want to activate
+unsubscribed groups, for instance, you might set this variable to
+@samp{5}.
+
@node Group Score
@section Group Score
(@code{gnus-group-make-doc-group}). If you give a prefix to this
command, you will be prompted for a file name and a file type.
Currently supported types are @code{babyl}, @code{mbox}, @code{digest},
-@code{mmdf}, and @code{forward}. If you run this command without a
-prefix, Gnus will guess at the file type.
+@code{mmdf}, @code{news}, @code{rnews}, and @code{forward}. If you run
+this command without a prefix, Gnus will guess at the file type.
@item G DEL
@kindex G DEL (Group)
@cindex help group
@code{nndoc} is a cute little thing that will let you read a single file
-as a newsgroup. Currently supported file types are @code{babyl} (the
-RMAIL file type), @code{mbox} (standard Unix mbox files), @code{digest}
-(various digests, MIME and otherwise), @code{mmdf} (the MMDF mail box
-format), and @code{forward} (a single forwarded mail). You can also use
-the special "file type" @code{guess}, which means that @code{nndoc} will
-try to guess what file type it is looking at.
+as a newsgroup. Several files types are supported:
+
+@table @code
+@cindex babyl
+@cindex rmail mbox
+@item babyl
+The babyl (rmail) mail box.
+@cindex mbox
+@cindex Unix mbox
+@item mbox
+The standard Unix mbox file.
+@cindex MMDF mail box
+@item mmdf
+The MMDF mail box format.
+@item news
+Several news articles appended into a file.
+@item rnews
+@cindex rnews batch files
+The rnews batch transport format.
+@cindex forwarded messages
+@item forward
+Forwarded articles.
+@item mime-digest
+@cindex digest
+@cindex MIME digest
+@cindex 1153 digest
+@cindex RFC 1153 digest
+@cindex RFC 341 digest
+MIME (RFC 1341) digest format.
+@item standard-digest
+The standard (RFC 1153) digest format.
+@item slack-digest
+Non-standard digest format---matches most things, but does it badly.
+@end table
+
+You can also use the special "file type" @code{guess}, which means that
+@code{nndoc} will try to guess what file type it is looking at.
+@code{digest} means that @code{nndoc} should guess what digest type the
+file is.
@code{nndoc} will not try to change the file or insert any extra headers into
it---it will simply, like, let you use the file as the basis for a
want.
@item
-You do the @kbd{G S r} command to pack these replies into a @sc{soup}
+You do the @kbd{G s r} command to pack these replies into a @sc{soup}
packet.
@item
You transfer this packet to the server.
@item
-You use Gnus to mail this packet out with the @kbd{G S s} command.
+You use Gnus to mail this packet out with the @kbd{G s s} command.
@item
You then repeat until you die.
@node SOUP Replies
-@subsubsection SOUP Replies
+@subsubsection @sc{SOUP} Replies
Just using @code{nnsoup} won't mean that your postings and mailings end
up in @sc{soup} reply packets automagically. You have to work a bit
@item nnmh-be-safe
@vindex nnmh-be-safe
If non-@code{nil}, @code{nnmh} will go to ridiculous lengths to make
-sure that the articles in the folder is actually what Gnus think they
-are. It will check date stamps, and stat everything in sight, so
+sure that the articles in the folder are actually what Gnus thinks they
+are. It will check date stamps and stat everything in sight, so
setting this to @code{t} will mean a serious slow-down. If you never
-use anything by Gnus to read the @code{nnmh} articles, you do not have to set
-this variable to @code{t}.
+use anything but Gnus to read the @code{nnmh} articles, you do not have
+to set this variable to @code{t}.
@end table
@node nnfolder
@findex gnus-group-prev-group
Go to the previous group (@code{gnus-group-prev-group}).
-@item SPC
-@kindex SPC (Browse)
+@item SPACE
+@kindex SPACE (Browse)
@findex gnus-browse-read-group
Enter the current group and display the first article
(@code{gnus-browse-read-group}).
(add-hook 'gnus-group-mode-hook 'gnus-topic-mode)
@end lisp
-There are, in general, two methods for dividing the groups into topics.
-You can either sniff around some variables, or you can use some handy
-commands.
-
@menu
* Topic Variables:: How to customize the topics the Lisp Way.
* Topic Commands:: Interactive E-Z commands.
@subsection Topic Variables
@cindex topic variables
-@vindex gnus-group-topics
-
-@code{gnus-group-topics} is the main variable that specifies what topics
-each group belong to. That is an alist where each entry looks like
-this:
-
-@lisp
-(TOPIC REGEXP SHOW)
-@end lisp
-
-As you've already guessed (only geniï read manuals anyway), all
-groups that match @var{regexp} gets put into a section called
-@var{topic}. If @var{show} is non-@code{nil}, it overrides
-@code{gnus-group-topic-topics-only}. In specific, if @var{show} is
-@code{t}, all groups with this topic are always shown, and if it is a
-number, these groups are never shown.
@vindex gnus-group-topic-topics-only
Whoo, this is complicated. If @code{gnus-group-topic-topics-only} is
of (tops) one topic each. If this is @code{nil}, each group might end
up being a member of several topics.
-You can also put a @code{topic} in the group parameters (@pxref{Group
-Parameters}).
-
Now, if you select a topic, if will fold/unfold that topic, which is
really neat, I think.
-Here's an example @code{gnus-group-topics}:
-
-@lisp
-(("Emacs - the Spice of Life" "^gnu.emacs\\|comp.emacs" t)
- ("Alternativeness" "^alt" 0)
- ("Hard Stuff" "^comp" nil)
- ("The Rest" "." nil))
-@end lisp
-
@vindex gnus-topic-line-format
The topic lines themselves are created according to the
@code{gnus-topic-line-format} variable. @xref{Formatting Variables}.
Check whether new articles have arrived in the current group
(@code{gnus-group-get-new-news-this-group}).
+@findex gnus-activate-all-groups
+@item C-c M-g
+@kindex C-c M-g (Group)
+Activate absolutely all groups (@code{gnus-activate-all-groups}).
+
@item ^
@kindex ^ (Group)
@findex gnus-group-enter-server-mode
* Threading:: How threads are made.
* Asynchronous Fetching:: Gnus might be able to pre-fetch articles.
* Article Caching:: You may store articles in a cache.
+* Persistent Articles:: Making articles expiry-resistant.
* Article Backlog:: Having already read articles hang around.
* Exiting the Summary Buffer:: Returning to the Group buffer.
* Process/Prefix:: A convention used by many treatment commands.
There are some marks that have nothing to do with whether the article is
read or not.
+@itemize @bullet
+
+@item
You can set a bookmark in the current article. Say you are reading a
-long thesis on cat's urinary tracts, and have to go home for dinner
+long thesis on cats' urinary tracts, and have to go home for dinner
before you've finished reading the thesis. You can then set a bookmark
in the article, and Gnus will jump to this bookmark the next time it
encounters the article.
+@item
All articles that you have replied to or made a followup to (i.e., have
answered) will be marked with an @samp{A} in the second column
(@code{gnus-replied-mark}).
+@item
+Articles that are stored in the article cache will be marked with an
+@samp{*} in the second column (@code{gnus-cached-mark}).
+
+@item
+Articles that are "saved" (in some manner or other; not necessarily
+religously) are marked with an @samp{S} in the second column
+(@code{gnus-saved-mark}.
+
+@item
@vindex gnus-not-empty-thread-mark
@vindex gnus-empty-thread-mark
It the @samp{%e} spec is used, the presence of threads or not will be
marked with @code{gnus-not-empty-thread-mark} and
-@code{gnus-empty-thread-mark}, respectively.
+@code{gnus-empty-thread-mark} in the third column, respectively.
+@item
@vindex gnus-process-mark
Finally we have the @dfn{process mark} (@code{gnus-process-mark}. A
variety of commands react to the presence of the process mark. For
all articles that have been marked with the process mark. Articles
marked with the process mark have a @samp{#} in the second column.
+@end itemize
+
+You might have noticed that most of these "non-readedness" marks appear
+in the second column by default. So if you have a cached, saved,
+replied article that you have process-marked, what will that look like?
+
+Nothing much. The presedence rules go as follows: process -> cache ->
+replied -> saved. So if the article is in the cache and is replied,
+you'll only see the cache mark and not the replied mark.
+
+
@node Setting Marks
@subsection Setting Marks
@cindex setting marks
default, since caching doesn't really work in @code{nnvirtual} groups,
since @code{nnvirtual} assigns random article numbers to its articles.
+@findex gnus-cache-generate-nov-databases
+@findex gnus-cache-generate-active
+If your cache becomes all messed up for some reason or other, Gnus
+offers two functions that will try to set things right. @kbd{M-x
+gnus-cache-generate-nov-databases} will (re)build all the @sc{nov}
+files, and @kbd{gnus-cache-generate-active} will (re)generate the active
+file.
+
+
+@node Persistent Articles
+@section Persistent Articles
+@cindex persistent articles
+
+Closely related to article caching, we have @dfn{persistent articles}.
+In fact, it's just a different way of looking at caching, and much more
+useful in my opinion.
+
+Say you're reading a newsgroup, and you happen on to some valuable gem
+that you want to keep and treasure forever. You'd normally just save it
+(using one of the many saving commands) in some file. The problem with
+that is that it's just, well, yucky. Ideally you'd prefer just having
+the article remain in the group where you found it forever; untouched by
+the expiry going on at the news server.
+
+This is what a @dfn{persistent article} is---an article that just won't
+be deleted. It's implemented using the normal cache functions, but
+you use two explicit commands for managing persistent articles:
+
+@table @kbd
+@item *
+@kindex * (Summary)
+@findex gnus-cache-enter-article
+Make the current article persistent (@code{gnus-cache-enter-article}).
+
+@item M-*
+@kindex M-* (Summary)
+@findex gnus-cache-remove-article
+Remove the current article from the persistent articles
+(@code{gnus-cache-remove-article}). This will normally delete the
+article.
+@end table
+
+Both these commands understand the process/prefix convention.
+
+To avoid having all ticked articles (and stuff) entered into the cache,
+you should set @code{gnus-use-cache} to @code{passive} if you're just
+interested in persistent articles:
+
+@lisp
+(setq gnus-use-cache 'passive)
+@end lisp
+
@node Article Backlog
@section Article Backlog
@code{gnus-exit-group-hook} is called when you exit the current
group.
+@findex gnus-summary-wake-up-the-dead
+@findex gnus-dead-summary-mode
+@vindex gnus-kill-summary-on-exit
+If you're in the habit of exiting groups, and then changing your mind
+about it, you might set @code{gnus-kill-summary-on-exit} to @code{nil}.
+If you do that, Gnus won't kill the summary buffer when you exit it.
+(Quelle surprise!) Instead it will change the name of the buffer to
+something like @samp{"*Dead Summary ... *"} and install a minor mode
+called @code{gnus-dead-summary-mode}. Now, if you switch back to this
+buffer, you'll find that all keys are mapped to a function called
+@code{gnus-summary-wake-up-the-dead}. So tapping any keys in a dead
+summary buffer will result in a live, normal summary buffer.
+
+There will never be more than one dead summary buffer at any one time.
+
@vindex gnus-use-cross-reference
The data on the current group will be updated (which articles you have
read, which articles you have replied to, etc.) when you exit the
head); it can be a symbol (which will be called as a function); or it
can be a list (which will be @code{eval}ed). If any of these actions
have a non-@code{nil} result, the @dfn{file} will be used as a default
-prompt.
+prompt. In addition, the result of the operation itself will be used if
+the function or form called returns a string or a list of strings.
+
+You basically end up with a list of file names that might be used when
+saving the current article. (All "matches" will be used.) You will
+then be prompted for what you really want to use as a name, with file
+name completion over the results from applying this variable.
+
+This variable is @code{((gnus-article-archive-name))} by default, which
+means that Gnus will look at the articles it saves for an
+@samp{Archive-name} line and use that as a suggestion for the file
+name.
@vindex gnus-use-long-file-name
Finally, you have the @code{gnus-use-long-file-name} variable. If it is
@code{nil}, all files that use the same viewing command will be given as
a list of parameters to that command.
+If @code{gnus-insert-pseudo-articles} is non-@code{nil}, insert
+pseudo-articles when decoding. It is @code{t} by default.
+
So; there you are, reading your @emph{pseudo-articles} in your
@emph{virtual newsgroup} from the @emph{virtual server}; and you think:
Why isn't anything real anymore? How did we get here?
@item A D
@kindex A D (Summary)
@findex gnus-summary-enter-digest-group
-If the current article is a digest, you might use this command to enter
-a group based on the current digest
-(@code{gnus-summary-enter-digest-group}). Gnus will try to guess what
-article type is currently displayed unless you give a prefix to this
-command, which forces a "digest" interpretation.
+If the current article is a collection of other articles (for instance,
+a digest), you might use this command to enter a group based on the that
+article (@code{gnus-summary-enter-digest-group}). Gnus will try to
+guess what article type is currently displayed unless you give a prefix
+to this command, which forces a "digest" interpretation. Basically,
+whenever you see a message that is a collection of other messages on
+some format, you @kbd{A D} and read these messages in a more convenient
+fashion.
@item C-t
@kindex C-t (Summary)
(setq gnus-visible-headers "^From:\\|^Subject:")
@end lisp
+This variable can also be a list of regexps to match headers that are to
+remain visible.
+
@item gnus-ignored-headers
@vindex gnus-ignored-headers
This variable is the reverse of @code{gnus-visible-headers}. If this
(setq gnus-ignored-headers "^References:\\|^Xref:")
@end lisp
+This variable can also be a list of regexps to match headers that are to
+be removed.
+
Note that if @code{gnus-visible-headers} is non-@code{nil}, this
variable will have no effect.
+
@end table
@vindex gnus-sorted-header-list
A few additional keystrokes are available:
@table @kbd
+
@item SPACE
@kindex SPACE (Article)
@findex gnus-article-next-page
Scroll forwards one page (@code{gnus-article-next-page}).
+
@item DEL
@kindex DEL (Article)
@findex gnus-article-prev-page
Scroll backwards one page (@code{gnus-article-prev-page}).
+
@item C-c ^
@kindex C-c ^ (Article)
@findex gnus-article-refer-article
If point is in the neighborhood of a @code{Message-ID} and you press
@kbd{r}, Gnus will try to get that article from the server
(@code{gnus-article-refer-article}).
+
@item C-c C-m
@kindex C-c C-m (Article)
@findex gnus-article-mail
Send a reply to the address near point (@code{gnus-article-mail}). If
given a prefix, include the mail.
+
@item s
@kindex s (Article)
@findex gnus-article-show-summary
Reconfigure the buffers so that the summary buffer becomes visible
(@code{gnus-article-show-summary}).
+
@item ?
@kindex ? (Article)
@findex gnus-article-describe-briefly
Give a very brief description of the available keystrokes
(@code{gnus-article-describe-briefly}).
+
+@item TAB
+@kindex TAB (Article)
+@findex gnus-article-next-button
+Go to the next button, if any (@code{gnus-article-next-button}. This
+only makes sense if you have buttonizing turned on.
+
+@item M-TAB
+@kindex M-TAB (Article)
+@findex gnus-article-prev-button
+Go to the previous button, if any (@code{gnus-article-prev-button}.
+
@end table
+
@node Misc Article
@section Misc Article
@item mark
The value of this entry should be a number. Any articles with a score
lower than this number will be marked as read.
+
@item expunge
The value of this entry should be a number. Any articles with a score
lower than this number will be removed from the summary buffer.
+
@item mark-and-expunge
The value of this entry should be a number. Any articles with a score
lower than this number will be marked as read and removed from the
summary buffer.
+
+@item thread-mark-and-expunge
+The value of this entry should be a number. All articles that belong to
+a thread that has a total score below this number will be marked as read
+and removed from the summary buffer. @code{gnus-thread-score-function}
+says how to compute the total score for a thread.
+
@item files
The value of this entry should be any number of file names. These files
are assumed to be score files as well, and will be loaded the same way
this one was.
+
@item exclude-files
The clue of this entry should be any number of files. This files will
not be loaded, even though they would normally be so, for some reason or
other.
+
@item eval
The value of this entry will be @code{eval}el. This element will be
ignored when handling global score files.
+
@item read-only
Read-only score files will not be updated or saved. Global score files
should feature this atom (@pxref{Global Score Files}).
+
@item orphan
The value of this entry should be a number. Articles that do not have
parents will get this number added to their scores.
+
@item adapt
This entry controls the adaptive scoring. If it is @code{t}, the
default adaptive scoring rules will be used. If it is @code{ignore}, no
groups, you'd set @code{gnus-use-adaptive-scoring} to @code{nil}, and
insert @code{(adapt t)} in the score files of the groups where you want
it.
+
+@item adapt-file
+All adaptive score entries will go to the file named by this entry. It
+will also be applied when entering the group. This atom might be handy
+if you want to adapt on several groups at once, using the same adaptive
+file for a number of groups.
+
@item local
@cindex local variables
The value of this entry should be a list of @code{(VAR VALUE)} pairs.
variable says how long the other elements on the line is (i.e., the
non-info part). If you put additional elements on the mode line (eg. a
clock), you should modify this variable:
+
@c Hook written by Keinonen Kari <kk85613@cs.tut.fi>.
@lisp
(add-hook 'display-time-hook
(+ 21 (length display-time-string)))))
@end lisp
+If this variable is @code{nil} (which is the default), the mode line
+strings won't be chopped off, and they won't be padded either.
+
@item gnus-visual
@vindex gnus-visual
@cindex visual