home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-07-22 | 44.1 KB | 1,250 lines |
- Hi,
-
- Josh Marantz posted a very small news reader called jnews a few weeks
- ago. I decided to try it and found that I really liked it, enough to
- enhance it to the point that it is now easy to install and confortable
- to use.
-
- It provides a very nice summary mode that allows the user a different
- sort of view of the "big picture". I find that I am much more aware of
- volumes and of subject flows that previously because I am always
- looking at a comprehensive list of the messages in the newsgroup.
-
- It's certainly not for everyone as it lacks power (e.g. kill files,
- follow thread), but it is a good alternative to RNEWS. It is now
- possible to follow up, reply, save messages to files and mail folders
- and do all those other things that we take for granted in other
- readers.
-
- Following is a shar file that contains 5 files:
-
- 1) jnews.doc - Josh's text at the front of his original posting. I
- have added an addendum which describes my changes to jnews.el and
- gives a more complete set of installation instructions including
- the use of the new conversion script for .newsrc to jnews.
-
- 2) jnews.c - the course for jnews's news directory scanner. This
- program runs when you install jnews and then each time you enter a
- news group. It builds the summary file that jnews uses to track
- messages that you have read.
-
- 3) jnews.el - the emacs lisp file that contains jnews mode in its
- entirety. Really small. Note that this is my version.
-
- 4) newsrc_to_jnews - a small script that will scan your .newsrc file
- and take all newsgroups to which your are subscribed and create
- jnews summary files. You must be in your ~/jnews directory when you
- run it.
-
- 5) jnews.original - Josh's original version of jnews.el.
-
- For those who already use jnews, here is a summary of changes:
-
- - installation script to create initial summary files.
-
- - when quitting either an article or a summary, the user is popped
- back to the buffer at the previous level. If there are no more
- articles, the user is popped all the way from the last article to
- the top level.
-
- - hooked up the ability to save an article into a file or a mail
- folder.
-
- - hooked up the stuff to allow mailing a message, following up to
- usenet, or posting a new message.
-
- - changed jnews-goto-unread to pop right into the article which
- saves typing and time.
-
- Kim
- ------------------cut here--------------------
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: jnews.doc jnews.c jnews.el newsrc_to_jnews jnews.original
- # Wrapped by kim@kim on Fri May 18 12:49:11 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'jnews.doc' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'jnews.doc'\"
- else
- echo shar: Extracting \"'jnews.doc'\" \(3737 characters\)
- sed "s/^X//" >'jnews.doc' <<'END_OF_FILE'
- XFrom: josh@viewlog.UUCP (Josh Marantz)
- XNewsgroups: comp.emacs
- XSubject: Jnews: Yet Another News Reading Package for Emacs
- XDate: 26 Apr 90 17:04:36 GMT
- XOrganization: Viewlogic Systems Inc., Marlboro, MA
- X
- XOriginal Documentation by Josh Marantz
- X
- XI'm sure people have had it up to their modelines with news packages,
- Xbut you may really like this one. Why? Because its small, fast, and
- Xfairly powerful given its small set of features. We've been using
- Xit here at Viewlogic for a while, and it definitely makes wading through
- Xreems of high-volume newsgroups much quicker.
- X
- XIt is used on newsgroups like Dired is used on files. In fact, for
- X"select newsgroup mode", it uses Dired on the directory ~/jnews, which
- Xcontains one file for each newsgroup you read. You select which
- Xnewsgroup you want by editing the file for it (e.g.
- X~/jnews/comp.emacs). The file is maintained with the first line as
- X-*-jnews-*-, so it goes into jnews-mode when you edit the file.
- X
- XWhen editing a jnews file, each message is displayed on one line,
- Xindicating date, author, subject, number of lines, and message number.
- XYou move the cursor to a message, and hit "e" to see it, hit
- XCarriage-Return to bypass it while marking it as read, and hit "*" to
- Xmark it as unread, even if you've seen it. Expired messages are
- Xmarked with '#', unread messages are marked with '*'. Hitting tab
- Xgets you to the next unexpired message. Hitting 'x' gets rid of all
- Xexpired messages. Hitting 'q' kills the buffer.
- X
- XHelp is available with '?' and 'h'.
- X
- XThe news-package works with a small elisp file and a small auxiliary C
- Xprogram. The C program is not linked into Emacs. It's run with call-process.
- XIt's used to build the summary file, which can be done faster in C than
- Xin Elisp because the C program doesn't have to scan the whole message,
- Xjust the first few lines.
- X
- XJnews will probably not work with nntp. Jnews assumes you have your news
- Xin a directory tree, off of /usr/spool/news. A small Elisp modification
- Xwould allow it to pass an alternate news-root to the jnews C program.
- X
- XYou must do "mkdir ~/jnews" manually before running jnews.
- X
- XAnyway, I apologize for not doing a super packaging job, but people around
- Xhere have been urging me to publicize this, so maybe you'll think its
- Xworth it. Here's some global bindings for your .emacs, followed by
- Xjnews.c and jnews.el. Enjoy!
- X
- XAddendum by Kim Letkeman
- X
- XI have added some functionality to jnews.el and a script that will
- Xtake your .newsrc and create the summary files in your jnews
- Xdirectory. It is still very small and fast and is somewhat more
- Xconvenient now to use and especially to get started. Kudos to Josh for
- Xa nice idea and a nice implementation.
- X
- XMy changes to jnews.el are:
- X
- X- when quitting either an article or a summary, the user is
- X popped back to the buffer at the previous level. If there
- X are no more articles, the user is popped all the way from
- X the last article to the top level.
- X
- X- hooked up the ability to save an article into a file or a
- X mail message, much like RNEWS.
- X
- X- hooked up the stuff to allow mailing a message, following up
- X to usenet, or posting a new message.
- X
- X- changed jnews-goto-unread to pop right into the article which
- X saves typing and time.
- X
- XTo install and run jnews:
- X
- X- compile jnews.c and put "jnews" in a directory on your path.
- X "cc -o jnews jnews.c"
- X
- X- byte compile jnews.el and put jnews.elc in your local lisp
- X directory
- X
- X- create ~/jnews, cd to it, and execute newsrc_to_jnews which
- X will create your jnews summary files
- X
- X- add the following 2 lines to your .emacs file (and bind keys to
- X the commands if you so choose.)
- X
- X(autoload 'jnews "jnews" "Read usenet news" t)
- X(autoload 'jnews-add-newsgroup "jnews" "Add new usenet newsgroup" t)
- X
- XKim
- X
- END_OF_FILE
- if test 3737 -ne `wc -c <'jnews.doc'`; then
- echo shar: \"'jnews.doc'\" unpacked with wrong size!
- fi
- # end of 'jnews.doc'
- fi
- if test -f 'jnews.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'jnews.c'\"
- else
- echo shar: Extracting \"'jnews.c'\" \(5495 characters\)
- sed "s/^X//" >'jnews.c' <<'END_OF_FILE'
- X
- X/* @(#)jnews.c 1.2 2/14/90 */
- X
- X/* Written by Joshua Marantz, Viewlogic Systems Inc, February 1990 */
- X
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <sys/dir.h>
- X
- XFILE *headerfile;
- X
- Xmain(argc, argv)
- X int argc;
- X char *argv[];
- X{
- X int article, last_header, last_article, first_article;
- X char dir[256];
- X
- X if ((argc != 2) && (argc != 3)) {
- X fprintf (stderr, "usage: %s news.group [newsgroup-directory]\n",
- X argv[0]);
- X exit (1);
- X }
- X
- X /* If the news group directory was not specified, figure it out */
- X get_newsgroup_directory (argv[1],
- X (argc == 2) ? "/usr/spool/news" : argv[2],
- X dir);
- X
- X /* Find the first and last articles in the directory */
- X max_articles (dir, &first_article, &last_article);
- X
- X /* Open the header file, and find the number of the last article in it */
- X last_header = open_and_scan_headerfile (argv[1]);
- X
- X /* Add in the articles that are yet in the header file */
- X if (first_article < last_header + 1)
- X first_article = last_header + 1;
- X for (article = first_article; article <= last_article; article++)
- X add_to_headerfile (article, dir);
- X
- X fclose (headerfile);
- X}
- X
- Xget_newsgroup_directory(newsname, root, dir)
- X char *newsname, *root, *dir;
- X{
- X int i;
- X sprintf (dir, "%s/%s", root, newsname);
- X for (i = strlen (root); dir[i] != '\0'; i++)
- X if (dir[i] == '.')
- X dir[i] = '/';
- X}
- X
- Xmax_articles(dirname, first, last)
- X char *dirname;
- X int *first, *last;
- X{
- X int i, j, article;
- X DIR *dir;
- X struct direct *readdir (), *dp;
- X
- X if ((dir = opendir (dirname)) == NULL) {
- X perror (dir);
- X exit (1);
- X }
- X
- X *first = 32767;
- X *last = 0;
- X
- X for (dp = readdir (dir); dp != NULL; dp = readdir (dir)) {
- X if ((sscanf (dp -> d_name, "%d", &article) == 1) &&
- X (article > 0))
- X {
- X if (article > *last)
- X *last = article;
- X if (article < *first)
- X *first = article;
- X }
- X }
- X closedir (dir);
- X}
- X
- Xopen_and_scan_headerfile(headername)
- X char *headername;
- X{
- X char buf[256];
- X char *jnews_signature = "-*-jnews-*-\n";
- X int article_number = 0;
- X
- X headerfile = fopen (headername, "r+");
- X if (headerfile == NULL) {
- X headerfile = fopen (headername, "w");
- X if (headerfile == NULL) {
- X perror (headername);
- X exit (1);
- X }
- X fputs (jnews_signature, headerfile);
- X }
- X
- X /* If the file exists, make sure its got the signature! */
- X else if ((fgets (buf, 255, headerfile) == NULL) ||
- X (strcmp (buf, jnews_signature) != 0))
- X {
- X fprintf (stderr, "Corrupt jnews header file: %s != %s\n",
- X buf, jnews_signature);
- X fclose (headerfile);
- X exit (1);
- X }
- X
- X /* Jump to the last line of the file, which should be exactly 80
- X bytes from the end of the file. */
- X else if (fseek (headerfile, -80L, 2) != 0) {
- X fclose (headerfile);
- X perror ("Could not seek to end-80 in jnews header file");
- X exit (1);
- X }
- X else if (fgets (buf, 255, headerfile) == NULL) {
- X fclose (headerfile);
- X perror ("Could not read the last 80 bytes of jnews header file");
- X exit (1);
- X }
- X else if ((sscanf (&buf[1], "%d|", &article_number) != 1) ||
- X (article_number == 0))
- X {
- X fclose (headerfile);
- X fprintf (stderr,
- X "Could not scan the last article number from line:\n%s",
- X buf);
- X exit (1);
- X }
- X fseek (headerfile, 0, 2);
- X return (article_number);
- X}
- X
- Xadd_to_headerfile(article, dir)
- X int article;
- X char *dir;
- X{
- X char buf[256], subject[80], date[80], lines[80], from[80], *author;
- X char fname[256];
- X FILE *f;
- X int matches = 0;
- X int i, len;
- X
- X sprintf (fname, "%s/%d", dir, article);
- X if ((f = fopen (fname, "r")) == NULL) {
- X perror (fname);
- X return;
- X }
- X
- X from[0] = subject[0] = date[0] = lines[0] = '\0';
- X
- X while ((matches < 4) && (fgets (buf, 255, f) != NULL)) {
- X
- X /* Chop off the last \n */
- X len = strlen (buf);
- X if ((len > 0) && (buf[len - 1] == '\n'))
- X buf[len - 1] = '\0';
- X
- X /* Assume we will get a match and decrement if we do not! */
- X matches++;
- X if (strncmp (buf, "From: ", 6) == 0)
- X strcpy (from, &buf[6]);
- X else if (strncmp (buf, "Date: ", 6) == 0)
- X strcpy (date, &buf[6]);
- X else if (strncmp (buf, "Subject: ", 9) == 0)
- X strcpy (subject, &buf[9]);
- X else if (strncmp (buf, "Lines: ", 7) == 0)
- X strcpy (lines, &buf[7]);
- X else
- X matches--;
- X }
- X
- X fclose (f);
- X
- X /* Transform the subject to a human name if supplied. */
- X if (((len = strlen (from)) == 0) || (from[len - 1] != ')'))
- X author = from;
- X else {
- X /* Copy the string right justified inside parens */
- X for (i = len - 1; (i >= 0) && (from[i] != '('); i--);
- X if (i >= 0) {
- X from[len - 1] = '\0';
- X author = &from[i + 1];
- X }
- X }
- X
- X date[9] = author[18] = subject[37] = lines[5] = '\0';
- X
- X /* If the date takes only one digit, chop off the space at the end */
- X if (date[strlen (date) - 1] == ' ')
- X date[strlen (date) - 1] = '\0';
- X
- X fprintf (headerfile, "*%5d|%37s|%9s|%18s|%5s\n",
- X article, subject, date, author, lines);
- X}
- END_OF_FILE
- if test 5495 -ne `wc -c <'jnews.c'`; then
- echo shar: \"'jnews.c'\" unpacked with wrong size!
- fi
- # end of 'jnews.c'
- fi
- if test -f 'jnews.el' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'jnews.el'\"
- else
- echo shar: Extracting \"'jnews.el'\" \(15915 characters\)
- sed "s/^X//" >'jnews.el' <<'END_OF_FILE'
- X; @(#)jnews.el 1.1 2/14/90
- X;[Insert Gnu Copyleft here]
- X;
- X;Jnews is Yet Another News Reading Package. Its philosophy follows that of
- X;DIRED. Users read news by DIREDing their jnews subdirectory, which contains
- X;summary files for each interesting newsgroup. When a user edits a summary
- X;file, jnews-mode is invoked. The summary file is composed of line line
- X;summaries of each article, indicating the article number, date, author,
- X;subject, and length. Users move to the article they want using standard Emacs
- X;keys, and hit 'e' to edit that message.
- X;
- X;A summary file is created by jnews, a C program that efficiently scans the
- X;usenet directory, which is in the /usr/spool/news hierarchy. It does this
- X;incrementally so that when new articles arrive, the old article summary
- X;lines are not recalculated. This happens very fast, unless you are building
- X;a new summary file for a large newsgroup. The speed for compiling a new
- X;summary is approximately 12 articles/second on an unloaded Sun 3/60 running
- X;SunOS 3.5, accessing news spool files on a Sun 386i running SunOS 4.01
- X;using NFS on an idle ethernet.
- X;
- X;It might be preferable to make the jnews program part of the Emacs executable,
- X;rather than doing a call-process, but that would make it harder for people
- X;to try out. If the FSF people want to integrate it in to Emacs, it shouldn't
- X;be difficult. Of course, everything that the jnews program does can be done
- X;in Emacs lisp, but not without reading in all of each article. Jnews only
- X;has to scan the first few lines to look at the header information. Then it
- X;closes the file and goes on to the next one.
- X;
- X; Written by Joshua Marantz, Viewlogic Systems Inc. February, 1989.
- X; Additions and changes by Kim Letkeman - May, 1990.
- X; Changes include:
- X; - when quitting either an article or a summary, the user is
- X; popped back to the buffer at the previous level. If there
- X; are no more articles, the user is popped all the way from
- X; the last article to the top level.
- X; - hooked up the ability to save an article into a file or a
- X; mail message, much like RNEWS.
- X; - hooked up the stuff to allow mailing a message, following up
- X; to usenet, or posting a new message.
- X; - changed jnews-goto-unread to pop right into the article which
- X; saves typing and time.
- X;
- X
- X;----------------------Jnews Global Variables---------------------------------
- X
- X(defvar jnews-program (expand-file-name "~kim/bin/jnews")
- X "Pathname of jnews summary building program.")
- X
- X(defvar jnews-summary-directory (concat (getenv "HOME") "/jnews/")
- X "Directory in which to save newsgroup headers.")
- X
- X(defvar jnews-mode-map (make-sparse-keymap)
- X "Local keymap for jnews summaries.")
- X
- X(defvar jnews-article-mode-map (make-sparse-keymap)
- X "Local keymap for jnews articles.")
- X
- X(defvar jnews-current-newsgroup nil
- X "Name of buffer containing current article's newsgroup summary")
- X;(make-variable-buffer-local 'jnews-current-newsgroup)
- X
- X(autoload 'rmail-output "rmailout"
- X "Save the current message into a mail folder."
- X t)
- X
- X(autoload 'news-save-item-in-file "rnews"
- X "Save the current message into a file."
- X t)
- X
- X(autoload 'news-reply "rnewspost"
- X "Compose and post a reply to the current article on USENET.
- XWhile composing the reply, use \\[mail-yank-original] to yank the original
- Xmessage into it."
- X t)
- X
- X(autoload 'news-mail-other-window "rnewspost"
- X "Send mail in another window.
- XWhile composing the message, use \\[mail-yank-original] to yank the
- Xoriginal message into it."
- X t)
- X
- X(autoload 'news-post-news "rnewspost"
- X "Begin editing a new USENET news article to be posted."
- X t)
- X
- X(autoload 'news-mail-reply "rnewspost"
- X "Mail a reply to the author of the current article.
- XWhile composing the reply, use \\[mail-yank-original] to yank the original
- Xmessage into it."
- X t)
- X
- X(defvar rmail-last-file (expand-file-name "~/mbox.news"))
- X
- X
- X;-----------------------Utility functions of general use-----------------------
- X
- X(defun describe-local-map ()
- X "Print help on each key that is bound in the current mode map"
- X (interactive)
- X (let ((help-keys (cdr (current-local-map))))
- X (pop-to-buffer "*Help*")
- X (erase-buffer)
- X (mapcar '(lambda (key-fun)
- X (insert-char (car key-fun) 1)
- X (indent-to-column 3)
- X (insert (prin1-to-string (cdr key-fun)))
- X (indent-to-column 26)
- X (save-excursion (insert (documentation (cdr key-fun))))
- X (move-to-column (- (screen-width) 1))
- X (delete-region (point) (point-max))
- X (insert "\n"))
- X help-keys)))
- X
- X(defun summarize-local-map ()
- X "Print one line message in minibuffer summarizing current local map"
- X (interactive)
- X (let ((help-keys (cdr (current-local-map))))
- X (save-excursion
- X (switch-to-buffer "*Help*")
- X (erase-buffer)
- X (mapcar '(lambda (key-fun)
- X (insert (char-to-string (car key-fun)) ":"
- X (prin1-to-string (cdr key-fun)) " "))
- X help-keys)
- X (message (buffer-substring (point-min) (point-max))))))
- X
- X(defun save-and-kill ()
- X "Save this buffer with no backup and kill it"
- X (interactive)
- X (save-buffer 0)
- X (kill-buffer (current-buffer)))
- X
- X(defun kill-current-buffer ()
- X "Kill the current buffer"
- X (interactive)
- X (kill-buffer (current-buffer)))
- X
- X(defun backward-line (arg)
- X "Go to beginning of previous line"
- X (interactive "p")
- X (forward-line (- arg)))
- X
- X; These next two functions were stolen from rnews.el
- X(defun string-subst-char (new old string)
- X "Substitute character NEW for character OLD in STRING"
- X (let (index)
- X (setq old (regexp-quote (char-to-string old))
- X string (substring string 0))
- X (while (setq index (string-match old string))
- X (aset string index new)))
- X string)
- X
- X;;; caesar-region written by phr@prep.ai.mit.edu Nov 86
- X;;; modified by tower@prep Nov 86, josh@vx Jan 89
- X(defun caesar-bounded-region (bound1 bound2 &optional n)
- X "Caesar rotation of region defined by BOUND1 and BOUND2 by N (default 13)"
- X (interactive "dmp")
- X (let ((rot (cond ((or (null current-prefix-arg)
- X (not (numberp n))
- X (= (% n 26) 0))
- X 13)
- X ((< n 0) (- 26 (% (- n) 26)))
- X (t (% n 26)))))
- X (if (or (not (boundp 'caesar-translate-table))
- X (/= (aref caesar-translate-table ?a) (+ ?a rot)))
- X (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
- X (message "Building caesar-translate-table...")
- X (setq caesar-translate-table (make-vector 256 0))
- X (while (< i 256)
- X (aset caesar-translate-table i i)
- X (setq i (1+ i)))
- X (setq lower (concat lower lower) upper (upcase lower) i 0)
- X (while (< i 26)
- X (aset caesar-translate-table (+ ?a i) (aref lower (+ i rot)))
- X (aset caesar-translate-table (+ ?A i) (aref upper (+ i rot)))
- X (setq i (1+ i)))
- X (message "Building caesar-translate-table... done")))
- X (let* ((from (min bound1 bound2))
- X (to (max bound1 bound2))
- X (i 0)
- X (str (buffer-substring from to))
- X (len (length str))
- X (buffer-read-only nil))
- X (while (< i len)
- X (aset str i (aref caesar-translate-table (aref str i)))
- X (setq i (1+ i)))
- X (goto-char from)
- X (kill-region from to)
- X (insert str))))
- X
- X;-------------------------Jnews Summary mode Functions------------------------
- X
- X(defun jnews ()
- X "Start a Jnews session"
- X (interactive)
- X (dired (concat (getenv "HOME") "/jnews")))
- X
- X(defun jnews-back-to-menu ()
- X "Leave summary mode"
- X (interactive)
- X (save-and-kill)
- X (pop-to-buffer (get-buffer "jnews")))
- X
- X(defun jnews-read-article ()
- X "Read the article referred to on the current line"
- X (interactive)
- X (let* ((newsgroup (file-name-nondirectory buffer-file-name))
- X (news-path (concat news-path (string-subst-char ?/ ?. newsgroup) "/"))
- X (message-number
- X (save-excursion
- X (beginning-of-line)
- X (if (= (point) (point-min)) (error "No more articles"))
- X (forward-char 1)
- X (re-search-forward "[ ]*")
- X (let ((msgno-start (point)))
- X (search-forward "|")
- X (buffer-substring msgno-start (- (point) 1))))))
- X (if (not (file-exists-p (concat news-path message-number)))
- X (progn
- X (jnews-expired)
- X (message "Article has expired!"))
- X (progn
- X (jnews-flag)
- X (pop-to-buffer (concat newsgroup ":" message-number))
- X (insert-file (concat news-path message-number))
- X (goto-char (point-min))
- X (setq buffer-read-only t)
- X (message "")
- X (jnews-article-mode newsgroup)))))
- X
- X(defun jnews-article-has-expired ()
- X (let* ((newsgroup (file-name-nondirectory buffer-file-name))
- X (news-path (concat news-path (string-subst-char ?/ ?. newsgroup)
- X "/"))
- X (message-number
- X (save-excursion
- X (beginning-of-line)
- X (if (= (point) (point-min)) (error "No more articles"))
- X (forward-char 1)
- X (re-search-forward "[ ]*")
- X (let ((msgno-start (point)))
- X (search-forward "|")
- X (buffer-substring msgno-start (- (point) 1))))))
- X (not (file-exists-p (concat news-path message-number)))))
- X
- X(defun jnews-goto-unread ()
- X "Move point to the first unseen article"
- X (interactive)
- X (search-forward "\n*" nil t)
- X (beginning-of-line)
- X (while (jnews-article-has-expired)
- X (jnews-expired))
- X (if (looking-at "^[*]")
- X (jnews-read-article)))
- X
- X(defun jnews-update ()
- X "Run a C program to look for new articles in the current newsgroup"
- X (interactive)
- X (save-buffer 0)
- X (message "Updating %s..." (file-name-nondirectory buffer-file-name))
- X (call-process jnews-program
- X nil nil nil
- X (file-name-nondirectory buffer-file-name)
- X news-path)
- X (revert-buffer t t)
- X (setq buffer-read-only t)
- X (message "Updating %s...done" (file-name-nondirectory buffer-file-name)))
- X
- X(defun jnews-add-newsgroup (newsgroup)
- X "Add a new newsgroup"
- X (interactive "sNewsgroup: ")
- X (find-file (concat jnews-summary-directory newsgroup))
- X (jnews-mode))
- X
- X(defun jnews-flag ()
- X "Flag the current message as previously read"
- X (interactive)
- X (beginning-of-line)
- X (if (= (following-char) ?*)
- X (let ((buffer-read-only nil))
- X (delete-char 1)
- X (insert-char ? 1)))
- X (forward-line 1))
- X
- X(defun jnews-expired ()
- X "Flag the current message as previously read"
- X (interactive)
- X (beginning-of-line)
- X (if (not (= (following-char) ?#))
- X (let ((buffer-read-only nil))
- X (delete-char 1)
- X (insert-char ?# 1)))
- X (forward-line 1))
- X
- X; Do a binary search for the boundary of expired messages!!
- X(defun jnews-expunge-expired ()
- X "Expunge all expired messages"
- X (interactive)
- X (goto-char (point-min))
- X (forward-line 1)
- X (let ((buffer-read-only nil)
- X (begin-deletion (point))
- X (low (point))
- X (high (point-max)))
- X
- X ; Get close with a binary search
- X (while (> (- high low) 80)
- X (goto-char (/ (+ low high) 2))
- X (beginning-of-line)
- X (if (jnews-article-has-expired)
- X (setq low (point))
- X (setq high (point))))
- X
- X ; Make sure we have not missed with linear searches
- X (while (and (> (point) (point-min))
- X (not (jnews-article-has-expired)))
- X (forward-line -1))
- X (while (and (> (point) (point-min))
- X (< (point) (point-max))
- X (jnews-article-has-expired))
- X (forward-line 1))
- X (if (> (point) (point-min))
- X (delete-region begin-deletion (point)))))
- X
- X(defun jnews-unflag ()
- X "Unflag current message as if it had never been read"
- X (interactive)
- X (beginning-of-line)
- X (if (= (following-char) ? )
- X (let ((buffer-read-only nil))
- X (delete-char 1)
- X (insert-char ?* 1)))
- X (forward-line 1))
- X
- X;; Jnews mode is suitable only for specially formatted data.
- X(put 'jnews-mode 'mode-class 'special)
- X
- X(defun jnews-mode ()
- X "Mode for browsing through newsgroup summaries."
- X (interactive)
- X (if (not (eq major-mode 'jnews-mode))
- X (progn
- X (kill-all-local-variables)
- X (setq major-mode 'jnews-mode)
- X (setq mode-name "Summaries")
- X (use-local-map jnews-mode-map)
- X (setq mode-line-buffer-identification '("Jnews: %17b"))
- X (jnews-update)
- X (run-hooks 'jnews-mode-hook))))
- X
- X; Set up the jnews keymap when jnews.el is loaded
- X(define-key jnews-mode-map "?" 'summarize-local-map)
- X(define-key jnews-mode-map "b" 'scroll-down)
- X(define-key jnews-mode-map " " 'scroll-up)
- X(define-key jnews-mode-map "*" 'jnews-unflag)
- X(define-key jnews-mode-map "r" 'jnews-flag)
- X(define-key jnews-mode-map "\C-i" 'jnews-goto-unread)
- X(define-key jnews-mode-map "x" 'jnews-expunge-expired)
- X(define-key jnews-mode-map "u" 'jnews-update)
- X(define-key jnews-mode-map "q" 'jnews-back-to-menu)
- X(define-key jnews-mode-map "p" 'backward-line)
- X(define-key jnews-mode-map "n" 'forward-line)
- X(define-key jnews-mode-map "h" 'describe-local-map)
- X(define-key jnews-mode-map "e" 'jnews-read-article)
- X(define-key jnews-mode-map "a" 'jnews-add-newsgroup)
- X
- X
- X;--------------------The following commands are used in article mode-----------
- X
- X(defun jnews-next-article (arg)
- X "Delete the current article and look at the next one"
- X (interactive "p")
- X (let* ((newsgroup jnews-current-newsgroup)
- X (summary-window (get-buffer-window newsgroup)))
- X (kill-buffer (current-buffer))
- X (if (null summary-window)
- X (set-buffer newsgroup)
- X (select-window summary-window))
- X (forward-line (- arg 1))
- X (jnews-read-article)))
- X
- X(defun jnews-previous-article (arg)
- X "Delete the current article and look at the previous one"
- X (interactive "p")
- X (jnews-next-article (- arg)))
- X
- X(defun jnews-rotate-article ()
- X "Caesar-rotate article"
- X (interactive)
- X (save-excursion
- X (goto-char (point-min))
- X (search-forward "\n\n")
- X (caesar-bounded-region (point) (point-max) 13)))
- X
- X(defun jnews-back-to-summary ()
- X "Kill this article's buffer and pop back into summary buffer."
- X (interactive)
- X (kill-buffer (current-buffer))
- X (pop-to-buffer (get-buffer jnews-current-newsgroup))
- X (if (looking-at "^$")
- X (jnews-back-to-menu)))
- X
- X(defun jnews-mark-article ()
- X "Mark the text of the current article"
- X (interactive)
- X (goto-char (point-min))
- X (search-forward "\n\n")
- X (set-mark (point-max)))
- X
- X(defun jnews-article-mode (newsgroup)
- X "Mode for browsing through newsgroup articles."
- X (kill-all-local-variables)
- X (setq major-mode 'jnews-article-mode)
- X (setq mode-name "Article")
- X (use-local-map jnews-article-mode-map)
- X (setq mode-line-buffer-identification '("Jnews: %17b"))
- X (setq jnews-current-newsgroup newsgroup)
- X (run-hooks 'jnews-article-hook))
- X
- X; Set up the jnews keymap when jnews.el is loaded
- X(define-key jnews-article-mode-map "?" 'summarize-local-map)
- X(define-key jnews-article-mode-map "b" 'scroll-down)
- X(define-key jnews-article-mode-map " " 'scroll-up)
- X(define-key jnews-article-mode-map "." 'beginning-of-buffer)
- X(define-key jnews-article-mode-map "\C-c\C-c" 'jnews-rotate-article)
- X(define-key jnews-article-mode-map "q" 'jnews-back-to-summary)
- X(define-key jnews-article-mode-map "p" 'jnews-previous-article)
- X(define-key jnews-article-mode-map "n" 'jnews-next-article)
- X(define-key jnews-article-mode-map "o" 'news-save-item-in-file)
- X(define-key jnews-article-mode-map "\C-o" 'rmail-output)
- X(define-key jnews-article-mode-map "r" 'news-mail-reply)
- X(define-key jnews-article-mode-map "f" 'news-reply)
- X(define-key jnews-article-mode-map "a" 'news-post-news)
- X(define-key jnews-article-mode-map "m" 'news-mail-other-window)
- X(define-key jnews-article-mode-map "M" 'jnews-mark-article)
- X(define-key jnews-article-mode-map "h" 'describe-local-map)
- END_OF_FILE
- if test 15915 -ne `wc -c <'jnews.el'`; then
- echo shar: \"'jnews.el'\" unpacked with wrong size!
- fi
- # end of 'jnews.el'
- fi
- if test -f 'newsrc_to_jnews' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'newsrc_to_jnews'\"
- else
- echo shar: Extracting \"'newsrc_to_jnews'\" \(307 characters\)
- sed "s/^X//" >'newsrc_to_jnews' <<'END_OF_FILE'
- X#! /bin/csh
- X#
- X# To use jnews.el
- X#
- X# 1) Make a directory called "jnews" under your home
- X# 2) cd to "jnews"
- X# 3) execute this script
- X#
- X# You are now ready to use jnews.
- X
- Xforeach FF (`grep ":" < $HOME/.newsrc | sed 's/:[ \t]*[-0-9]*[ \t]*$//'`)
- X echo 'Converting ' $FF
- X jnews $FF
- Xend
- Xexit
- END_OF_FILE
- if test 307 -ne `wc -c <'newsrc_to_jnews'`; then
- echo shar: \"'newsrc_to_jnews'\" unpacked with wrong size!
- fi
- # end of 'newsrc_to_jnews'
- fi
- if test -f 'jnews.original' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'jnews.original'\"
- else
- echo shar: Extracting \"'jnews.original'\" \(13757 characters\)
- sed "s/^X//" >'jnews.original' <<'END_OF_FILE'
- X; @(#)jnews.el 1.1 2/14/90
- X;[Insert Gnu Copyleft here]
- X;
- X;Jnews is Yet Another News Reading Package. Its philosophy follows that of
- X;DIRED. Users read news by DIREDing their jnews subdirectory, which contains
- X;summary files for each interesting newsgroup. When a user edits a summary
- X;file, jnews-mode is invoked. The summary file is composed of line line
- X;summaries of each article, indicating the article number, date, author,
- X;subject, and length. Users move to the article they want using standard Emacs
- X;keys, and hit 'e' to edit that message.
- X;
- X;A summary file is created by jnews, a C program that efficiently scans the
- X;usenet directory, which is in the /usr/spool/news hierarchy. It does this
- X;incrementally so that when new articles arrive, the old article summary
- X;lines are not recalculated. This happens very fast, unless you are building
- X;a new summary file for a large newsgroup. The speed for compiling a new
- X;summary is approximately 12 articles/second on an unloaded Sun 3/60 running
- X;SunOS 3.5, accessing news spool files on a Sun 386i running SunOS 4.01
- X;using NFS on an idle ethernet.
- X;
- X;It might be preferable to make the jnews program part of the Emacs executable,
- X;rather than doing a call-process, but that would make it harder for people
- X;to try out. If the FSF people want to integrate it in to Emacs, it shouldn't
- X;be difficult. Of course, everything that the jnews program does can be done
- X;in Emacs lisp, but not without reading in all of each article. Jnews only
- X;has to scan the first few lines to look at the header information. Then it
- X;closes the file and goes on to the next one.
- X;
- X; Written by Joshua Marantz, Viewlogic Systems Inc. February, 1989.
- X
- X;----------------------Jnews Global Variables---------------------------------
- X
- X(require 'mlsupport)
- X(load "mlconvert")
- X
- X(defvar jnews-program (expand-file-name "~kim/bin/jnews")
- X "Pathname of jnews summary building program.")
- X
- X(defvar jnews-summary-directory (concat (getenv "HOME") "/jnews/")
- X "Directory in which to save newsgroup headers.")
- X
- X(defvar jnews-mode-map (make-sparse-keymap)
- X "Local keymap for jnews summaries.")
- X
- X(defvar jnews-article-mode-map (make-sparse-keymap)
- X "Local keymap for jnews articles.")
- X
- X(defvar jnews-current-newsgroup nil
- X "Name of buffer containing current article's newsgroup summary")
- X(make-variable-buffer-local 'jnews-current-newsgroup)
- X
- X;-----------------------Utility functions of general use-----------------------
- X
- X(defun describe-local-map ()
- X "Print help on each key that is bound in the current mode map"
- X (interactive)
- X (let ((help-keys (cdr (current-local-map))))
- X (pop-to-buffer "*Help*")
- X (erase-buffer)
- X (mapcar '(lambda (key-fun)
- X (insert-char (car key-fun) 1)
- X (indent-to-column 3)
- X (insert (prin1-to-string (cdr key-fun)))
- X (indent-to-column 26)
- X (save-excursion (insert (documentation (cdr key-fun))))
- X (move-to-column (- (screen-width) 1))
- X (delete-region (point) (point-max))
- X (insert "\n"))
- X help-keys)))
- X
- X(defun summarize-local-map ()
- X "Print one line message in minibuffer summarizing current local map"
- X (interactive)
- X (let ((help-keys (cdr (current-local-map))))
- X (save-excursion
- X (switch-to-buffer "*Help*")
- X (erase-buffer)
- X (mapcar '(lambda (key-fun)
- X (insert (char-to-string (car key-fun)) ":"
- X (prin1-to-string (cdr key-fun)) " "))
- X help-keys)
- X (message (buffer-substring (point-min) (point-max))))))
- X
- X(defun save-and-kill ()
- X "Save this buffer with no backup and kill it"
- X (interactive)
- X (save-buffer 0)
- X (kill-buffer (current-buffer)))
- X
- X(defun kill-current-buffer ()
- X "Kill the current buffer"
- X (interactive)
- X (kill-buffer (current-buffer)))
- X
- X(defun backward-line (arg)
- X "Go to beginning of previous line"
- X (interactive "p")
- X (forward-line (- arg)))
- X
- X; These next two functions were stolen from rnews.el
- X(defun string-subst-char (new old string)
- X "Substitute character NEW for character OLD in STRING"
- X (let (index)
- X (setq old (regexp-quote (char-to-string old))
- X string (substring string 0))
- X (while (setq index (string-match old string))
- X (aset string index new)))
- X string)
- X
- X;;; caesar-region written by phr@prep.ai.mit.edu Nov 86
- X;;; modified by tower@prep Nov 86, josh@vx Jan 89
- X(defun caesar-bounded-region (bound1 bound2 &optional n)
- X "Caesar rotation of region defined by BOUND1 and BOUND2 by N (default 13)"
- X (interactive "dmp")
- X (let ((rot (cond ((or (null current-prefix-arg)
- X (not (numberp n))
- X (= (% n 26) 0))
- X 13)
- X ((< n 0) (- 26 (% (- n) 26)))
- X (t (% n 26)))))
- X (if (or (not (boundp 'caesar-translate-table))
- X (/= (aref caesar-translate-table ?a) (+ ?a rot)))
- X (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
- X (message "Building caesar-translate-table...")
- X (setq caesar-translate-table (make-vector 256 0))
- X (while (< i 256)
- X (aset caesar-translate-table i i)
- X (setq i (1+ i)))
- X (setq lower (concat lower lower) upper (upcase lower) i 0)
- X (while (< i 26)
- X (aset caesar-translate-table (+ ?a i) (aref lower (+ i rot)))
- X (aset caesar-translate-table (+ ?A i) (aref upper (+ i rot)))
- X (setq i (1+ i)))
- X (message "Building caesar-translate-table... done")))
- X (let* ((from (min bound1 bound2))
- X (to (max bound1 bound2))
- X (i 0)
- X (str (buffer-substring from to))
- X (len (length str))
- X (buffer-read-only nil))
- X (while (< i len)
- X (aset str i (aref caesar-translate-table (aref str i)))
- X (setq i (1+ i)))
- X (goto-char from)
- X (kill-region from to)
- X (insert str))))
- X
- X;-------------------------Jnews Summary mode Functions------------------------
- X
- X(defun jnews ()
- X "Start a Jnews session"
- X (interactive)
- X (dired (concat (getenv "HOME") "/jnews")))
- X
- X(defun jnews-read-article ()
- X "Read the article referred to on the current line"
- X (interactive)
- X (let* ((newsgroup (file-name-nondirectory buffer-file-name))
- X (news-path (concat news-path (string-subst-char ?/ ?. newsgroup) "/"))
- X (message-number
- X (save-excursion
- X (beginning-of-line)
- X (if (= (point) (point-min)) (error "No more articles"))
- X (forward-char 1)
- X (re-search-forward "[ ]*")
- X (let ((msgno-start (point)))
- X (search-forward "|")
- X (buffer-substring msgno-start (- (point) 1))))))
- X (if (not (file-exists-p (concat news-path message-number)))
- X (progn
- X (jnews-expired)
- X (message "Article has expired!"))
- X (progn
- X (jnews-flag)
- X (pop-to-buffer (concat newsgroup ":" message-number))
- X (insert-file (concat news-path message-number))
- X (goto-char (point-min))
- X (setq buffer-read-only t)
- X (message "")
- X (jnews-article-mode newsgroup)))))
- X
- X(defun jnews-article-has-expired ()
- X (let* ((newsgroup (file-name-nondirectory buffer-file-name))
- X (news-path (concat news-path (string-subst-char ?/ ?. newsgroup)
- X "/"))
- X (message-number
- X (save-excursion
- X (beginning-of-line)
- X (if (= (point) (point-min)) (error "No more articles"))
- X (forward-char 1)
- X (re-search-forward "[ ]*")
- X (let ((msgno-start (point)))
- X (search-forward "|")
- X (buffer-substring msgno-start (- (point) 1))))))
- X (not (file-exists-p (concat news-path message-number)))))
- X
- X(defun jnews-goto-unread ()
- X "Move point to the first unseen article"
- X (interactive)
- X (search-forward "\n*")
- X (beginning-of-line)
- X (while (jnews-article-has-expired)
- X (jnews-expired)))
- X
- X(defun jnews-update ()
- X "Run a C program to look for new articles in the current newsgroup"
- X (interactive)
- X (save-buffer 0)
- X (message "Updating %s..." (file-name-nondirectory buffer-file-name))
- X (call-process jnews-program
- X nil nil nil
- X (file-name-nondirectory buffer-file-name)
- X news-path)
- X (revert-buffer t t)
- X (setq buffer-read-only t)
- X (message "Updating %s...done" (file-name-nondirectory buffer-file-name)))
- X
- X(defun jnews-add-newsgroup (newsgroup)
- X "Add a new newsgroup"
- X (interactive "sNewsgroup: ")
- X (find-file (concat jnews-summary-directory newsgroup))
- X (jnews-mode))
- X
- X(defun jnews-flag ()
- X "Flag the current message as previously read"
- X (interactive)
- X (ml-prefix-argument-loop
- X (beginning-of-line)
- X (if (= (following-char) ?*)
- X (let ((buffer-read-only nil))
- X (delete-char 1)
- X (insert-char ? 1)))
- X (forward-line 1)))
- X
- X(defun jnews-expired ()
- X "Flag the current message as previously read"
- X (interactive)
- X (ml-prefix-argument-loop
- X (beginning-of-line)
- X (if (not (= (following-char) ?#))
- X (let ((buffer-read-only nil))
- X (delete-char 1)
- X (insert-char ?# 1)))
- X (forward-line 1)))
- X
- X; Do a binary search for the boundary of expired messages!!
- X(defun jnews-expunge-expired ()
- X "Expunge all expired messages"
- X (interactive)
- X (goto-char (point-min))
- X (forward-line 1)
- X (let ((buffer-read-only nil)
- X (begin-deletion (point))
- X (low (point))
- X (high (point-max)))
- X
- X ; Get close with a binary search
- X (while (> (- high low) 80)
- X (goto-char (/ (+ low high) 2))
- X (beginning-of-line)
- X (if (jnews-article-has-expired)
- X (setq low (point))
- X (setq high (point))))
- X
- X ; Make sure we have not missed with linear searches
- X (while (and (> (point) (point-min))
- X (not (jnews-article-has-expired)))
- X (forward-line -1))
- X (while (and (> (point) (point-min))
- X (< (point) (point-max))
- X (jnews-article-has-expired))
- X (forward-line 1))
- X (if (> (point) (point-min))
- X (delete-region begin-deletion (point)))))
- X
- X(defun jnews-unflag ()
- X "Unflag current message as if it had never been read"
- X (interactive)
- X (ml-prefix-argument-loop
- X (beginning-of-line)
- X (if (= (following-char) ? )
- X (let ((buffer-read-only nil))
- X (delete-char 1)
- X (insert-char ?* 1)))
- X (forward-line 1)))
- X
- X;; Jnews mode is suitable only for specially formatted data.
- X(put 'jnews-mode 'mode-class 'special)
- X
- X(defun jnews-mode ()
- X "Mode for browsing through newsgroup summaries."
- X (interactive)
- X (if (not (eq major-mode 'jnews-mode))
- X (progn
- X (kill-all-local-variables)
- X (setq major-mode 'jnews-mode)
- X (setq mode-name "Summaries")
- X (use-local-map jnews-mode-map)
- X (setq mode-line-buffer-identification '("Jnews: %17b"))
- X (jnews-update)
- X (run-hooks 'jnews-mode-hook))))
- X
- X; Set up the jnews keymap when jnews.el is loaded
- X(define-key jnews-mode-map "?" 'summarize-local-map)
- X(define-key jnews-mode-map "\^?" 'scroll-down)
- X(define-key jnews-mode-map " " 'scroll-up)
- X(define-key jnews-mode-map "*" 'jnews-unflag)
- X(define-key jnews-mode-map "\^M" 'jnews-flag)
- X(define-key jnews-mode-map "\^I" 'jnews-goto-unread)
- X(define-key jnews-mode-map "x" 'jnews-expunge-expired)
- X(define-key jnews-mode-map "u" 'jnews-update)
- X(define-key jnews-mode-map "q" 'save-and-kill)
- X(define-key jnews-mode-map "p" 'backward-line)
- X(define-key jnews-mode-map "n" 'forward-line)
- X(define-key jnews-mode-map "h" 'describe-local-map)
- X(define-key jnews-mode-map "e" 'jnews-read-article)
- X
- X;(global-set-key "\^X\^J" 'jnews-add-newsgroup)
- X;(global-set-key "\^X\j" 'jnews)
- X
- X;--------------------The following commands are used in article mode-----------
- X
- X(defun jnews-next-article (arg)
- X "Delete the current article and look at the next one"
- X (interactive "p")
- X (let* ((newsgroup jnews-current-newsgroup)
- X (summary-window (get-buffer-window newsgroup)))
- X (kill-buffer (current-buffer))
- X (if (null summary-window)
- X (set-buffer newsgroup)
- X (select-window summary-window))
- X (forward-line (- arg 1))
- X (jnews-read-article)))
- X
- X(defun jnews-previous-article (arg)
- X "Delete the current article and look at the previous one"
- X (interactive "p")
- X (jnews-next-article (- arg)))
- X
- X(defun jnews-rotate-article ()
- X "Caesar-rotate article"
- X (interactive)
- X (save-excursion
- X (goto-char (point-min))
- X (search-forward "\n\n")
- X (caesar-bounded-region (point) (point-max) 13)))
- X
- X(defun jnews-mark-article ()
- X "Mark the text of the current article"
- X (interactive)
- X (goto-char (point-min))
- X (search-forward "\n\n")
- X (set-mark (point-max)))
- X
- X(defun jnews-article-mode (newsgroup)
- X "Mode for browsing through newsgroup articles."
- X (kill-all-local-variables)
- X (setq major-mode 'jnews-article-mode)
- X (setq mode-name "Article")
- X (use-local-map jnews-article-mode-map)
- X (setq mode-line-buffer-identification '("Jnews: %17b"))
- X (setq jnews-current-newsgroup newsgroup)
- X (run-hooks 'jnews-article-hook))
- X
- X; Set up the jnews keymap when jnews.el is loaded
- X(define-key jnews-article-mode-map "?" 'summarize-local-map)
- X(define-key jnews-article-mode-map "\^?" 'scroll-down)
- X(define-key jnews-article-mode-map " " 'scroll-up)
- X(define-key jnews-article-mode-map "." 'beginning-of-buffer)
- X(define-key jnews-article-mode-map "r" 'jnews-rotate-article)
- X(define-key jnews-article-mode-map "q" 'kill-current-buffer)
- X(define-key jnews-article-mode-map "\^I" 'kill-current-buffer)
- X(define-key jnews-article-mode-map "p" 'jnews-previous-article)
- X(define-key jnews-article-mode-map "n" 'jnews-next-article)
- X(define-key jnews-article-mode-map "m" 'jnews-mark-article)
- X(define-key jnews-article-mode-map "h" 'describe-local-map)
- END_OF_FILE
- if test 13757 -ne `wc -c <'jnews.original'`; then
- echo shar: \"'jnews.original'\" unpacked with wrong size!
- fi
- # end of 'jnews.original'
- fi
- echo shar: End of shell archive.
- exit 0
-
-
- --
- Kim Letkeman uunet!mitel!spock!kim
-
-
-