[comp.emacs] vn.el -- a repost

Unknown@caeco.UUCP (Steve Murphy) (11/15/87)

I got some letters stating that newslist.c had been lost from
the original distribution.... There were some bugs in the
vn.el package, too, so...... sorry, but I'm reposting this.
This time everythings in one file, so if something gets lost,
it all gets lost, and you won't know this happened (sigh).

Again, to answer some Q's.... this is an imitation of vn -- a
news reading program by Mr. McQueer, that I pulled off usenet some
time ago.

You should set up the NEWSBOX environment variable to point to
a directory where news articles are to be saved. The most
practical thing to do is put something like this in the .xxxrc
(or whatever) file in your home dir:

setenv NEWSBOX ~/newsbox

(assuming, of course you have a subdir of your home dir called newsbox.
Have fun, read the headers, etc.

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./vn.el`
then
echo "writing ./vn.el"
cat > ./vn.el << '\Rogue\Monster\'
;;; 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 "$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)
	  (call-process "newslist" nil buffer nil)
      (if (not (zerop (buffer-size)))
		  (goto-char (point-min))))))

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

(defun vn ()
  "A half-hearted 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 "r" 'vn-read-article)
  (define-key vn-mode-map " " 'vn-read-article)
  (define-key vn-mode-map "q" 'vn-quit)
  (define-key vn-mode-map "k" 'vn-up-article)
  (define-key vn-mode-map "j" 'vn-down-article)
  (define-key vn-mode-map "\C-h" 'vn-prev-group)
  (define-key vn-mode-map "n" 'vn-next-group)
  (define-key vn-mode-map "\C-m" 'vn-next-page)
  (define-key vn-mode-map "R" 'vn-read-all-articles)
  (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 "q" 'vn-abort-reading)
  (define-key vn-read-mode-map "Q" 'vn-abort-reading-next-group)
  (define-key vn-read-mode-map "r" '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-reply)
  (define-key vn-read-mode-map "f" 'news-reply)
  (define-key vn-read-mode-map "s" 'vn-save-this-article)
  (define-key vn-read-mode-map "p" 'vn-print-this-article)
  (define-key vn-read-mode-map " " 'vn-next-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-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 prev. 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)
  (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)
	  (forward-line 1))))

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

(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)
  (call-interactively 'vn-save-article-2 nil)
  (beginning-of-line)
  (toggle-read-only)
  (delete-char 1)
  (insert " ")
  (toggle-read-only))

(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)
  (let ((curgroup (vn-current-group)))
	(beginning-of-line)
	(toggle-read-only)
	(delete-char 1)
	(insert " ")	
	(toggle-read-only)
	(vn-down-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)
	(beginning-of-line)
	(toggle-read-only)
	(delete-char 1)
	(insert " ")	
	(toggle-read-only)
	(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)
	(beginning-of-line)
	(toggle-read-only)
	(delete-char 1)
	(insert " ")	
	(toggle-read-only)
	(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)
	(vn-read-article)))

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

(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))

\Rogue\Monster\
else
  echo "will not over write ./vn.el"
fi
if `test ! -s ./newslist.c`
then
echo "writing ./newslist.c"
cat > ./newslist.c << '\Rogue\Monster\'
/* a facility to generate article lists much after the format
   originating with Bob McQueer's 'vn' package.
   Written by Steve Murphy of Midvale, Utah
   
   Copyright (C) 1987 Free Software Foundation, Inc.

		       NO WARRANTY

  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.

 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.

		GENERAL PUBLIC LICENSE TO COPY

  1. You may copy and distribute verbatim copies of this source file
as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy a valid copyright notice "Copyright
(C) 1985 Free Software Foundation, Inc."; and include following the
copyright notice a verbatim copy of the above disclaimer of warranty
and of this License.  You may charge a distribution fee for the
physical act of transferring a copy.

  2. You may modify your copy or copies of this source file or
any portion of it, and copy and distribute such modifications under
the terms of Paragraph 1 above, provided that you also do the following:

    a) cause the modified files to carry prominent notices stating
    that you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish,
    that in whole or in part contains or is a derivative of this
    program or any part thereof, to be licensed at no charge to all
    third parties on terms identical to those contained in this
    License Agreement (except that you may choose to grant more
    extensive warranty protection to third parties, at your option).

    c) You may charge a distribution fee for the physical act of
    transferring a copy, and you may at your option offer warranty
    protection in exchange for a fee.

  3. You may copy and distribute this program or any portion of it in
compiled, executable or object code form under the terms of Paragraphs
1 and 2 above provided that you do the following:

    a) cause each such copy to be accompanied by the
    corresponding machine-readable source code, which must
    be distributed under the terms of Paragraphs 1 and 2 above; or,

    b) cause each such copy to be accompanied by a
    written offer, with no time limit, to give any third party
    free (except for a nominal shipping charge) a machine readable
    copy of the corresponding source code, to be distributed
    under the terms of Paragraphs 1 and 2 above; or,

    c) in the case of a recipient of this program in compiled, executable
    or object code form (without the corresponding source code) you
    shall cause copies you distribute to be accompanied by a copy
    of the written offer of source code which you received along
    with the copy you received.

  4. You may not copy, sublicense, distribute or transfer this program
except as expressly provided under this License Agreement.  Any attempt
otherwise to copy, sublicense, distribute or transfer this program is void and
your rights to use the program under this License agreement shall be
automatically terminated.  However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.

  5. If you wish to incorporate parts of this program into other free
programs whose distribution conditions are different, write to the Free
Software Foundation at 1000 Mass Ave, Cambridge, MA 02138.  We have not yet
worked out a simple rule that can be stated here, but we will often permit
this.  We will be guided by the two goals of preserving the free status of
all derivatives of our free software and of promoting the sharing and reuse of
software.


In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */

#include <stdio.h>
#include <strings.h>

struct groupnode
{
	struct groupnode *next,*prev;
	char name[80];
	char status;
	struct art *arts;
	struct active
	{
		int start,end;
	} active;
	struct myrc	
	{
		int start,end;
	} myrc;
};

struct art
{
	struct art *next;
	int num;
	char subj[200];
};


struct groupnode *groups=0,*groupend = 0;

struct groupnode *find_gn(name)
char *name;
{
	struct groupnode *gnp;
	for(gnp=groups; gnp; gnp=gnp->next)
	{
		if(!strcmp(gnp->name,name))
		{
			return gnp;
		}
	}
	return 0;
}

ins_gn(gnp)
struct groupnode *gnp;
{
	if( !groups )
	{
		groups = groupend = gnp;
	}
	else
	{
		gnp->prev = groupend;
		gnp->next = 0;
		groupend = gnp;
		gnp->prev->next = gnp;
	}
}

main()
{
	FILE *f;
	char *getenv();
	char buff[1000],t[1000];
	struct groupnode *gnp;
	
	sprintf(buff,"%s/.newsrc",getenv("HOME"));
	f = fopen(buff,"r");
	while (fgets(buff,1000,f)) /* skip options */
	{
		if(!strncmp(buff,"options",7))
			continue;
		else break;
	}
	do
	{
		int n;
		gnp = (struct groupnode *)calloc(sizeof(struct groupnode),1);
		n = sscanf(buff,"%s %d-%d",gnp->name,&gnp->myrc.start,
		              &gnp->myrc.end);
		if( n < 3 )
			gnp->myrc.end = gnp->myrc.start = 0;
		gnp->active.start = gnp->active.end = -1;
		gnp->status = gnp->name[strlen(gnp->name)-1]; 
		gnp->name[strlen(gnp->name)-1] = 0;
		ins_gn(gnp);
	} while (fgets(buff,1000,f));
	fclose(f);
	strcpy(buff,"/usr/lib/news/active");
	f = fopen(buff,"r");
	if( !f )
	{
		fprintf(stderr,"Couldn't open %s\n",buff);
		exit(999);
	}
	while (fgets(buff,1000,f))
	{
		int start,end;
		
		sscanf(buff,"%s %d %d ",t,&end,&start);
		gnp = find_gn(t);
		if( !gnp )
		{
			printf("++++ Group: %s not in .newsrc file...\n",t);
			gnp = (struct groupnode *)calloc(sizeof(struct groupnode),1);
			strcpy(gnp->name,t);
			gnp->status = ':';
			gnp->myrc.start = 1;
			gnp->myrc.end = 0;
			ins_gn(gnp);
		}
		gnp->active.start = start;
		gnp->active.end = end;
	}
	fclose(f);
	for(gnp=groups; gnp; gnp=gnp->next)
	{
		if(gnp->active.start == gnp->active.end
		   && gnp->active.end == -1 )
		{
			printf("---- Group: %s No Longer Exists\n",gnp->name);
		}
	}
	
	for(gnp=groups; gnp; gnp=gnp->next)
	{
		int st,en,i;
		char *sp,subject[300],from[300],lines[10];
		if( gnp->status == ':' && (gnp->myrc.end < gnp->active.end
		   && gnp->active.end > gnp->active.start
		   || gnp->active.end < gnp->myrc.end ))
		{
			if( gnp->active.end < gnp->myrc.end )
			{
				gnp->myrc.end = gnp->active.start; /* for those cases when it's pretty obvious
													  that something's out of whack, lets just
													  play it safe and view the whole set again... */
			}
			printf("==== Group: %s =====\n", gnp->name);
			st = gnp->myrc.end + 1; /* the next one after the last one read */
			if( st < gnp->active.start )
				st = gnp->active.start; /* active start == first art there */
			en = gnp->active.end;
			sprintf(buff,"/usr/spool/news/%s",gnp->name);
			while( sp=index( buff,'.' ))
			{
				*sp = '/';
			}
			for( i=st; i<= en; i++)
			{
				char tb[1000];
				strcpy(subject,"");
				strcpy(from,"");
				strcpy(lines,"");
				sprintf(t,"%s/%d",buff,i);
				f = fopen(t,"r");
				if( !f )
					continue;
				while( fgets(tb,1000,f))
				{
					tb[strlen(tb)-1] = 0; /* get rid of the trailing \n */
					if( !strncmp(tb,"Subject:",8))
					{
						strcpy(subject,tb+9);
					}
					if( !strncmp(tb,"Lines:",6))
					{
						strcpy(lines,tb+7);
					}
					if( !strncmp(tb,"From:",5))
					{
						char *tt;
						tt = index(tb,'(');
						if( tt )
						{
							strcpy(from,tt);
							tt = index(from,')');
							if(tt)
							{
								*(tt+1) = 0;
							}
						}
						else
							strcpy(from,tb+6);
					}
					if(*subject && *from && *lines)
						break;
				}
				fclose(f);
				printf("  %d: %s ~ %s %s\n",
					   i,subject,lines,from);
			}
		}
	}
}
\Rogue\Monster\
else
  echo "will not over write ./newslist.c"
fi
echo "Finished archive 1 of 1"
exit
-- 
	Steve Murphy, 	CAECO, Inc., 7090 South Union Park Avenue,
			Suite 200, Midvale, UT 84047
			(801)255-8880
	<WORLD>!{byuadam,utah-cs,nrc-ut,wicat}!caeco!murf