[comp.emacs] Updated vn.el

karl@tut.UUCP (12/07/87)

As I mentioned last week, I've been doing some things with the vn.el
newsreader which was posted to comp.emacs a few weeks back.  It had a
number of bugs which made it almost impossible for me to use, but I've
taken care of all the outright bugs I could find.  In the process, I
reformatted it, because its existing formatting (indentation) looked
pretty pathological to me as a relative novice to Emacs lisp hacking.
Anyhow, this is the complete new package, not diffs.  The newslist.c
that was in the author's previous posting will do fine; I didn't
notice any bugs in it.

One small request to people creating new modes for GNU Emacs: please
don't do anything particularly abnormal with keymaps when you define a
new mode.  Vn was using C-h as a backward-page character of some sort;
it really threw me off when I wanted to use "help" commands.  I've
changed that particular item to use, I believe, DEL.

There are a few things that could afford to be done better, like
getting back to the correct window following a reply or followup, and
perhaps setting vn-header-on back to nil if it had previously been nil
but had to be set to t for the purpose of following up.  I've just
made it workable for myself, not perfect.

;;; USENET news reader for gnu emacs
;; Copyright (C) 1987 Free Software Foundation, Inc.

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;; written by Steve Murphy, Midvale, Utah, Oct. 1987
;; Dedicated to Ryan (He and this code share Birthdays)

;; I would suggest that those dissatified with the poor coding,
;; hacked up kloojes, etc. dedicate their energies to fixing it rather
;; than complaining!

;; This is an obvious imitation of vn, written in C by Bob McQueer
;; This 'plagiarism' is actually meant to be a tribute, as vn was/is by far
;; my news reader of choice.

;; I left out some of the functionality, like printing, 'selected' functions,
;; and such-like, because of duplicate functionality in gnu emacs, or
;; just laziness or lack of time. Note the lack of 'digestify'.
;; Also note that sending mail to the author of the current article
;; works, but... when you return things are awry. I believe this to be
;; the fault of rnewspost.el and mail.el. Perhaps I'll post something
;; later on this...

;; the c-code for 'newslist' is part of this package-- I didn't feel expert
;; enough to generate the code in lisp. It was easier for me to come out
;; with a small program that would generate the pages that vn does, showing
;; the article numbers, groups, subject lines, lines, and author. All the
;; data vn-mode needs to update the .newsrc is in the text of the vn buffer.
;; speed is comparable to vn's in generating this list.

;; The concept of 'pages' in vn was lost in the translation--purposely
;; on my part, as I personally like just a long list, and to have as much
;; visible on a page as possible. For those enthusiasts who like it the
;; other way, there are 'narrow' functions that could be put to use, but
;; I didn't feel it was worth it.

;; Two different modes are used, one for paging thru the headers, the other
;; for actually reading the article. The package cleans up after itself, 
;; leaving the windows the way they were before the package was invoked.

;; dired and rnews were freely copied here, and several 'autoloads' are
;; included from these sources. Why TOTALLY re-invent the wheel?

;; why did I do it? 1. Someone else indicated a wish for something like this,
;; 2. I wanted it myself, 3. The Challenge of It (why climb Mt. Everest?)
;; and lastly, 4. It was educational.

;; Enjoy. Ps. If the Gnu people want this, they are welcome to it...
;; if they don't.. no sweat, I don't blame them.
;; I'm giving it to them nevertheless, and thus to all who happen on it.

