home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / interfaces / jnews.shar / text0000.txt < prev   
Encoding:
Text File  |  1990-07-22  |  44.1 KB  |  1,250 lines

  1. Hi,
  2.  
  3. Josh Marantz posted a very small news reader called jnews a few weeks
  4. ago. I decided to try it and found that I really liked it, enough to
  5. enhance it to the point that it is now easy to install and confortable
  6. to use.
  7.  
  8. It provides a very nice summary mode that allows the user a different
  9. sort of view of the "big picture". I find that I am much more aware of
  10. volumes and of subject flows that previously because I am always
  11. looking at a comprehensive list of the messages in the newsgroup.
  12.  
  13. It's certainly not for everyone as it lacks power (e.g. kill files,
  14. follow thread), but it is a good alternative to RNEWS. It is now
  15. possible to follow up, reply, save messages to files and mail folders
  16. and do all those other things that we take for granted in other
  17. readers.
  18.  
  19. Following is a shar file that contains 5 files:
  20.  
  21. 1) jnews.doc - Josh's text at the front of his original posting. I
  22.    have added an addendum which describes my changes to jnews.el and
  23.    gives a more complete set of installation instructions including
  24.    the use of the new conversion script for .newsrc to jnews.
  25.  
  26. 2) jnews.c - the course for jnews's news directory scanner. This
  27.    program runs when you install jnews and then each time you enter a
  28.    news group. It builds the summary file that jnews uses to track
  29.    messages that you have read.
  30.  
  31. 3) jnews.el - the emacs lisp file that contains jnews mode in its
  32.    entirety. Really small. Note that this is my version.
  33.  
  34. 4) newsrc_to_jnews - a small script that will scan your .newsrc file
  35.    and take all newsgroups to which your are subscribed and create
  36.    jnews summary files. You must be in your ~/jnews directory when you
  37.    run it.
  38.  
  39. 5) jnews.original - Josh's original version of jnews.el.
  40.  
  41. For those who already use jnews, here is a summary of changes:
  42.  
  43.    - installation script to create initial summary files.
  44.  
  45.    - when quitting either an article or a summary, the user is popped
  46.      back to the buffer at the previous level. If there are no more
  47.      articles, the user is popped all the way from the last article to
  48.      the top level.
  49.  
  50.    - hooked up the ability to save an article into a file or a mail
  51.      folder.
  52.  
  53.    - hooked up the stuff to allow mailing a message, following up to
  54.      usenet, or posting a new message.
  55.  
  56.    - changed jnews-goto-unread to pop right into the article which
  57.      saves typing and time.
  58.  
  59. Kim
  60. ------------------cut here--------------------
  61. #! /bin/sh
  62. # This is a shell archive.  Remove anything before this line, then unpack
  63. # it by saving it into a file and typing "sh file".  To overwrite existing
  64. # files, type "sh file -c".  You can also feed this as standard input via
  65. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  66. # will see the following message at the end:
  67. #        "End of shell archive."
  68. # Contents:  jnews.doc jnews.c jnews.el newsrc_to_jnews jnews.original
  69. # Wrapped by kim@kim on Fri May 18 12:49:11 1990
  70. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  71. if test -f 'jnews.doc' -a "${1}" != "-c" ; then 
  72.   echo shar: Will not clobber existing file \"'jnews.doc'\"
  73. else
  74. echo shar: Extracting \"'jnews.doc'\" \(3737 characters\)
  75. sed "s/^X//" >'jnews.doc' <<'END_OF_FILE'
  76. XFrom: josh@viewlog.UUCP (Josh Marantz)
  77. XNewsgroups: comp.emacs
  78. XSubject: Jnews: Yet Another News Reading Package for Emacs
  79. XDate: 26 Apr 90 17:04:36 GMT
  80. XOrganization: Viewlogic Systems Inc., Marlboro, MA
  81. X
  82. XOriginal Documentation by Josh Marantz
  83. X
  84. XI'm sure people have had it up to their modelines with news packages,
  85. Xbut you may really like this one.  Why?  Because its small, fast, and
  86. Xfairly powerful given its small set of features.  We've been using
  87. Xit here at Viewlogic for a while, and it definitely makes wading through
  88. Xreems of high-volume newsgroups much quicker.
  89. X
  90. XIt is used on newsgroups like Dired is used on files.  In fact, for
  91. X"select newsgroup mode", it uses Dired on the directory ~/jnews, which
  92. Xcontains one file for each newsgroup you read.  You select which
  93. Xnewsgroup you want by editing the file for it (e.g.
  94. X~/jnews/comp.emacs).  The file is maintained with the first line as
  95. X-*-jnews-*-, so it goes into jnews-mode when you edit the file.
  96. X
  97. XWhen editing a jnews file, each message is displayed on one line,
  98. Xindicating date, author, subject, number of lines, and message number.
  99. XYou move the cursor to a message, and hit "e" to see it, hit
  100. XCarriage-Return to bypass it while marking it as read, and hit "*" to
  101. Xmark it as unread, even if you've seen it.  Expired messages are
  102. Xmarked with '#', unread messages are marked with '*'.  Hitting tab
  103. Xgets you to the next unexpired message.  Hitting 'x' gets rid of all
  104. Xexpired messages.  Hitting 'q' kills the buffer.
  105. X
  106. XHelp is available with '?' and 'h'.
  107. X
  108. XThe news-package works with a small elisp file and a small auxiliary C
  109. Xprogram.  The C program is not linked into Emacs.  It's run with call-process.
  110. XIt's used to build the summary file, which can be done faster in C than
  111. Xin Elisp because the C program doesn't have to scan the whole message,
  112. Xjust the first few lines.
  113. X
  114. XJnews will probably not work with nntp.  Jnews assumes you have your news
  115. Xin a directory tree, off of /usr/spool/news.  A small Elisp modification
  116. Xwould allow it to pass an alternate news-root to the jnews C program.
  117. X
  118. XYou must do "mkdir ~/jnews" manually before running jnews.
  119. X
  120. XAnyway, I apologize for not doing a super packaging job, but people around
  121. Xhere have been urging me to publicize this, so maybe you'll think its
  122. Xworth it.  Here's some global bindings for your .emacs, followed by
  123. Xjnews.c and jnews.el.  Enjoy!
  124. X
  125. XAddendum by Kim Letkeman
  126. X
  127. XI have added some functionality to jnews.el and a script that will
  128. Xtake your .newsrc and create the summary files in your jnews
  129. Xdirectory. It is still very small and fast and is somewhat more
  130. Xconvenient now to use and especially to get started. Kudos to Josh for
  131. Xa nice idea and a nice implementation.
  132. X
  133. XMy changes to jnews.el are:
  134. X
  135. X- when quitting either an article or a summary, the user is
  136. X  popped back to the buffer at the previous level. If there
  137. X  are no more articles, the user is popped all the way from
  138. X  the last article to the top level.
  139. X
  140. X- hooked up the ability to save an article into a file or a
  141. X  mail message, much like RNEWS.
  142. X
  143. X- hooked up the stuff to allow mailing a message, following up
  144. X  to usenet, or posting a new message.
  145. X
  146. X- changed jnews-goto-unread to pop right into the article which
  147. X  saves typing and time.
  148. X
  149. XTo install and run jnews:
  150. X
  151. X- compile jnews.c and put "jnews" in a directory on your path. 
  152. X  "cc -o jnews jnews.c"
  153. X
  154. X- byte compile jnews.el and put jnews.elc in your local lisp
  155. X  directory
  156. X
  157. X- create ~/jnews, cd to it, and execute newsrc_to_jnews which 
  158. X  will create your jnews summary files
  159. X
  160. X- add the following 2 lines to your .emacs file (and bind keys to 
  161. X  the commands if you so choose.)
  162. X
  163. X(autoload 'jnews "jnews" "Read usenet news" t)
  164. X(autoload 'jnews-add-newsgroup "jnews" "Add new usenet newsgroup" t)
  165. X
  166. XKim
  167. X    
  168. END_OF_FILE
  169. if test 3737 -ne `wc -c <'jnews.doc'`; then
  170.     echo shar: \"'jnews.doc'\" unpacked with wrong size!
  171. fi
  172. # end of 'jnews.doc'
  173. fi
  174. if test -f 'jnews.c' -a "${1}" != "-c" ; then 
  175.   echo shar: Will not clobber existing file \"'jnews.c'\"
  176. else
  177. echo shar: Extracting \"'jnews.c'\" \(5495 characters\)
  178. sed "s/^X//" >'jnews.c' <<'END_OF_FILE'
  179. X
  180. X/* @(#)jnews.c    1.2 2/14/90 */
  181. X
  182. X/* Written by Joshua Marantz, Viewlogic Systems Inc, February 1990 */
  183. X
  184. X#include <stdio.h>
  185. X#include <sys/types.h>
  186. X#include <sys/dir.h>
  187. X
  188. XFILE *headerfile;
  189. X
  190. Xmain(argc, argv)
  191. X    int argc;
  192. X    char *argv[];
  193. X{
  194. X    int article, last_header, last_article, first_article;
  195. X    char dir[256];
  196. X    
  197. X    if ((argc != 2) && (argc != 3)) {
  198. X        fprintf (stderr, "usage: %s news.group [newsgroup-directory]\n",
  199. X                 argv[0]);
  200. X        exit (1);
  201. X    }
  202. X
  203. X    /* If the news group directory was not specified, figure it out */
  204. X    get_newsgroup_directory (argv[1],
  205. X                             (argc == 2) ? "/usr/spool/news" : argv[2],
  206. X                             dir);
  207. X
  208. X    /* Find the first and last articles in the directory */
  209. X    max_articles (dir, &first_article, &last_article);
  210. X
  211. X    /* Open the header file, and find the number of the last article in it */
  212. X    last_header = open_and_scan_headerfile (argv[1]);
  213. X
  214. X    /* Add in the articles that are yet in the header file */
  215. X    if (first_article < last_header + 1)
  216. X        first_article = last_header + 1;
  217. X    for (article = first_article; article <= last_article; article++)
  218. X        add_to_headerfile (article, dir);
  219. X
  220. X    fclose (headerfile);
  221. X}
  222. X
  223. Xget_newsgroup_directory(newsname, root, dir)
  224. X    char *newsname, *root, *dir;
  225. X{
  226. X    int i;
  227. X    sprintf (dir, "%s/%s", root, newsname);
  228. X    for (i = strlen (root); dir[i] != '\0'; i++)
  229. X        if (dir[i] == '.')
  230. X            dir[i] = '/';
  231. X}
  232. X            
  233. Xmax_articles(dirname, first, last)
  234. X    char *dirname;
  235. X    int *first, *last;
  236. X{
  237. X    int i, j, article;
  238. X    DIR *dir;
  239. X    struct direct *readdir (), *dp;
  240. X
  241. X    if ((dir = opendir (dirname)) == NULL) {
  242. X        perror (dir);
  243. X        exit (1);
  244. X    }
  245. X
  246. X    *first = 32767;
  247. X    *last = 0;
  248. X
  249. X    for (dp = readdir (dir); dp != NULL; dp = readdir (dir)) {
  250. X        if ((sscanf (dp -> d_name, "%d", &article) == 1) &&
  251. X            (article > 0))
  252. X        {
  253. X            if (article > *last)
  254. X                *last = article;
  255. X            if (article < *first)
  256. X                *first = article;
  257. X        }
  258. X    }
  259. X    closedir (dir);
  260. X}
  261. X
  262. Xopen_and_scan_headerfile(headername)
  263. X    char *headername;
  264. X{
  265. X    char buf[256];
  266. X    char *jnews_signature = "-*-jnews-*-\n";
  267. X    int article_number = 0;
  268. X
  269. X    headerfile = fopen (headername, "r+");
  270. X    if (headerfile == NULL) {
  271. X        headerfile = fopen (headername, "w");
  272. X        if (headerfile == NULL) {
  273. X            perror (headername);
  274. X            exit (1);
  275. X        }
  276. X        fputs (jnews_signature, headerfile);
  277. X    }
  278. X
  279. X    /* If the file exists, make sure its got the signature! */
  280. X    else if ((fgets (buf, 255, headerfile) == NULL) ||
  281. X             (strcmp (buf, jnews_signature) != 0))
  282. X    {
  283. X        fprintf (stderr, "Corrupt jnews header file: %s != %s\n",
  284. X                 buf, jnews_signature);
  285. X        fclose (headerfile);
  286. X        exit (1);
  287. X    }
  288. X
  289. X    /* Jump to the last line of the file, which should be exactly 80
  290. X       bytes from the end of the file. */
  291. X    else if (fseek (headerfile, -80L, 2) != 0) {
  292. X        fclose (headerfile);
  293. X        perror ("Could not seek to end-80 in jnews header file");
  294. X        exit (1);
  295. X    }
  296. X    else if (fgets (buf, 255, headerfile) == NULL) {
  297. X        fclose (headerfile);
  298. X        perror ("Could not read the last 80 bytes of jnews header file");
  299. X        exit (1);
  300. X    }
  301. X    else if ((sscanf (&buf[1], "%d|", &article_number) != 1) ||
  302. X             (article_number == 0))
  303. X    {
  304. X        fclose (headerfile);
  305. X        fprintf (stderr,
  306. X                 "Could not scan the last article number from line:\n%s",
  307. X                 buf);
  308. X        exit (1);
  309. X    }
  310. X    fseek (headerfile, 0, 2);
  311. X    return (article_number);
  312. X}
  313. X            
  314. Xadd_to_headerfile(article, dir)
  315. X    int article;
  316. X    char *dir;
  317. X{
  318. X    char buf[256], subject[80], date[80], lines[80], from[80], *author;
  319. X    char fname[256];
  320. X    FILE *f;
  321. X    int matches = 0;
  322. X    int i, len;
  323. X
  324. X    sprintf (fname, "%s/%d", dir, article);
  325. X    if ((f = fopen (fname, "r")) == NULL) {
  326. X        perror (fname);
  327. X        return;
  328. X    }
  329. X
  330. X    from[0] = subject[0] = date[0] = lines[0] = '\0';
  331. X
  332. X    while ((matches < 4) && (fgets (buf, 255, f) != NULL)) {
  333. X
  334. X        /* Chop off the last \n */
  335. X        len = strlen (buf);
  336. X        if ((len > 0) && (buf[len - 1] == '\n'))
  337. X            buf[len - 1] = '\0';
  338. X        
  339. X        /* Assume we will get a match and decrement if we do not! */
  340. X        matches++;
  341. X        if (strncmp (buf, "From: ", 6) == 0)
  342. X            strcpy (from, &buf[6]);
  343. X        else if (strncmp (buf, "Date: ", 6) == 0)
  344. X            strcpy (date, &buf[6]);
  345. X        else if (strncmp (buf, "Subject: ", 9) == 0)
  346. X            strcpy (subject, &buf[9]);
  347. X        else if (strncmp (buf, "Lines: ", 7) == 0)
  348. X            strcpy (lines, &buf[7]);
  349. X        else
  350. X            matches--;
  351. X    }
  352. X
  353. X    fclose (f);
  354. X
  355. X    /* Transform the subject to a human name if supplied. */
  356. X    if (((len = strlen (from)) == 0) || (from[len - 1] != ')'))
  357. X        author = from;
  358. X    else {
  359. X        /* Copy the string right justified inside parens */
  360. X        for (i = len - 1; (i >= 0) && (from[i] != '('); i--);
  361. X        if (i >= 0) {
  362. X            from[len - 1] = '\0';
  363. X            author = &from[i + 1];
  364. X        }
  365. X    }
  366. X
  367. X    date[9] = author[18] = subject[37] = lines[5] = '\0';
  368. X
  369. X    /* If the date takes only one digit, chop off the space at the end */
  370. X    if (date[strlen (date) - 1] == ' ')
  371. X        date[strlen (date) - 1] = '\0';
  372. X
  373. X    fprintf (headerfile, "*%5d|%37s|%9s|%18s|%5s\n",
  374. X             article, subject, date, author, lines);
  375. X}
  376. END_OF_FILE
  377. if test 5495 -ne `wc -c <'jnews.c'`; then
  378.     echo shar: \"'jnews.c'\" unpacked with wrong size!
  379. fi
  380. # end of 'jnews.c'
  381. fi
  382. if test -f 'jnews.el' -a "${1}" != "-c" ; then 
  383.   echo shar: Will not clobber existing file \"'jnews.el'\"
  384. else
  385. echo shar: Extracting \"'jnews.el'\" \(15915 characters\)
  386. sed "s/^X//" >'jnews.el' <<'END_OF_FILE'
  387. X; @(#)jnews.el    1.1 2/14/90
  388. X;[Insert Gnu Copyleft here]
  389. X;
  390. X;Jnews is Yet Another News Reading Package.  Its philosophy follows that of
  391. X;DIRED.  Users read news by DIREDing their jnews subdirectory, which contains
  392. X;summary files for each interesting newsgroup.  When a user edits a summary
  393. X;file, jnews-mode is invoked.  The summary file is composed of line line
  394. X;summaries of each article, indicating the article number, date, author,
  395. X;subject, and length.  Users move to the article they want using standard Emacs
  396. X;keys, and hit 'e' to edit that message.
  397. X;
  398. X;A summary file is created by jnews, a C program that efficiently scans the
  399. X;usenet directory, which is in the /usr/spool/news hierarchy.  It does this
  400. X;incrementally so that when new articles arrive, the old article summary
  401. X;lines are not recalculated.  This happens very fast, unless you are building
  402. X;a new summary file for a large newsgroup.  The speed for compiling a new
  403. X;summary is approximately 12 articles/second on an unloaded Sun 3/60 running
  404. X;SunOS 3.5, accessing news spool files on a Sun 386i running SunOS 4.01
  405. X;using NFS on an idle ethernet.
  406. X;
  407. X;It might be preferable to make the jnews program part of the Emacs executable,
  408. X;rather than doing a call-process, but that would make it harder for people
  409. X;to try out.  If the FSF people want to integrate it in to Emacs, it shouldn't
  410. X;be difficult.  Of course, everything that the jnews program does can be done
  411. X;in Emacs lisp, but not without reading in all of each article.  Jnews only
  412. X;has to scan the first few lines to look at the header information.  Then it
  413. X;closes the file and goes on to the next one.
  414. X;
  415. X; Written by Joshua Marantz, Viewlogic Systems Inc. February, 1989.
  416. X; Additions and changes by Kim Letkeman - May, 1990.
  417. X;    Changes include: 
  418. X;       - when quitting either an article or a summary, the user is
  419. X;         popped back to the buffer at the previous level. If there
  420. X;         are no more articles, the user is popped all the way from
  421. X;         the last article to the top level.
  422. X;       - hooked up the ability to save an article into a file or a
  423. X;         mail message, much like RNEWS.
  424. X;       - hooked up the stuff to allow mailing a message, following up
  425. X;         to usenet, or posting a new message.
  426. X;       - changed jnews-goto-unread to pop right into the article which
  427. X;         saves typing and time.
  428. X;                    
  429. X
  430. X;----------------------Jnews Global Variables---------------------------------
  431. X
  432. X(defvar jnews-program (expand-file-name "~kim/bin/jnews")
  433. X  "Pathname of jnews summary building program.")
  434. X
  435. X(defvar jnews-summary-directory (concat (getenv "HOME") "/jnews/")
  436. X  "Directory in which to save newsgroup headers.")
  437. X
  438. X(defvar jnews-mode-map (make-sparse-keymap)
  439. X  "Local keymap for jnews summaries.")
  440. X
  441. X(defvar jnews-article-mode-map (make-sparse-keymap)
  442. X  "Local keymap for jnews articles.")
  443. X
  444. X(defvar jnews-current-newsgroup nil
  445. X  "Name of buffer containing current article's newsgroup summary")
  446. X;(make-variable-buffer-local 'jnews-current-newsgroup)
  447. X
  448. X(autoload 'rmail-output "rmailout"
  449. X  "Save the current message into a mail folder."
  450. X  t)
  451. X
  452. X(autoload 'news-save-item-in-file "rnews"
  453. X  "Save the current message into a file."
  454. X  t)
  455. X
  456. X(autoload 'news-reply "rnewspost"
  457. X  "Compose and post a reply to the current article on USENET.
  458. XWhile composing the reply, use \\[mail-yank-original] to yank the original
  459. Xmessage into it."
  460. X  t)
  461. X
  462. X(autoload 'news-mail-other-window "rnewspost"
  463. X  "Send mail in another window.
  464. XWhile composing the message, use \\[mail-yank-original] to yank the
  465. Xoriginal message into it."
  466. X  t)
  467. X
  468. X(autoload 'news-post-news "rnewspost"
  469. X  "Begin editing a new USENET news article to be posted."
  470. X  t)
  471. X
  472. X(autoload 'news-mail-reply "rnewspost"
  473. X  "Mail a reply to the author of the current article.
  474. XWhile composing the reply, use \\[mail-yank-original] to yank the original
  475. Xmessage into it."
  476. X  t)
  477. X
  478. X(defvar rmail-last-file (expand-file-name "~/mbox.news"))
  479. X
  480. X
  481. X;-----------------------Utility functions of general use-----------------------
  482. X
  483. X(defun describe-local-map ()
  484. X  "Print help on each key that is bound in the current mode map"
  485. X  (interactive)
  486. X  (let ((help-keys (cdr (current-local-map))))
  487. X    (pop-to-buffer "*Help*")
  488. X    (erase-buffer)
  489. X    (mapcar '(lambda (key-fun)
  490. X               (insert-char (car key-fun) 1)
  491. X               (indent-to-column 3)
  492. X               (insert (prin1-to-string (cdr key-fun)))
  493. X               (indent-to-column 26)
  494. X               (save-excursion (insert (documentation (cdr key-fun))))
  495. X               (move-to-column (- (screen-width) 1))
  496. X               (delete-region (point) (point-max))
  497. X               (insert "\n"))
  498. X            help-keys)))
  499. X
  500. X(defun summarize-local-map ()
  501. X  "Print one line message in minibuffer summarizing current local map"
  502. X  (interactive)
  503. X  (let ((help-keys (cdr (current-local-map))))
  504. X    (save-excursion
  505. X      (switch-to-buffer "*Help*")
  506. X      (erase-buffer)
  507. X      (mapcar '(lambda (key-fun)
  508. X                 (insert (char-to-string (car key-fun)) ":"
  509. X                         (prin1-to-string (cdr key-fun)) " "))
  510. X              help-keys)
  511. X      (message (buffer-substring (point-min) (point-max))))))
  512. X
  513. X(defun save-and-kill ()
  514. X  "Save this buffer with no backup and kill it"
  515. X  (interactive)
  516. X  (save-buffer 0)
  517. X  (kill-buffer (current-buffer)))
  518. X
  519. X(defun kill-current-buffer ()
  520. X  "Kill the current buffer"
  521. X  (interactive)
  522. X  (kill-buffer (current-buffer)))
  523. X
  524. X(defun backward-line (arg)
  525. X  "Go to beginning of previous line"
  526. X  (interactive "p")
  527. X  (forward-line (- arg)))
  528. X
  529. X; These next two functions were stolen from rnews.el
  530. X(defun string-subst-char (new old string)
  531. X  "Substitute character NEW for character OLD in STRING"
  532. X  (let (index)
  533. X    (setq old (regexp-quote (char-to-string old))
  534. X      string (substring string 0))
  535. X    (while (setq index (string-match old string))
  536. X      (aset string index new)))
  537. X  string)
  538. X
  539. X;;; caesar-region written by phr@prep.ai.mit.edu  Nov 86
  540. X;;; modified by tower@prep Nov 86, josh@vx Jan 89
  541. X(defun caesar-bounded-region (bound1 bound2 &optional n)
  542. X  "Caesar rotation of region defined by BOUND1 and BOUND2 by N (default 13)"
  543. X  (interactive "dmp")
  544. X  (let ((rot (cond ((or (null current-prefix-arg)
  545. X                        (not (numberp n))
  546. X                        (= (% n 26) 0))
  547. X                    13)
  548. X                   ((< n 0) (- 26 (% (- n) 26)))
  549. X                   (t (% n 26)))))
  550. X    (if (or (not (boundp 'caesar-translate-table))
  551. X            (/= (aref caesar-translate-table ?a) (+ ?a rot)))
  552. X        (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
  553. X          (message "Building caesar-translate-table...")
  554. X          (setq caesar-translate-table (make-vector 256 0))
  555. X          (while (< i 256)
  556. X            (aset caesar-translate-table i i)
  557. X            (setq i (1+ i)))
  558. X          (setq lower (concat lower lower) upper (upcase lower) i 0)
  559. X          (while (< i 26)
  560. X            (aset caesar-translate-table (+ ?a i) (aref lower (+ i rot)))
  561. X            (aset caesar-translate-table (+ ?A i) (aref upper (+ i rot)))
  562. X            (setq i (1+ i)))
  563. X          (message "Building caesar-translate-table... done")))
  564. X    (let* ((from (min bound1 bound2))
  565. X           (to (max bound1 bound2))
  566. X           (i 0)
  567. X           (str (buffer-substring from to))
  568. X           (len (length str))
  569. X           (buffer-read-only nil))
  570. X      (while (< i len)
  571. X        (aset str i (aref caesar-translate-table (aref str i)))
  572. X        (setq i (1+ i)))
  573. X      (goto-char from)
  574. X      (kill-region from to)
  575. X      (insert str))))
  576. X
  577. X;-------------------------Jnews Summary mode Functions------------------------
  578. X
  579. X(defun jnews ()
  580. X  "Start a Jnews session"
  581. X  (interactive)
  582. X  (dired (concat (getenv "HOME") "/jnews")))
  583. X
  584. X(defun jnews-back-to-menu ()
  585. X  "Leave summary mode"
  586. X  (interactive)
  587. X  (save-and-kill)
  588. X  (pop-to-buffer (get-buffer "jnews")))
  589. X
  590. X(defun jnews-read-article ()
  591. X  "Read the article referred to on the current line"
  592. X  (interactive)
  593. X  (let* ((newsgroup (file-name-nondirectory buffer-file-name))
  594. X         (news-path (concat news-path (string-subst-char ?/ ?. newsgroup) "/"))
  595. X         (message-number
  596. X          (save-excursion
  597. X            (beginning-of-line)
  598. X            (if (= (point) (point-min)) (error "No more articles"))
  599. X            (forward-char 1)
  600. X            (re-search-forward "[ ]*")
  601. X            (let ((msgno-start (point)))
  602. X              (search-forward "|")
  603. X              (buffer-substring msgno-start (- (point) 1))))))
  604. X    (if (not (file-exists-p (concat news-path message-number)))
  605. X        (progn
  606. X          (jnews-expired)
  607. X          (message "Article has expired!"))
  608. X      (progn
  609. X        (jnews-flag)
  610. X        (pop-to-buffer (concat newsgroup ":" message-number))
  611. X        (insert-file (concat news-path message-number))
  612. X        (goto-char (point-min))
  613. X        (setq buffer-read-only t)
  614. X        (message "")
  615. X        (jnews-article-mode newsgroup)))))
  616. X
  617. X(defun jnews-article-has-expired ()
  618. X  (let* ((newsgroup (file-name-nondirectory buffer-file-name))
  619. X         (news-path (concat news-path (string-subst-char ?/ ?. newsgroup)
  620. X                            "/"))
  621. X         (message-number
  622. X          (save-excursion
  623. X            (beginning-of-line)
  624. X            (if (= (point) (point-min)) (error "No more articles"))
  625. X            (forward-char 1)
  626. X            (re-search-forward "[ ]*")
  627. X            (let ((msgno-start (point)))
  628. X              (search-forward "|")
  629. X              (buffer-substring msgno-start (- (point) 1))))))
  630. X    (not (file-exists-p (concat news-path message-number)))))
  631. X
  632. X(defun jnews-goto-unread ()
  633. X  "Move point to the first unseen article"
  634. X  (interactive)
  635. X  (search-forward "\n*" nil t)
  636. X  (beginning-of-line)
  637. X  (while (jnews-article-has-expired)
  638. X    (jnews-expired))
  639. X  (if (looking-at "^[*]")
  640. X      (jnews-read-article)))
  641. X
  642. X(defun jnews-update ()
  643. X  "Run a C program to look for new articles in the current newsgroup"
  644. X  (interactive)
  645. X  (save-buffer 0)
  646. X  (message "Updating %s..." (file-name-nondirectory buffer-file-name))
  647. X  (call-process jnews-program
  648. X                nil nil nil
  649. X                (file-name-nondirectory buffer-file-name)
  650. X                news-path)
  651. X  (revert-buffer t t)
  652. X  (setq buffer-read-only t)
  653. X  (message "Updating %s...done" (file-name-nondirectory buffer-file-name)))
  654. X
  655. X(defun jnews-add-newsgroup (newsgroup)
  656. X  "Add a new newsgroup"
  657. X  (interactive "sNewsgroup: ")
  658. X  (find-file (concat jnews-summary-directory newsgroup))
  659. X  (jnews-mode))
  660. X
  661. X(defun jnews-flag ()
  662. X  "Flag the current message as previously read"
  663. X  (interactive)
  664. X   (beginning-of-line)
  665. X   (if (= (following-char) ?*)
  666. X       (let ((buffer-read-only nil))
  667. X         (delete-char 1)
  668. X         (insert-char ?  1)))
  669. X   (forward-line 1))
  670. X
  671. X(defun jnews-expired ()
  672. X  "Flag the current message as previously read"
  673. X  (interactive)
  674. X   (beginning-of-line)
  675. X   (if (not (= (following-char) ?#))
  676. X       (let ((buffer-read-only nil))
  677. X         (delete-char 1)
  678. X         (insert-char ?# 1)))
  679. X   (forward-line 1))
  680. X
  681. X; Do a binary search for the boundary of expired messages!!
  682. X(defun jnews-expunge-expired ()
  683. X  "Expunge all expired messages"
  684. X  (interactive)
  685. X  (goto-char (point-min))
  686. X  (forward-line 1)
  687. X  (let ((buffer-read-only nil)
  688. X        (begin-deletion (point))
  689. X        (low (point))
  690. X        (high (point-max)))
  691. X
  692. X    ; Get close with a binary search
  693. X    (while (> (- high low) 80)
  694. X      (goto-char (/ (+ low high) 2))
  695. X      (beginning-of-line)
  696. X      (if (jnews-article-has-expired)
  697. X          (setq low (point))
  698. X        (setq high (point))))
  699. X
  700. X    ; Make sure we have not missed with linear searches
  701. X    (while (and (> (point) (point-min))
  702. X                (not (jnews-article-has-expired)))
  703. X      (forward-line -1))
  704. X    (while (and (> (point) (point-min))
  705. X                (< (point) (point-max))
  706. X                (jnews-article-has-expired))
  707. X      (forward-line 1))
  708. X    (if (> (point) (point-min))
  709. X        (delete-region begin-deletion (point)))))
  710. X
  711. X(defun jnews-unflag ()
  712. X  "Unflag current message as if it had never been read"
  713. X  (interactive)
  714. X   (beginning-of-line)
  715. X   (if (= (following-char) ? )
  716. X       (let ((buffer-read-only nil))
  717. X         (delete-char 1)
  718. X         (insert-char ?* 1)))
  719. X   (forward-line 1))
  720. X
  721. X;; Jnews mode is suitable only for specially formatted data.
  722. X(put 'jnews-mode 'mode-class 'special)
  723. X
  724. X(defun jnews-mode ()
  725. X  "Mode for browsing through newsgroup summaries."
  726. X  (interactive)
  727. X  (if (not (eq major-mode 'jnews-mode))
  728. X      (progn
  729. X          (kill-all-local-variables)    
  730. X          (setq major-mode 'jnews-mode)
  731. X          (setq mode-name "Summaries")
  732. X          (use-local-map jnews-mode-map)
  733. X          (setq mode-line-buffer-identification '("Jnews: %17b"))
  734. X          (jnews-update)
  735. X          (run-hooks 'jnews-mode-hook))))
  736. X
  737. X; Set up the jnews keymap when jnews.el is loaded
  738. X(define-key jnews-mode-map "?" 'summarize-local-map)
  739. X(define-key jnews-mode-map "b" 'scroll-down)
  740. X(define-key jnews-mode-map " " 'scroll-up)
  741. X(define-key jnews-mode-map "*" 'jnews-unflag)
  742. X(define-key jnews-mode-map "r" 'jnews-flag)
  743. X(define-key jnews-mode-map "\C-i" 'jnews-goto-unread)
  744. X(define-key jnews-mode-map "x" 'jnews-expunge-expired)
  745. X(define-key jnews-mode-map "u" 'jnews-update)
  746. X(define-key jnews-mode-map "q" 'jnews-back-to-menu)
  747. X(define-key jnews-mode-map "p" 'backward-line)
  748. X(define-key jnews-mode-map "n" 'forward-line)
  749. X(define-key jnews-mode-map "h" 'describe-local-map)
  750. X(define-key jnews-mode-map "e" 'jnews-read-article)
  751. X(define-key jnews-mode-map "a" 'jnews-add-newsgroup)
  752. X
  753. X
  754. X;--------------------The following commands are used in article mode-----------
  755. X
  756. X(defun jnews-next-article (arg)
  757. X  "Delete the current article and look at the next one"
  758. X  (interactive "p")
  759. X  (let* ((newsgroup jnews-current-newsgroup)
  760. X         (summary-window (get-buffer-window newsgroup)))
  761. X    (kill-buffer (current-buffer))
  762. X    (if (null summary-window)
  763. X        (set-buffer newsgroup)
  764. X      (select-window summary-window))
  765. X    (forward-line (- arg 1))
  766. X    (jnews-read-article)))
  767. X
  768. X(defun jnews-previous-article (arg)
  769. X  "Delete the current article and look at the previous one"
  770. X  (interactive "p")
  771. X  (jnews-next-article (- arg)))
  772. X
  773. X(defun jnews-rotate-article ()
  774. X  "Caesar-rotate article"
  775. X  (interactive)
  776. X  (save-excursion
  777. X    (goto-char (point-min))
  778. X    (search-forward "\n\n")
  779. X    (caesar-bounded-region (point) (point-max) 13)))
  780. X
  781. X(defun jnews-back-to-summary ()
  782. X  "Kill this article's buffer and pop back into summary buffer."
  783. X  (interactive)
  784. X  (kill-buffer (current-buffer))
  785. X  (pop-to-buffer (get-buffer jnews-current-newsgroup))
  786. X  (if (looking-at "^$")
  787. X      (jnews-back-to-menu)))
  788. X
  789. X(defun jnews-mark-article ()
  790. X  "Mark the text of the current article"
  791. X  (interactive)
  792. X  (goto-char (point-min))
  793. X  (search-forward "\n\n")
  794. X  (set-mark (point-max)))
  795. X
  796. X(defun jnews-article-mode (newsgroup)
  797. X  "Mode for browsing through newsgroup articles."
  798. X  (kill-all-local-variables)    
  799. X  (setq major-mode 'jnews-article-mode)
  800. X  (setq mode-name "Article")
  801. X  (use-local-map jnews-article-mode-map)
  802. X  (setq mode-line-buffer-identification '("Jnews: %17b"))
  803. X  (setq jnews-current-newsgroup newsgroup)
  804. X  (run-hooks 'jnews-article-hook))
  805. X
  806. X; Set up the jnews keymap when jnews.el is loaded
  807. X(define-key jnews-article-mode-map "?" 'summarize-local-map)
  808. X(define-key jnews-article-mode-map "b" 'scroll-down)
  809. X(define-key jnews-article-mode-map " " 'scroll-up)
  810. X(define-key jnews-article-mode-map "." 'beginning-of-buffer)
  811. X(define-key jnews-article-mode-map "\C-c\C-c" 'jnews-rotate-article)
  812. X(define-key jnews-article-mode-map "q" 'jnews-back-to-summary)
  813. X(define-key jnews-article-mode-map "p" 'jnews-previous-article)
  814. X(define-key jnews-article-mode-map "n" 'jnews-next-article)
  815. X(define-key jnews-article-mode-map "o" 'news-save-item-in-file)
  816. X(define-key jnews-article-mode-map "\C-o" 'rmail-output)
  817. X(define-key jnews-article-mode-map "r" 'news-mail-reply)
  818. X(define-key jnews-article-mode-map "f" 'news-reply)
  819. X(define-key jnews-article-mode-map "a" 'news-post-news)
  820. X(define-key jnews-article-mode-map "m" 'news-mail-other-window)
  821. X(define-key jnews-article-mode-map "M" 'jnews-mark-article)
  822. X(define-key jnews-article-mode-map "h" 'describe-local-map)
  823. END_OF_FILE
  824. if test 15915 -ne `wc -c <'jnews.el'`; then
  825.     echo shar: \"'jnews.el'\" unpacked with wrong size!
  826. fi
  827. # end of 'jnews.el'
  828. fi
  829. if test -f 'newsrc_to_jnews' -a "${1}" != "-c" ; then 
  830.   echo shar: Will not clobber existing file \"'newsrc_to_jnews'\"
  831. else
  832. echo shar: Extracting \"'newsrc_to_jnews'\" \(307 characters\)
  833. sed "s/^X//" >'newsrc_to_jnews' <<'END_OF_FILE'
  834. X#! /bin/csh
  835. X#
  836. X# To use jnews.el
  837. X#
  838. X#    1) Make a directory called "jnews" under your home
  839. X#    2) cd to "jnews"
  840. X#    3) execute this script
  841. X#    
  842. X#    You are now ready to use jnews.
  843. X
  844. Xforeach FF (`grep ":" < $HOME/.newsrc | sed  's/:[ \t]*[-0-9]*[ \t]*$//'`)
  845. X   echo 'Converting ' $FF
  846. X   jnews $FF
  847. Xend
  848. Xexit
  849. END_OF_FILE
  850. if test 307 -ne `wc -c <'newsrc_to_jnews'`; then
  851.     echo shar: \"'newsrc_to_jnews'\" unpacked with wrong size!
  852. fi
  853. # end of 'newsrc_to_jnews'
  854. fi
  855. if test -f 'jnews.original' -a "${1}" != "-c" ; then 
  856.   echo shar: Will not clobber existing file \"'jnews.original'\"
  857. else
  858. echo shar: Extracting \"'jnews.original'\" \(13757 characters\)
  859. sed "s/^X//" >'jnews.original' <<'END_OF_FILE'
  860. X; @(#)jnews.el    1.1 2/14/90
  861. X;[Insert Gnu Copyleft here]
  862. X;
  863. X;Jnews is Yet Another News Reading Package.  Its philosophy follows that of
  864. X;DIRED.  Users read news by DIREDing their jnews subdirectory, which contains
  865. X;summary files for each interesting newsgroup.  When a user edits a summary
  866. X;file, jnews-mode is invoked.  The summary file is composed of line line
  867. X;summaries of each article, indicating the article number, date, author,
  868. X;subject, and length.  Users move to the article they want using standard Emacs
  869. X;keys, and hit 'e' to edit that message.
  870. X;
  871. X;A summary file is created by jnews, a C program that efficiently scans the
  872. X;usenet directory, which is in the /usr/spool/news hierarchy.  It does this
  873. X;incrementally so that when new articles arrive, the old article summary
  874. X;lines are not recalculated.  This happens very fast, unless you are building
  875. X;a new summary file for a large newsgroup.  The speed for compiling a new
  876. X;summary is approximately 12 articles/second on an unloaded Sun 3/60 running
  877. X;SunOS 3.5, accessing news spool files on a Sun 386i running SunOS 4.01
  878. X;using NFS on an idle ethernet.
  879. X;
  880. X;It might be preferable to make the jnews program part of the Emacs executable,
  881. X;rather than doing a call-process, but that would make it harder for people
  882. X;to try out.  If the FSF people want to integrate it in to Emacs, it shouldn't
  883. X;be difficult.  Of course, everything that the jnews program does can be done
  884. X;in Emacs lisp, but not without reading in all of each article.  Jnews only
  885. X;has to scan the first few lines to look at the header information.  Then it
  886. X;closes the file and goes on to the next one.
  887. X;
  888. X; Written by Joshua Marantz, Viewlogic Systems Inc. February, 1989.
  889. X
  890. X;----------------------Jnews Global Variables---------------------------------
  891. X
  892. X(require 'mlsupport)
  893. X(load "mlconvert")
  894. X
  895. X(defvar jnews-program (expand-file-name "~kim/bin/jnews")
  896. X  "Pathname of jnews summary building program.")
  897. X
  898. X(defvar jnews-summary-directory (concat (getenv "HOME") "/jnews/")
  899. X  "Directory in which to save newsgroup headers.")
  900. X
  901. X(defvar jnews-mode-map (make-sparse-keymap)
  902. X  "Local keymap for jnews summaries.")
  903. X
  904. X(defvar jnews-article-mode-map (make-sparse-keymap)
  905. X  "Local keymap for jnews articles.")
  906. X
  907. X(defvar jnews-current-newsgroup nil
  908. X  "Name of buffer containing current article's newsgroup summary")
  909. X(make-variable-buffer-local 'jnews-current-newsgroup)
  910. X
  911. X;-----------------------Utility functions of general use-----------------------
  912. X
  913. X(defun describe-local-map ()
  914. X  "Print help on each key that is bound in the current mode map"
  915. X  (interactive)
  916. X  (let ((help-keys (cdr (current-local-map))))
  917. X    (pop-to-buffer "*Help*")
  918. X    (erase-buffer)
  919. X    (mapcar '(lambda (key-fun)
  920. X               (insert-char (car key-fun) 1)
  921. X               (indent-to-column 3)
  922. X               (insert (prin1-to-string (cdr key-fun)))
  923. X               (indent-to-column 26)
  924. X               (save-excursion (insert (documentation (cdr key-fun))))
  925. X               (move-to-column (- (screen-width) 1))
  926. X               (delete-region (point) (point-max))
  927. X               (insert "\n"))
  928. X            help-keys)))
  929. X
  930. X(defun summarize-local-map ()
  931. X  "Print one line message in minibuffer summarizing current local map"
  932. X  (interactive)
  933. X  (let ((help-keys (cdr (current-local-map))))
  934. X    (save-excursion
  935. X      (switch-to-buffer "*Help*")
  936. X      (erase-buffer)
  937. X      (mapcar '(lambda (key-fun)
  938. X                 (insert (char-to-string (car key-fun)) ":"
  939. X                         (prin1-to-string (cdr key-fun)) " "))
  940. X              help-keys)
  941. X      (message (buffer-substring (point-min) (point-max))))))
  942. X
  943. X(defun save-and-kill ()
  944. X  "Save this buffer with no backup and kill it"
  945. X  (interactive)
  946. X  (save-buffer 0)
  947. X  (kill-buffer (current-buffer)))
  948. X
  949. X(defun kill-current-buffer ()
  950. X  "Kill the current buffer"
  951. X  (interactive)
  952. X  (kill-buffer (current-buffer)))
  953. X
  954. X(defun backward-line (arg)
  955. X  "Go to beginning of previous line"
  956. X  (interactive "p")
  957. X  (forward-line (- arg)))
  958. X
  959. X; These next two functions were stolen from rnews.el
  960. X(defun string-subst-char (new old string)
  961. X  "Substitute character NEW for character OLD in STRING"
  962. X  (let (index)
  963. X    (setq old (regexp-quote (char-to-string old))
  964. X      string (substring string 0))
  965. X    (while (setq index (string-match old string))
  966. X      (aset string index new)))
  967. X  string)
  968. X
  969. X;;; caesar-region written by phr@prep.ai.mit.edu  Nov 86
  970. X;;; modified by tower@prep Nov 86, josh@vx Jan 89
  971. X(defun caesar-bounded-region (bound1 bound2 &optional n)
  972. X  "Caesar rotation of region defined by BOUND1 and BOUND2 by N (default 13)"
  973. X  (interactive "dmp")
  974. X  (let ((rot (cond ((or (null current-prefix-arg)
  975. X                        (not (numberp n))
  976. X                        (= (% n 26) 0))
  977. X                    13)
  978. X                   ((< n 0) (- 26 (% (- n) 26)))
  979. X                   (t (% n 26)))))
  980. X    (if (or (not (boundp 'caesar-translate-table))
  981. X            (/= (aref caesar-translate-table ?a) (+ ?a rot)))
  982. X        (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
  983. X          (message "Building caesar-translate-table...")
  984. X          (setq caesar-translate-table (make-vector 256 0))
  985. X          (while (< i 256)
  986. X            (aset caesar-translate-table i i)
  987. X            (setq i (1+ i)))
  988. X          (setq lower (concat lower lower) upper (upcase lower) i 0)
  989. X          (while (< i 26)
  990. X            (aset caesar-translate-table (+ ?a i) (aref lower (+ i rot)))
  991. X            (aset caesar-translate-table (+ ?A i) (aref upper (+ i rot)))
  992. X            (setq i (1+ i)))
  993. X          (message "Building caesar-translate-table... done")))
  994. X    (let* ((from (min bound1 bound2))
  995. X           (to (max bound1 bound2))
  996. X           (i 0)
  997. X           (str (buffer-substring from to))
  998. X           (len (length str))
  999. X           (buffer-read-only nil))
  1000. X      (while (< i len)
  1001. X        (aset str i (aref caesar-translate-table (aref str i)))
  1002. X        (setq i (1+ i)))
  1003. X      (goto-char from)
  1004. X      (kill-region from to)
  1005. X      (insert str))))
  1006. X
  1007. X;-------------------------Jnews Summary mode Functions------------------------
  1008. X
  1009. X(defun jnews ()
  1010. X  "Start a Jnews session"
  1011. X  (interactive)
  1012. X  (dired (concat (getenv "HOME") "/jnews")))
  1013. X
  1014. X(defun jnews-read-article ()
  1015. X  "Read the article referred to on the current line"
  1016. X  (interactive)
  1017. X  (let* ((newsgroup (file-name-nondirectory buffer-file-name))
  1018. X         (news-path (concat news-path (string-subst-char ?/ ?. newsgroup) "/"))
  1019. X         (message-number
  1020. X          (save-excursion
  1021. X            (beginning-of-line)
  1022. X            (if (= (point) (point-min)) (error "No more articles"))
  1023. X            (forward-char 1)
  1024. X            (re-search-forward "[ ]*")
  1025. X            (let ((msgno-start (point)))
  1026. X              (search-forward "|")
  1027. X              (buffer-substring msgno-start (- (point) 1))))))
  1028. X    (if (not (file-exists-p (concat news-path message-number)))
  1029. X        (progn
  1030. X          (jnews-expired)
  1031. X          (message "Article has expired!"))
  1032. X      (progn
  1033. X        (jnews-flag)
  1034. X        (pop-to-buffer (concat newsgroup ":" message-number))
  1035. X        (insert-file (concat news-path message-number))
  1036. X        (goto-char (point-min))
  1037. X        (setq buffer-read-only t)
  1038. X        (message "")
  1039. X        (jnews-article-mode newsgroup)))))
  1040. X
  1041. X(defun jnews-article-has-expired ()
  1042. X  (let* ((newsgroup (file-name-nondirectory buffer-file-name))
  1043. X         (news-path (concat news-path (string-subst-char ?/ ?. newsgroup)
  1044. X                            "/"))
  1045. X         (message-number
  1046. X          (save-excursion
  1047. X            (beginning-of-line)
  1048. X            (if (= (point) (point-min)) (error "No more articles"))
  1049. X            (forward-char 1)
  1050. X            (re-search-forward "[ ]*")
  1051. X            (let ((msgno-start (point)))
  1052. X              (search-forward "|")
  1053. X              (buffer-substring msgno-start (- (point) 1))))))
  1054. X    (not (file-exists-p (concat news-path message-number)))))
  1055. X
  1056. X(defun jnews-goto-unread ()
  1057. X  "Move point to the first unseen article"
  1058. X  (interactive)
  1059. X  (search-forward "\n*")
  1060. X  (beginning-of-line)
  1061. X  (while (jnews-article-has-expired)
  1062. X    (jnews-expired)))
  1063. X
  1064. X(defun jnews-update ()
  1065. X  "Run a C program to look for new articles in the current newsgroup"
  1066. X  (interactive)
  1067. X  (save-buffer 0)
  1068. X  (message "Updating %s..." (file-name-nondirectory buffer-file-name))
  1069. X  (call-process jnews-program
  1070. X                nil nil nil
  1071. X                (file-name-nondirectory buffer-file-name)
  1072. X                news-path)
  1073. X  (revert-buffer t t)
  1074. X  (setq buffer-read-only t)
  1075. X  (message "Updating %s...done" (file-name-nondirectory buffer-file-name)))
  1076. X
  1077. X(defun jnews-add-newsgroup (newsgroup)
  1078. X  "Add a new newsgroup"
  1079. X  (interactive "sNewsgroup: ")
  1080. X  (find-file (concat jnews-summary-directory newsgroup))
  1081. X  (jnews-mode))
  1082. X
  1083. X(defun jnews-flag ()
  1084. X  "Flag the current message as previously read"
  1085. X  (interactive)
  1086. X  (ml-prefix-argument-loop
  1087. X   (beginning-of-line)
  1088. X   (if (= (following-char) ?*)
  1089. X       (let ((buffer-read-only nil))
  1090. X         (delete-char 1)
  1091. X         (insert-char ?  1)))
  1092. X   (forward-line 1)))
  1093. X
  1094. X(defun jnews-expired ()
  1095. X  "Flag the current message as previously read"
  1096. X  (interactive)
  1097. X  (ml-prefix-argument-loop
  1098. X   (beginning-of-line)
  1099. X   (if (not (= (following-char) ?#))
  1100. X       (let ((buffer-read-only nil))
  1101. X         (delete-char 1)
  1102. X         (insert-char ?# 1)))
  1103. X   (forward-line 1)))
  1104. X
  1105. X; Do a binary search for the boundary of expired messages!!
  1106. X(defun jnews-expunge-expired ()
  1107. X  "Expunge all expired messages"
  1108. X  (interactive)
  1109. X  (goto-char (point-min))
  1110. X  (forward-line 1)
  1111. X  (let ((buffer-read-only nil)
  1112. X        (begin-deletion (point))
  1113. X        (low (point))
  1114. X        (high (point-max)))
  1115. X
  1116. X    ; Get close with a binary search
  1117. X    (while (> (- high low) 80)
  1118. X      (goto-char (/ (+ low high) 2))
  1119. X      (beginning-of-line)
  1120. X      (if (jnews-article-has-expired)
  1121. X          (setq low (point))
  1122. X        (setq high (point))))
  1123. X
  1124. X    ; Make sure we have not missed with linear searches
  1125. X    (while (and (> (point) (point-min))
  1126. X                (not (jnews-article-has-expired)))
  1127. X      (forward-line -1))
  1128. X    (while (and (> (point) (point-min))
  1129. X                (< (point) (point-max))
  1130. X                (jnews-article-has-expired))
  1131. X      (forward-line 1))
  1132. X    (if (> (point) (point-min))
  1133. X        (delete-region begin-deletion (point)))))
  1134. X
  1135. X(defun jnews-unflag ()
  1136. X  "Unflag current message as if it had never been read"
  1137. X  (interactive)
  1138. X  (ml-prefix-argument-loop
  1139. X   (beginning-of-line)
  1140. X   (if (= (following-char) ? )
  1141. X       (let ((buffer-read-only nil))
  1142. X         (delete-char 1)
  1143. X         (insert-char ?* 1)))
  1144. X   (forward-line 1)))
  1145. X
  1146. X;; Jnews mode is suitable only for specially formatted data.
  1147. X(put 'jnews-mode 'mode-class 'special)
  1148. X
  1149. X(defun jnews-mode ()
  1150. X  "Mode for browsing through newsgroup summaries."
  1151. X  (interactive)
  1152. X  (if (not (eq major-mode 'jnews-mode))
  1153. X      (progn
  1154. X          (kill-all-local-variables)    
  1155. X          (setq major-mode 'jnews-mode)
  1156. X          (setq mode-name "Summaries")
  1157. X          (use-local-map jnews-mode-map)
  1158. X          (setq mode-line-buffer-identification '("Jnews: %17b"))
  1159. X          (jnews-update)
  1160. X          (run-hooks 'jnews-mode-hook))))
  1161. X
  1162. X; Set up the jnews keymap when jnews.el is loaded
  1163. X(define-key jnews-mode-map "?" 'summarize-local-map)
  1164. X(define-key jnews-mode-map "\^?" 'scroll-down)
  1165. X(define-key jnews-mode-map " " 'scroll-up)
  1166. X(define-key jnews-mode-map "*" 'jnews-unflag)
  1167. X(define-key jnews-mode-map "\^M" 'jnews-flag)
  1168. X(define-key jnews-mode-map "\^I" 'jnews-goto-unread)
  1169. X(define-key jnews-mode-map "x" 'jnews-expunge-expired)
  1170. X(define-key jnews-mode-map "u" 'jnews-update)
  1171. X(define-key jnews-mode-map "q" 'save-and-kill)
  1172. X(define-key jnews-mode-map "p" 'backward-line)
  1173. X(define-key jnews-mode-map "n" 'forward-line)
  1174. X(define-key jnews-mode-map "h" 'describe-local-map)
  1175. X(define-key jnews-mode-map "e" 'jnews-read-article)
  1176. X
  1177. X;(global-set-key "\^X\^J" 'jnews-add-newsgroup)
  1178. X;(global-set-key "\^X\j" 'jnews)
  1179. X
  1180. X;--------------------The following commands are used in article mode-----------
  1181. X
  1182. X(defun jnews-next-article (arg)
  1183. X  "Delete the current article and look at the next one"
  1184. X  (interactive "p")
  1185. X  (let* ((newsgroup jnews-current-newsgroup)
  1186. X         (summary-window (get-buffer-window newsgroup)))
  1187. X    (kill-buffer (current-buffer))
  1188. X    (if (null summary-window)
  1189. X        (set-buffer newsgroup)
  1190. X      (select-window summary-window))
  1191. X    (forward-line (- arg 1))
  1192. X    (jnews-read-article)))
  1193. X
  1194. X(defun jnews-previous-article (arg)
  1195. X  "Delete the current article and look at the previous one"
  1196. X  (interactive "p")
  1197. X  (jnews-next-article (- arg)))
  1198. X
  1199. X(defun jnews-rotate-article ()
  1200. X  "Caesar-rotate article"
  1201. X  (interactive)
  1202. X  (save-excursion
  1203. X    (goto-char (point-min))
  1204. X    (search-forward "\n\n")
  1205. X    (caesar-bounded-region (point) (point-max) 13)))
  1206. X
  1207. X(defun jnews-mark-article ()
  1208. X  "Mark the text of the current article"
  1209. X  (interactive)
  1210. X  (goto-char (point-min))
  1211. X  (search-forward "\n\n")
  1212. X  (set-mark (point-max)))
  1213. X
  1214. X(defun jnews-article-mode (newsgroup)
  1215. X  "Mode for browsing through newsgroup articles."
  1216. X  (kill-all-local-variables)    
  1217. X  (setq major-mode 'jnews-article-mode)
  1218. X  (setq mode-name "Article")
  1219. X  (use-local-map jnews-article-mode-map)
  1220. X  (setq mode-line-buffer-identification '("Jnews: %17b"))
  1221. X  (setq jnews-current-newsgroup newsgroup)
  1222. X  (run-hooks 'jnews-article-hook))
  1223. X
  1224. X; Set up the jnews keymap when jnews.el is loaded
  1225. X(define-key jnews-article-mode-map "?" 'summarize-local-map)
  1226. X(define-key jnews-article-mode-map "\^?" 'scroll-down)
  1227. X(define-key jnews-article-mode-map " " 'scroll-up)
  1228. X(define-key jnews-article-mode-map "." 'beginning-of-buffer)
  1229. X(define-key jnews-article-mode-map "r" 'jnews-rotate-article)
  1230. X(define-key jnews-article-mode-map "q" 'kill-current-buffer)
  1231. X(define-key jnews-article-mode-map "\^I" 'kill-current-buffer)
  1232. X(define-key jnews-article-mode-map "p" 'jnews-previous-article)
  1233. X(define-key jnews-article-mode-map "n" 'jnews-next-article)
  1234. X(define-key jnews-article-mode-map "m" 'jnews-mark-article)
  1235. X(define-key jnews-article-mode-map "h" 'describe-local-map)
  1236. END_OF_FILE
  1237. if test 13757 -ne `wc -c <'jnews.original'`; then
  1238.     echo shar: \"'jnews.original'\" unpacked with wrong size!
  1239. fi
  1240. # end of 'jnews.original'
  1241. fi
  1242. echo shar: End of shell archive.
  1243. exit 0
  1244.  
  1245.  
  1246. -- 
  1247. Kim Letkeman    uunet!mitel!spock!kim
  1248.  
  1249.  
  1250.