[comp.emacs] vn.el -- dired and rnews combined for gnu emacs...

murf@caeco.UUCP (Steve Murphy) (10/13/87)

The following is vn.el, a marriage of dired principles and rnews
practices, made to resemble 'vn', by Bob McQueer. This is not a shar
file. Cut at the line and stuff into the file vn.el. Use load-file
with gnu- emacs. Works with 18.47.

I stuck a standard gnu banner on it. If you see this, Mr. Stallman,
and it meets not with your approval, forgive me, for I am ignorant.

This package needs the file, newslist.c to be compiled and have the
executable, newslist, somewhere in your path.

newslist.c will be in another article.

Enjoy....

Steve Murphy,
Midvale, Ut
(Caeco, Inc.)

-----------vn.el follows------------------------------------------------------
;;; 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.

;; 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))
  (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))
	(setq vn-buffer (current-buffer))
	(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]")
  (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" ))
	  (progn
		(goto-char (point-max))
		(forward-line -1))
	(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 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)
		(beginning-of-line)
		(kill-line 1)))))

(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
						(skip-chars-forward "^:!")
						(forward-char 2)
						(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
	(let ((thisgroup (vn-current-group-dot)))
	  (if (re-search-backward (concat ".... Group: " thisgroup " "))
		  (progn
			(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))

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