(provide 'vn)
(require 'mail-utils)

(autoload 'rmail-output "rmailout" 
"Append this message to Unix mail file named FILE-NAME" t)

(autoload 'news-convert-format "rnews" "get rid of the headers" t)
(autoload 'string-subst-char "rnews" "with args new-char old-char string,
replace old-char with new-char in string" t)
(autoload 'news-mail-other-window "rnewspost"
 "Send mail in another window. While composing the message, use
\\[mail-yank-original] to yank the original message into it."
 t)
(autoload 'news-caesar-buffer-body "rnews" "rotate the article" t)

(autoload 'news-mail-reply "rnewspost"
 "Mail a reply to the author of the current article. While composing
 the reply, use \\[mail-yank-original] to yank the original message into it."
 t)

(defvar news-startup-file "$HOME/.newsrc" "Contains ~/.newsrc")
(defvar newsbox-dir "$HOME/.newsbox" "The newsbox directory")
(defvar vn-buffer nil "which buffer vn is running in")
(defvar vn-read-buffer nil "which buffer the article is read in")
(defvar vn-initial-window-config nil "how it was before we mucked..")

(defun vn-find-buffer ()
  (let ((blist (buffer-list))
	found)
    (while blist
      (save-excursion
        (set-buffer (car blist))
	(if (eq major-mode 'vn-mode)
	    (setq found (car blist)
		  blist nil)
	  (setq blist (cdr blist)))))
    (or found
	(create-file-buffer "*VN-Buffer*"))))

(defun vn-read-find-buffer ()
  (let ((blist (buffer-list))
	found)
    (while blist
      (save-excursion
        (set-buffer (car blist))
	(if (eq major-mode 'vn-read-mode)
	    (setq found (car blist)
		  blist nil)
	  (setq blist (cdr blist)))))
    (or found
	(create-file-buffer "*Usenet Article*"))))

(defun vn-readin (buffer)
  (save-excursion
    (set-buffer buffer)
    (let ((buffer-read-only nil))
      (widen)
      (erase-buffer)
      (message "Checking for unread news...")
      (call-process "newslist" nil buffer nil)
      (message "Checking for unread news...done.")
      (if (not (zerop (buffer-size)))
	  (goto-char (point-min))))))

(defvar vn-window nil "the window vn is running in")

(defun vn ()
  "A simulation of vn for emacs.
Dired displays a list of unread articles.
You can move around in it with the usual commands.
Type `h' after entering vn for more info."
  (interactive)
  (setq vn-initial-window-config (current-window-configuration))
  (switch-to-buffer (vn-noselect))
  (setq vn-buffer (current-buffer))
  (vn-del-non-groups)
  (if (zerop (buffer-size))
      (progn
	(kill-buffer (current-buffer))
	(set-window-configuration vn-initial-window-config)
	(message "No News! (Good News?)"))
    (setq vn-window (selected-window))
    (cd (substitute-in-file-name newsbox-dir))))

(defun vn-other-window ()
  "VN simulation for emacs.
Like M-x vn but selects in another window."
  (interactive)
  (switch-to-buffer-other-window 
    (vn-noselect))
  (setq vn-window (selected-window))
  (setq vn-buffer (current-buffer)))

(defun vn-noselect ()
  "Like M-x vn but returns the vn buffer 
as value, does not select it."
  (let ((buffer (vn-find-buffer)))
    (save-excursion
      (set-buffer buffer)
      (vn-readin buffer)
      (vn-move-to-article)
      (vn-mode))
    buffer))

(defun vn-read-other-window ()
  "VN simulation for emacs.
Like M-x vn but selects in another window."
  (interactive)
  (switch-to-buffer-other-window 
    (vn-read-noselect))
  (setq vn-read-buffer (current-buffer))
  (cd (substitute-in-file-name newsbox-dir)))

(defun vn-read-noselect ()
  "Like M-x vn but returns the vn read buffer 
as value, does not select it."
  (let ((buffer (vn-read-find-buffer)))
    (save-excursion
      (set-buffer buffer)
      (vn-read-mode))
    buffer))

(defvar vn-mode-map nil 
  "Local keymap for vn-mode buffers.")
(defvar vn-read-mode-map nil 
  "Local keymap for vn-mode buffers.")

(if vn-mode-map
    nil
  (setq vn-mode-map (make-keymap))
  (suppress-keymap vn-mode-map)
  (define-key vn-mode-map " " 'vn-read-one-article)
  (define-key vn-mode-map "r" 'vn-read-one-article)
  (define-key vn-mode-map "R" 'vn-read-all-articles)
  (define-key vn-mode-map "q" 'vn-quit)
  (define-key vn-mode-map "p" 'vn-up-article)
  (define-key vn-mode-map "n" 'vn-down-article)
  (define-key vn-mode-map "k" 'vn-up-article)
  (define-key vn-mode-map "j" 'vn-down-article)
  (define-key vn-mode-map "\ep" 'vn-prev-group)
  (define-key vn-mode-map "\en" 'vn-next-group)
  (define-key vn-mode-map "\C-m" 'vn-next-page)
  (define-key vn-mode-map "\177" 'vn-prev-page)
  (define-key vn-mode-map "s" 'vn-save-article)
  (define-key vn-mode-map "S" 'vn-save-all-articles)
  (define-key vn-mode-map "w" 'vn-update-rc-to-cursor)
  (define-key vn-mode-map "W" 'vn-update-rc-to-eogroup)
  (define-key vn-mode-map "\C-w" 'vn-update-rc-all)
  (define-key vn-mode-map "u" 'vn-unsubscribe-group)
  (define-key vn-mode-map "h" 'vn-toggle-header-display))

(if vn-read-mode-map
    nil
  (setq vn-read-mode-map (make-keymap))
  (suppress-keymap vn-read-mode-map)
  (define-key vn-read-mode-map "n" 'vn-next-article)
  (define-key vn-read-mode-map "p" 'vn-prev-article)
  (define-key vn-read-mode-map "q" 'vn-abort-reading)
  (define-key vn-read-mode-map "Q" 'vn-abort-reading-next-group)
  (define-key vn-read-mode-map "." 'vn-beginning-of-article)
  (define-key vn-read-mode-map "e" 'vn-end-of-article)
  (define-key vn-read-mode-map "\C-m" 'vn-scroll-line)
  (define-key vn-read-mode-map "m" 'news-mail-other-window)
  (define-key vn-read-mode-map "r" 'vn-news-mail-reply)
  (define-key vn-read-mode-map "f" 'vn-news-reply)
  (define-key vn-read-mode-map "s" 'vn-save-this-article)
  (define-key vn-read-mode-map " " 'vn-next-page)
  (define-key vn-read-mode-map "\177" 'vn-prev-page)
  (define-key vn-read-mode-map "z" 'vn-toggle-rot)
  (define-key vn-read-mode-map "h" 'vn-toggle-header))

;; vn mode is suitable only for specially formatted data.
(put 'vn-mode 'mode-class 'special)
(put 'vn-read-mode 'mode-class 'special)

(defun vn-mode ()
  "Mode for reading Usenet News in a vn sort of way.
\\{vn-mode-map}"
  (kill-all-local-variables)    
  (setq major-mode 'vn-mode)
  (setq mode-name "Vn")
  (setq mode-line-buffer-identification '("Emacs: %17b"))
  (setq case-fold-search nil)
  (setq buffer-read-only t)
  (use-local-map vn-mode-map)
  (run-hooks 'vn-mode-hook))

(defun vn-read-mode ()
  "Mode for reading Usenet News articles in a vn sort of way.
\\{vn-read-mode-map}"
  (kill-all-local-variables)    
  (setq major-mode 'vn-read-mode)
  (setq mode-name "Vn-Read")
  (setq mode-line-buffer-identification '("VN-Read: %17b"))
  (setq case-fold-search nil)
  (use-local-map vn-read-mode-map)
  (run-hooks 'vn-read-mode-hook))

(defun vn-move-to-article ()
  "Move cursor to nearest following article"
  (if (not (zerop (buffer-size)))
      (progn
	(beginning-of-line)
	(while (and (not (looking-at "  [0-9]"))
		    (not (eobp)))
	  (forward-line 1)))))

(defun vn-current-group ()
  "return the current group of this article in path format"
  (save-excursion
    (let (dirname)
      (setq dirname (vn-current-group-dot))
      (string-subst-char ?/ ?. dirname))))

(defun vn-current-group-dot () 
  "return the current group of this article in dotted notation"
  (save-excursion
    (progn
      (set-buffer vn-buffer)
      (search-backward "==== Group: ")
      (skip-chars-forward "^:")
      (forward-char 2)
      (let (dirname)
	(setq dirname (buffer-substring (point)
					(progn
					  (skip-chars-forward "^ ")
					  (point))))
	dirname))))

(defun vn-current-article ()
  "get number of current article"
  (save-excursion
    (set-buffer vn-buffer)
    (skip-chars-forward "> ")
    (let ((beg (point)))
      (skip-chars-forward "0123456789")
      (buffer-substring beg (point)))))

(defun vn-current-article-file ()
  "return full path of article file"
  (concat "/usr/spool/news/" (vn-current-group) "/" (vn-current-article)))

(defun vn-up-article ()
  "move cursor to previous article line"
  (interactive)
  (re-search-backward "^  [0-9]" nil t)
  (beginning-of-line)
)

(defun vn-down-article ()
  "move cursor to next article line (skip group headers)"
  (interactive)
  (forward-line)
  (beginning-of-line)
  (re-search-forward "^  [0-9]" nil t)
  (beginning-of-line)
)

(defun vn-news-mail-reply ()
  "write mail to an article's originator"
  (interactive)
  (if (not vn-header-on)
      (vn-toggle-header))
  (news-mail-reply)
)

(defun vn-news-reply ()
  "write a followup article"
  (interactive)
  (if (not vn-header-on)
      (vn-toggle-header))
  (news-reply)
)

(defun vn-quit (arg)
  "end vn session and update .newsrc file"
  (interactive 
    "nUpdating .newsrc: 0=do not, 1=all, 2=to cursor, 3=to cursor group: ")
  (let ((newsrcbuf (find-file-noselect
		     (substitute-in-file-name news-startup-file))))
    (if (equal arg 1) (update-all-groups newsrcbuf))
    (if (equal arg 2) (update-all-groups-to-cursor newsrcbuf))
    (if (equal arg 3) (update-all-groups-to-eogroup newsrcbuf))
    (set-buffer newsrcbuf)
    (save-buffer)
    (kill-buffer (current-buffer))
    (kill-buffer vn-buffer)
    (if vn-read-buffer
	(kill-buffer vn-read-buffer))
    (set-window-configuration vn-initial-window-config)
    ))

(defun update-all-groups (newsrcbuf)
  "update all groups"
  (vn-add-missing-groups newsrcbuf)
  (goto-char (point-max))
  (forward-line -1)
  (vn-update-current-group-to-cursor newsrcbuf)
  (re-search-backward "^.... Group:")
  (forward-line -1)
  (while (not (bobp))
    (vn-update-current-group-to-cursor newsrcbuf)
    (re-search-backward "^.... Group:")
    (forward-line -1))
  (vn-unsubscribe-groups newsrcbuf))

(defun update-all-groups-to-cursor (newsrcbuf)
  "update all groups"
  (vn-add-missing-groups newsrcbuf)
  (vn-update-current-group-to-cursor newsrcbuf)
  (re-search-backward "^.... Group:")
  (forward-line -1)
  (while (not (bobp))
    (vn-update-current-group-to-cursor newsrcbuf)
    (re-search-backward "^.... Group:")
    (forward-line -1))
  (vn-unsubscribe-groups newsrcbuf))
	
(defun update-all-groups-to-eogroup (newsrcbuf)
  "update all groups"
  (vn-add-missing-groups newsrcbuf)
  (beginning-of-line)
  (if (looking-at "^.... Group:")
      (forward-line 1))
  (if (not (re-search-forward "^.... Group" nil t))
      (progn
	(goto-char (point-max))
	(forward-line -1))
    (forward-line -1))
  (vn-update-current-group-to-cursor newsrcbuf)
  (re-search-backward "^.... Group:" nil t)
  (forward-line -1)
  (while (not (bobp))
    (vn-update-current-group-to-cursor newsrcbuf)
    (re-search-backward "^.... Group:" nil t)
    (forward-line -1))
  (vn-unsubscribe-groups newsrcbuf))
	
(defun vn-del-non-groups ()
  "delete groups that aren't in active"
  (save-excursion
    (if (not (zerop (buffer-size)))
	(let ((newsrcbuf (find-file-noselect
			   (substitute-in-file-name news-startup-file)))
	      last-art last-group)
	  (goto-char 0)
	  (while (re-search-forward "---- Group:" nil t)
	    (forward-char 1)
	    (setq last-group (buffer-substring (point)
					       (progn
						 (skip-chars-forward "^ ")
						 (point))))
	    (set-buffer newsrcbuf)
	    (if (re-search-forward (concat last-group ":") nil t)
		(progn
		  (beginning-of-line)
		  (kill-line 1)
		  (save-buffer)
		  (kill-buffer newsrcbuf)))
	    (set-buffer vn-buffer)
	    (beginning-of-line)
	    (kill-line 1))))))
  
(defun vn-add-missing-groups (newsrcbuf) "plug in the missing groups"
  (let (last-art last-group)
    (save-excursion
      (goto-char 0)
      (while (re-search-forward "++++ Group:" nil t)
	(forward-char 1)
	(setq last-group (buffer-substring (point)
					   (progn
					     (skip-chars-forward "^ ")
					     (point))))
	(set-buffer newsrcbuf)
	(goto-char (point-max))
	(insert last-group)
	(insert ": 0\n")
	(set-buffer vn-buffer)
	(toggle-read-only)
	(beginning-of-line)
	(kill-line 1)
	(toggle-read-only)))))

(defun vn-unsubscribe-groups (newsrcbuf)
  "mark groups unsubscribed"
  (let (last-art last-group)
    (save-excursion
      (goto-char 0)
      (while (re-search-forward "---- Group:" nil t)
	(forward-char 1)
	(setq last-group (buffer-substring (point)
					   (progn
					     (skip-chars-forward "^ ")
					     (point))))
	(set-buffer newsrcbuf)
	(if (re-search-forward (concat last-group ":") nil t)
	    (progn
	      (delete-char -1)
	      (insert "!")))
	(set-buffer vn-buffer)))))

(defun vn-update-current-group-to-cursor (newsrcbuf)
  "update the .newsrc file"
  (let (last-art last-group)
    (save-excursion
      (set-buffer vn-buffer)
      (if (not (bolp))
	  (beginning-of-line))
      (if (not (bobp))
	  (progn
	    (while (and (looking-at ".... Group:")
			(not (bobp)))
	      (forward-line -1))
	    (if (not (bobp))
		(progn
		  (setq last-art (vn-current-article))
		  (setq last-group (vn-current-group-dot))
		  (message "updating %s-%s" last-group last-art )
		  (set-buffer newsrcbuf)
		  (goto-char 0)
		  (if (re-search-forward (concat "^" last-group "[:!]") nil t)
		      (progn
			(forward-char 1)
			(if (looking-at "0")
			    (progn
			      (delete-char 1)
			      (insert "1-0")
			      (forward-char -3)))
			(skip-chars-forward "^-")
			(forward-char 1)
			(delete-region (point) (progn (end-of-line) (point)))
			(insert last-art))
		    (message "Couldn't find %s" (concat last-group))))))))))

(defun vn-prev-group ()
  "jump to first article line in previous group"
  (interactive)
  (search-backward "==== Group: ")
  (search-backward "==== Group: ")
  (vn-move-to-article))

(defun vn-next-group ()
  "jump to first article line in next group"
  (interactive)
  (search-forward "==== Group: ")
  (vn-move-to-article))

(defun vn-read-article ()
  "open up another buffer to view article in"
  (interactive)
  (beginning-of-line)
  (toggle-read-only)
  (delete-char 1)
  (insert ">")
  (toggle-read-only)
  (beginning-of-line)
  (let ((filename (vn-current-article-file)))
    (vn-read-other-window)
    (erase-buffer)
    (let ((start (point)))
      (insert-file-contents filename)
      (if (not vn-header-on)
	  (news-convert-format))
      (goto-char start)
      (if vn-header-on
	  (forward-line 1)))))

(defvar vn-read-sequence nil "indicates we are in read-all mode")

(defun vn-read-one-article ()
  "open up another buffer to view article in"
  (interactive)
  (setq vn-read-sequence nil)
  (vn-read-article))

(defun vn-read-all-articles ()
  "open up another buffer and view all the articles
serially in it from current article to end of group"
  (interactive)
  (setq vn-read-sequence t)
  (vn-read-article))

(defun vn-save-article ()
  "save the current article to a disk file"
  (interactive)
  (beginning-of-line)
  (toggle-read-only)
  (delete-char 1)
  (insert ">")
  (toggle-read-only)
  (beginning-of-line)
  (call-interactively 'vn-save-article-2 nil)
  (beginning-of-line)
  (toggle-read-only)
  (delete-char 1)
  (insert " ")
  (toggle-read-only)
  (beginning-of-line))

(defun vn-save-article-2 (file)
  "save the current article to a disk file"
  (interactive "FCopy to File Name:")
  (copy-file (concat "/usr/spool/news/" (vn-current-group) 
		     "/" (vn-current-article)) file 1 nil))

(defun vn-save-all-articles (file)
  "save all the articles from current article in this group to a disk file"
  (interactive "FSave them (from current article to end of group to file: ")
  (let ((newsrcbuf (find-file-noselect file))
	(curgroup (vn-current-group))
	currartfile)
    (while (and (equal curgroup (vn-current-group))
		(not (eobp)))
      (setq currartfile (vn-current-article-file))
      (set-buffer newsrcbuf)
      (goto-char (point-max))
      (insert-file currartfile)
      (set-buffer vn-buffer)
      (vn-down-article))
    (set-buffer newsrcbuf)
    (save-buffer)
    (kill-buffer newsrcbuf)))

(defun vn-update-rc-to-cursor ()
  "all articles on or above cursor will be marked as read in the .newsrc file"
  (interactive)
  (save-excursion
    (let ((newsrcbuf (find-file-noselect
		       (substitute-in-file-name news-startup-file))))
      (update-all-groups-to-cursor newsrcbuf)
      (set-buffer newsrcbuf)
      (save-buffer)
      (kill-buffer (current-buffer)))))

(defun vn-update-rc-to-eogroup ()
  "all articles on or above the current group
will be marked as read in the .newsrc file"
  (interactive)
  (save-excursion
    (let ((newsrcbuf (find-file-noselect
		       (substitute-in-file-name news-startup-file))))
      (update-all-groups-to-eogroup newsrcbuf)
      (set-buffer newsrcbuf)
      (save-buffer)
      (kill-buffer (current-buffer)))))

(defun vn-update-rc-all()
  "all articles scheduled to be read will
be marked as read in the .newsrc file"
  (interactive)
  (save-excursion
    (let ((newsrcbuf (find-file-noselect
		       (substitute-in-file-name news-startup-file))))
      (update-all-groups newsrcbuf)
      (set-buffer newsrcbuf)
      (save-buffer)
      (kill-buffer (current-buffer)))))

(defun vn-unsubscribe-group ()
  "toggle between subscription/unsubscription of/from current group"
  (interactive)
  (save-excursion
    (if (re-search-backward "^.... Group: " nil t)
	(progn
	  (re-search-forward "^.... Group: " nil t)
	  (beginning-of-line)
	  (if (looking-at "====")
	      (progn
		(toggle-read-only)
		(delete-char 4)
		(insert "----")
		(toggle-read-only))
	    (toggle-read-only)
	    (delete-char 4)
	    (insert "====")
	    (toggle-read-only))))))

(defun vn-toggle-header-display ()
  "Toggle showing ALL the header lines"
  (interactive)
  (if vn-header-on
      (setq vn-header-on nil)
    (setq vn-header-on t)))

;; ------------reader mode stuff------------------------

(defun vn-next-article ()
  "back to the vn buffer if no further articles"
  (interactive)
  (erase-buffer)
;  (set-buffer vn-buffer)
;  (select-window vn-window)
  (pop-to-buffer vn-buffer)
  (let ((curgroup (vn-current-group)))
    (beginning-of-line)
    (toggle-read-only)
    (delete-char 1)
    (insert " ")	
    (toggle-read-only)
    (beginning-of-line)
    (vn-down-article)
    (if (and vn-read-sequence
	     (equal curgroup (vn-current-group)))
	(vn-read-article))))

(defun vn-prev-article ()
  "back to the vn buffer if no previous articles"
  (interactive)
  (erase-buffer)
;  (set-buffer vn-buffer)
;  (select-window vn-window)
  (pop-to-buffer vn-buffer)
  (let ((curgroup (vn-current-group)))
    (beginning-of-line)
    (toggle-read-only)
    (delete-char 1)
    (insert " ")	
    (toggle-read-only)
    (beginning-of-line)
    (vn-up-article)
    (if (and vn-read-sequence
	     (equal curgroup (vn-current-group)))
	(vn-read-article))))

(defun vn-abort-reading ()
  "back to the buffer, and no other articles"
  (interactive)
  (erase-buffer)
  (setq vn-read-sequence nil)
  (let ((buffer (vn-find-buffer)))
;    (set-buffer buffer)
;    (select-window vn-window)
    (pop-to-buffer buffer)
    (beginning-of-line)
    (toggle-read-only)
    (delete-char 1)
    (insert " ")	
    (toggle-read-only)
    (beginning-of-line)
    (vn-down-article)))

(defun vn-abort-reading-next-group ()
  "back to the buffer, the first article in the next group"
  (interactive)
  (erase-buffer)
  (setq vn-read-sequence nil)
  (let ((buffer (vn-find-buffer)))
;    (set-buffer buffer)
;    (select-window vn-window)
    (pop-to-buffer buffer)
    (beginning-of-line)
    (toggle-read-only)
    (delete-char 1)
    (insert " ")	
    (toggle-read-only)
    (beginning-of-line)
    (vn-next-group)))

(defun vn-beginning-of-article ()
  "back to the beginning of the article..."
  (interactive)
  (beginning-of-buffer))

(defun vn-end-of-article ()
  "jump to the end of the article"
  (interactive)
  (end-of-buffer))

(defun vn-scroll-line ()
  "scroll line up one"
  (interactive)
  (scroll-up 1))

(defun vn-toggle-rot ()
  "caesar en/de code the current article"
  (interactive)
  (news-caesar-buffer-body)
)

(defvar vn-header-on nil "nil or t, no or yes to verbose header...")

(defun vn-toggle-header ()
  "to display those header lines or not to..."
  (interactive)
  (if vn-header-on 
      (progn
	(setq vn-header-on nil)
	(save-excursion
	  (goto-char (point-min))
	  (news-convert-format)))
    (setq vn-header-on t)
;    (set-buffer vn-buffer)
;    (select-window vn-window)
    (pop-to-buffer vn-buffer)
    (vn-read-article)))

(defun vn-next-page ()
  "scroll article/article-list up a page"
  (interactive)
  (scroll-up))

(defun vn-prev-page ()
  "scroll article/article-list down a page"
  (interactive)
  (scroll-down)
  (move-to-window-line 0))

(defun vn-save-this-article (file)
  "save this article by appending to a file"
  (interactive "FSave item in file:")
  (append-to-file (point-min) (point-max) file))
-- 
Karl