guido@mcvax.UUCP (Guido van Rossum) (12/30/83)
Here is the promised posting of enews. I hope this format is convenient for all of you. To check for transmission errors, you can say "sum *"; it should print 23008 1 Makefile 22637 2 README 18639 12 endex.c 23669 1 enews-help 30510 10 enews.ml 40673 2 followup.ml 51465 2 reply.ml (Maybe the second column can differ due to different block sizes?). ========== Feed the rest of this file to /bin/sh ========== echo 'x README' cat >README <<'XyZZy.README' This directory contains the files necessary to build 'enews', an interface to news 2.10 based on Emacs. Very probably it won't run for older news versions. You need Gosling's Emacs #85 to run it. If you have Emacs #264, it may run after changing all "error-occured" (with three r's) into "error-occurred" (with four r's). To make the C support program endex, type "make" in this directory. If your news system has different names for "/usr/spool/news", "/usr/lib/news/active" or ".newsrc", first change the corresponding #define lines in the source. Before installation, first choose directories where the shell script to invoke it, the Emacs MLisp files and the C-support should live, and change the definitions for DESTDIR, MACLIB and DESTLIB respectively in "Makefile". Preferably MACLIB should be Emacs' default macro library (if you have write access to it). Also change the line saying ("setq-default news-library ...) in file "enews.ml" (almost at the end). There is a reference to "/usr/lib/news/recmail" in "reply.ml"; this program is used to post the letter. Check if this works on your system; if not, find an alternative or live without the 'r' command. If all seems ok or is fixed, you can type "make install" to install everything. XyZZy.README echo 'x Makefile' cat >Makefile <<'XyZZy.Makefile' DESTLIB=/usr/lib DESTDIR=/usr/bin MACLIB=/usr/lib/emacs/maclib EMACS=emacs CFLAGS=-O endex: endex.o rm -f core gmon.out cc $(LDFLAGS) endex.o -o endex enews.sh: Makefile echo EPATH=$(MACLIB) >enews.sh echo 'export EPATH' >>enews.sh echo 'exec $(EMACS) -l"enews.ml" -e"read-news-then-exit"' >>enews.sh echo ': "This file is created by Makefile, dont edit it!"' >>enews.sh install: endex enews.sh cp endex $(DESTLIB)/endex cp enews-help $(DESTLIB)/enews-help cp enews.ml $(MACLIB)/enews.ml cp followup.ml $(MACLIB)/followup.ml cp reply.ml $(MACLIB)/reply.ml cp enews.sh $(DESTDIR)/enews XyZZy.Makefile echo 'x endex.c' cat >endex.c <<'XyZZy.endex.c' /* * endex [-u] * * Process .newsrc and news spooling directories to aid enews. * Without -u, prints newsgroups and article file names of unread news; * with -u, reads such a list and marks starred articles as read. * * DISCLAIMER: this code probably only works with news 2.10. */ /* * This software is copyright (c) Mathematical Centre, Amsterdam, 1983. * Permission is granted to use and copy this software, but not for profit, * and provided that these same conditions are imposed on any person * receiving or using the software. */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/dir.h> typedef char *string; #define Strequ(s, t) (strcmp((s), (t)) == 0) #define Strnequ(s, t, n) (strncmp((s), (t), (n)) == 0) string rindex(); string index(); string strcpy(); string strncpy(); string malloc(); string strcat(); string strncat(); string getenv(); #define NEWSRC "/.newsrc" /* Must include a slash, $HOME is prepended! */ #define ACTIVE "/usr/lib/news/active" #define NEWSDIR "/usr/spool/news/" #define MAXOPTIONS 1000 #define MAXOPTLINES 100 #define MAXACTIVE 1000 #define MAXBUF 1000 #define MAXBITS 32000 #define BITSPBYTE 8 #define BITSPW (BITSPBYTE*sizeof(int)) #define BITWORDS (MAXBITS/BITSPW + 1) typedef int bool; typedef char BUF[MAXBUF]; typedef int BITS[BITWORDS]; #define Yes 1 #define No 0 #define Setbit(b, i) ((b)[(i)/BITSPW] |= (1<<(i)%BITSPW)) #define Clrbit(b, i) ((b)[(i)/BITSPW] &= ~(1<<(i)%BITSPW)) #define Bitset(b, i) ((b)[(i)/BITSPW] & (1<<(i)%BITSPW)) struct active { string a_name; int a_maxart; bool a_off; string a_data; }; string defoptions[] = {"announce", "general", "all.announce", "all.general", (string)NULL}; struct active active[MAXACTIVE]; int nactive; string optlines[MAXOPTLINES]; int noptlines; string options[MAXOPTIONS]; int noptions; string whoami; bool updating; BUF newsrc; main(argc, argv) int argc; string argv[]; { getargs(argc, argv); mknewsrc(); getnewsrc(); if (updating) getcommands(); mainloop(); if (updating) putnewsrc(); return 0; } getargs(argc, argv) int argc; string argv[]; { int i; string cp; whoami = argv[0]; cp = rindex(whoami, '/'); if (cp) whoami = cp+1; for (i = 1; i < argc && argv[i][0] == '-'; ++i) { switch (argv[i][1]) { case 'u': updating = Yes; continue; } break; } if (i < argc) { fprintf(stderr, "usage: %s [-u]\n", whoami); exit(2); } } mknewsrc() { string home; home = getenv("HOME"); if (!home) { warning("$HOME not set, assumed current directory"); home = "."; } strcpy(newsrc, home); strcat(newsrc, NEWSRC); } string copystring(str) string str; { string cpy; cpy = malloc(strlen(str) + 1); if (!cpy) fatal("out of memory"); strcpy(cpy, str); return cpy; } getnewsrc() { BUF linebuf; FILE *fp; setdefoptions(); fp = fopen(newsrc, "r"); if (fp) { if (!fgets(linebuf, MAXBUF, fp) || !getoptions(fp, linebuf)) { fclose(fp); fp = (FILE*)NULL; } } addoption("!junk"); addoption("!control"); getactive(); if (fp) { do { getdata(linebuf); } while (fgets(linebuf, MAXBUF, fp)); fclose(fp); } } setdefoptions() { string *p; for (p = defoptions; *p; ++p) addoption(*p); } mainloop() { int ng; auto int minart; int maxart; BITS b; BUF namebuf; for (ng = 0; ng < nactive; ++ng) { if (active[ng].a_off) continue; unpackdata(ng, b, &minart); maxart = active[ng].a_maxart; if (minart <= maxart) { mkname(ng, namebuf); getdir(namebuf, b, minart, maxart); if (updating) packdata(ng, b, minart); else showdata(namebuf, ng, b, minart, maxart); } } } showdata(namebuf, ng, b, minart, maxart) int ng; BITS b; int minart; int maxart; { while (minart <= maxart && Bitset(b, minart)) ++minart; if (minart <= maxart) { printf("N%s\n", active[ng].a_name); for (; minart <= maxart; ++minart) { if (!Bitset(b, minart)) printf(" %s/%d\n", namebuf, minart); } } } mkname(ng, namebuf) int ng; BUF namebuf; { string cp; strcpy(namebuf, NEWSDIR); namebuf += strlen(namebuf); cp = active[ng].a_name; for (; *cp; ++namebuf, ++cp) { if (*cp == '.') *namebuf = '/'; else *namebuf = *cp; } *namebuf = '\0'; } bool match(str, pat) register string str; register string pat; { while (*pat) { if (pat[0] == 'a' && pat[1] == 'l' && pat[2] == 'l') { pat += 3; for (;;) { while (*str && *str != '.' && *str != *pat) ++str; if (match(str, pat)) return Yes; if (!*str || *str == '.') return No; ++str; } } if (*str != *pat) return No; ++str; ++pat; } return !*str || *str == '.'; } int findng(name) register string name; { register int ng; for (ng = 0; ng < nactive; ++ng) { if (Strequ(name, active[ng].a_name)) return ng; } return -1; } addoption(name) string name; { if (noptions >= MAXOPTIONS) { warning("too many options"); return; } options[noptions] = copystring(name); ++noptions; } bool selected(name) register string name; { register bool neg; int i; register string pat; bool ok; ok = No; for (i = 0; i < noptions; ++i) { pat = options[i]; neg = *pat == '!'; if (neg) ++pat; if (match(name, pat)) { ok = !neg; if (Strequ(name, pat)) break; } } return ok; } clearbits(b) register BITS b; { register int i; for (i = 0; i < BITWORDS; ++i) b[i] = 0; } string getword(cpp) string *cpp; { register string wp; register string cp; cp = *cpp; while (isspace(*cp) || *cp == ',') ++cp; if (!*cp) { *cpp = (string)NULL; return (string)NULL; } wp = cp; while (*cp && !isspace(*cp) && *cp != ',') ++cp; if (*cp) { *cp = '\0'; ++cp; } *cpp = cp; return wp; } bool getoptions(fp, linebuf) FILE *fp; BUF linebuf; { auto string cp; string wp; if (!Strnequ(linebuf, "options ", 8)) return Yes; for (cp = linebuf+7; isspace(*cp); cp = linebuf) { if (noptlines >= MAXOPTLINES) warning("too many option lines"); else { optlines[noptlines] = copystring(linebuf); ++noptlines; } while (wp = getword(&cp)) { if (Strequ(wp, "-n")) { while ((wp = getword(&cp)) && wp[0] != '-') addoption(wp); } } if (!fgets(linebuf, MAXBUF, fp)) return No; } return Yes; } getactive() { FILE *fp; BUF linebuf; auto string cp; string wp; int maxart; fp = fopen(ACTIVE, "r"); if (!fp) { perror(ACTIVE); fatal("can't read %s", ACTIVE); } while (fgets(linebuf, MAXBUF, fp)) { cp = linebuf; wp = getword(&cp); if (!wp) { warning("empty line in %s", ACTIVE); continue; } if (!selected(wp)) continue; if (nactive >= MAXACTIVE) { warning("too many active newsgroups in %s", ACTIVE); break; } active[nactive].a_name = copystring(wp); wp = getword(&cp); if (wp) { maxart = atoi(wp); if (maxart > MAXBITS) { warning("Newsgroup %s: too many articles", active[nactive].a_name); maxart = MAXBITS; } active[nactive].a_maxart = maxart; } ++nactive; } fclose(fp); } getdir(name, b, minart, maxart) string name; BITS b; int minart; int maxart; { BITS here; DIR *dp; struct direct *dirent; auto string cp; auto int artno; dp = opendir(name); clearbits(here); if (dp) { while (dirent = readdir(dp)) { cp = dirent->d_name; if (getint(&cp, &artno) && !*cp && !dupfile(dirent) && artno >= minart && artno <= maxart) Setbit(here, artno); } closedir(dp); } for (; minart <= maxart; ++minart) { if (!Bitset(here, minart)) Setbit(b, minart); } } bool dupfile(dirent) struct direct *dirent; { static BITS inodes; long ino; ino = dirent->d_ino; if (ino >= 1 && ino <= MAXBITS) { if (Bitset(inodes, ino)) return Yes; Setbit(inodes, ino); } return No; } putnewsrc() { int i; FILE *fp; fp = fopen(newsrc, "w"); if (!fp) { perror(newsrc); fatal("can't rewrite %s", newsrc); } for (i = 0; i < noptlines; ++i) fputs(optlines[i], fp); for (i = 0; i < nactive; ++i) { if (!active[i].a_data && !active[i].a_off) continue; fprintf(fp, "%s%c %s\n", active[i].a_name, active[i].a_off ? '!' : ':', active[i].a_data ? active[i].a_data : ""); } if (fclose(fp) == EOF) { perror(newsrc); fatal("can't flush %s", newsrc); } } getcommands() { BUF linebuf; BITS b; string cp; string name; int ng; int artno; int curng; int minart; curng = -1; while (fgets(linebuf, MAXBUF, stdin)) { cp = index(linebuf, '\n'); if (cp) *cp = '\0'; switch (linebuf[0]) { case 'N': break; case 'S': case 'U': ng = findng(linebuf+1); if (ng < 0) warning("Newsgroup %s: not found", linebuf+1); else active[ng].a_off = linebuf[0] == 'U'; break; case 'e': case ' ': case '*': cp = rindex(linebuf+1, '/'); if (!cp) { warning("bad input line: `%s'", linebuf); break; } *cp = '\0'; ++cp; if (!getint(&cp, &artno) || *cp) { warning("bad article number in input line `%s/%s'", linebuf, cp); break; } name = linebuf+1; if (Strnequ(name, NEWSDIR, sizeof NEWSDIR - 1)) name += sizeof NEWSDIR - 1; for (cp = name; *cp; ++cp) { if (*cp == '/') *cp = '.'; } ng = findng(name); if (ng < 0) { warning("newsgroup not found: %s", name); break; } if (ng != curng) { if (curng >= 0) packdata(curng, b, minart); unpackdata(ng, b, &minart); curng = ng; } if (linebuf[0] == '*') Setbit(b, artno); else if (linebuf[0] == 'e') Clrbit(b, artno); break; default: warning("bad key character `%c'", linebuf[0]); } } if (curng > 0) packdata(curng, b, minart); } getdata(linebuf) string linebuf; { string cp; string ep; int c; int ng; cp = linebuf; while (*cp && *cp != ':' && *cp != '!' && !isspace(*cp)) ++cp; c = *cp; if (c) { *cp = '\0'; ++cp; } if (!*linebuf) { warning("bad line in %s", newsrc); return; } ng = findng(linebuf); if (ng < 0) { ng = nactive; if (ng >= MAXACTIVE) { warning("too many lines in %s", newsrc); return; } active[ng].a_name = copystring(linebuf); c = '!'; ++nactive; } while (*cp && isspace(*cp)) ++cp; if (*cp) { ep = index(cp, '\n'); if (ep) *ep = '\0'; if (active[ng].a_data) free(active[ng].a_data); active[ng].a_data = copystring(cp); } active[ng].a_off = c == '!'; } unpackdata(ng, b, pminart) int ng; BITS b; int *pminart; { auto int u; auto int v; auto string data; int minart; int maxart; data = active[ng].a_data; maxart = active[ng].a_maxart; minart = 1; clearbits(b); while (getint(&data, &u)) { v = u; if (*data == '-') { ++data; getint(&data, &v); } if (v > maxart) v = maxart; if (u <= minart) minart = u = v+1; for (; u <= v; ++u) Setbit(b, u); while (*data == ',' || isspace(*data)) ++data; } *pminart = minart; } packdata(ng, b, minart) int ng; BITS b; int minart; { int maxart; BUF data; string cp = data; int low; maxart = active[ng].a_maxart; low = 1; while (low <= maxart) { while (minart <= maxart && Bitset(b, minart)) ++minart; if (minart > low) { if (cp > data) { *cp = ','; ++cp; } sprintf(cp, "%d", low); cp += strlen(cp); if (minart > low+1) { sprintf(cp, "-%d", minart-1); cp += strlen(cp); } } while (minart <= maxart && !Bitset(b, minart)) ++minart; low = minart; } if (active[ng].a_data) free(active[ng].a_data); active[ng].a_data = cp > data ? copystring(data) : (string)NULL; } bool getint(cpp, ip) string *cpp; int *ip; { int i; string cp; cp = *cpp; if (!isdigit(*cp)) return No; i = 0; do { i = i*10 + *cp - '0'; ++cp; } while (isdigit(*cp)); *ip = i; *cpp = cp; return Yes; } /* VARARGS 1 */ fatal(str, a1, a2, a3, a4, a5) string str; { fprintf(stderr, "%s: ", whoami); fprintf(stderr, str, a1, a2, a3, a4, a5); fprintf(stderr, "\n"); exit(1); } /* VARARGS 1 */ warning(str, a1, a2, a3, a4, a5) string str; { fprintf(stderr, "%s: ", whoami); fprintf(stderr, str, a1, a2, a3, a4, a5); fprintf(stderr, " (warning)\n"); } XyZZy.endex.c echo 'x enews.ml' cat >enews.ml <<'XyZZy.enews.ml' ; Read your news with Emacs! (but only at high speeds...) ; Given some C support to compare the news database with the contents ; of "$HOME/.newsrc", Emacs shows you each article in turn in a window. ; Finally the C support updates the ".newsrc" file. ; ; The C support consists of the program "endex", that lives is ; directories ~guido/src/enews (sources) and ~guido/lib (binaries). ; For a command summary, read the file news-help. ; ---------------------------------------------------------------------- (message "Loading the news system, please wait...") (sit-for 0) ; Subroutines (defun (find-current-article (pop-to-buffer "news-index") (exchange-dot-and-mark) (beginning-of-line) (set-mark) ) ) (defun (find-current-newsgroup (find-current-article) (if (error-occured (re-search-reverse "^[UN]")) (error-message "First newsgroup.") ) (beginning-of-line) ) ) (defun (find-next-article (find-current-article) (forward-character) (if (error-occured (re-search-forward "^[ e*]")) (error-message "Last article.") ) (beginning-of-line) (set-mark) ) ) (defun (find-first-article (pop-to-buffer "news-index") (beginning-of-file) (set-mark) (find-next-article) (set-mark) ) ) (defun (find-next-newsgroup (find-current-article) (if (error-occured (re-search-forward "^[NU]")) (error-message "Last newsgroup.") ) (beginning-of-line) ) ) (defun (find-next-newsgroup-to-read (find-current-article) (forward-character) (if (error-occured (re-search-forward "^N")) (error-message "Last newsgroup.") ) (beginning-of-line) ) ) (defun (find-next-article-to-read (find-current-article) (forward-character) (if (error-occured (re-search-forward "^ ")) (error-message "Last article.") ) (beginning-of-line) (set-mark) ) ) (defun (find-previous-article (find-current-article) (backward-character) (if (error-occured (re-search-reverse "^[ e*]")) (error-message "First article.") ) (beginning-of-line) (set-mark) ) ) (defun (mark-current-article-read (save-window-excursion (find-current-article) (if (looking-at " ") (progn (delete-next-character) (insert-character '*') ) ) ) ) ) ; ---------------------------------------------------------------------- (defun (show-article file-name (save-window-excursion (find-current-article) (forward-character) (set-mark) (end-of-line) (setq file-name (region-to-string)) ) (pop-to-buffer "news-article") (read-file file-name) (show-news-headers) (if (error-occured (re-search-forward "^Lines:")) (beginning-of-file) ;else (line-to-top-of-window) ) (end-of-window) (beginning-of-line) (novalue) ) ) (defun (show-news-headers old-case-fold-search (pop-to-buffer "news-headers") (erase-buffer) (pop-to-buffer "news-article") (beginning-of-file) (set-mark) (re-search-forward "^$") (narrow-region) (setq old-case-fold-search case-fold-search) (setq case-fold-search 1) (app-news-header "^Subject" "news-headers") (app-news-header "^Title" "news-headers") (app-news-header "^Newsgroup" "news-headers") (app-news-header "^From" "news-headers") (app-news-header "^Date" "news-headers") (setq case-fold-search old-case-fold-search) (widen-region) (pop-to-buffer "news-headers") (delete-other-windows) ; This puts the headers window at the top (pop-to-buffer "news-article") (pop-to-buffer "news-headers") (while (> (window-height) 4) ; Stupid Emacs misspells function names! (shrink-window) ) (pop-to-buffer "news-article") (novalue) ) ) (defun (app-news-header (beginning-of-file) (if (! (error-occured (re-search-forward (arg 1)))) (progn (beginning-of-line) (set-mark) (next-line) (append-region-to-buffer (arg 2)) ) ) ) ) ; ---------------------------------------------------------------------- (defun (simple-next-article (save-window-excursion (mark-current-article-read) (prefix-argument-loop (find-next-article) ) ) (show-article) ) ) (defun (next-article (save-window-excursion (mark-current-article-read) (prefix-argument-loop (find-next-article-to-read) ) ) (show-article) ) ) (defun (previous-article (save-window-excursion (prefix-argument-loop (find-previous-article) ) ) (show-article) ) ) (defun (next-page-or-article (pop-to-buffer "news-article") (end-of-window) (if (eobp) (next-article) ;else (progn (next-page) (end-of-window) (beginning-of-line) ) ) (novalue) ) ) (defun (save-article file-name (mark-current-article-read) (pop-to-buffer "news-article") (setq file-name (arg 1 "Append to file: (default Articles) ")) (if (= file-name "") (setq file-name "Articles") ) (append-to-file file-name) (setq buffer-is-modified 0) (message (concat "Appended to file " (expand-file-name file-name))) (novalue) ) ) (defun (quit-news (mark-current-article-read) (pop-to-buffer "news-index") (delete-other-windows) (update-newsrc) (exit-emacs) ) ) (defun (quit-news-save-files (mark-current-article-read) (pop-to-buffer "news-index") (delete-other-windows) (update-newsrc) (write-file-exit) ) ) (defun (next-newsgroup (save-window-excursion (find-next-newsgroup-to-read) (next-line) (set-mark) (find-next-article-to-read) ) (show-article) ) ) (defun (kill-newsgroup (save-window-excursion (find-current-article) (while (! (looking-at "[UN]")) (if (looking-at " ") (progn (delete-next-character) (insert-character '*') ) ) (next-line) (beginning-of-line) (if (eobp) (error-message "Last newsgroup.") ) ) (set-mark) (find-next-article-to-read) ) (show-article) ) ) (defun (unsubscribe-newsgroup (save-window-excursion (find-current-newsgroup) (delete-next-character) (insert-character 'U') (set-mark) (end-of-line) (message (concat "Unsubscribing to newsgroup " (region-to-string))) (find-next-newsgroup-to-read) (set-mark) (find-next-article-to-read) ) (show-article) ) ) (defun (news-help (delete-other-windows) ; Avoid help showing in tiny headers window (save-excursion (visit-file (concat news-library "enews-help")) (init-article-buffer "news-help") ) (novalue) ) ) (defun (show-headers (if (= (current-buffer-name) "news-headers") (pop-to-buffer "news-article") (pop-to-buffer "news-headers") ) (novalue) ) ) (defun (full-headers (if (if (= (current-buffer-name) "news-article") (progn (beginning-of-window) (bobp) ) ) (progn (exchange-dot-and-mark) (line-to-top-of-window) (set-mark) (end-of-window) ) (progn (pop-to-buffer "news-article") (beginning-of-window) (if (! (bobp)) (progn (set-mark) (beginning-of-file) ) ) ) ) (novalue) ) ) (defun (show-news-index (if (= (current-buffer-name) "news-article") (switch-to-buffer "news-index") ;else (show-article) ) (novalue) ) ) (defun (erase-article-read (save-window-excursion (find-current-article) (delete-next-character) (insert-character 'e') ) (message "Article assumed unread") (find-next-article-to-read) (show-article) ) ) ; ---------------------------------------------------------------------- (defun (make-news-index (pop-to-buffer "news-index") (erase-buffer) (message "Reading news index, please wait...") (sit-for 0) ; Screen refresh (set-mark) (filter-region (concat "exec " news-library "endex")) (setq buffer-is-modified 0) (beginning-of-file) (if (eobp) (error-message "No news.")) (novalue) ) ) (defun (update-newsrc (save-window-excursion (pop-to-buffer "news-index") (if buffer-is-modified (progn (message "Updating .newsrc, please wait...") (sit-for 0) ; Screen refresh (end-of-file) (set-mark) (beginning-of-file) (filter-region (concat "exec " news-library "endex -u")) (setq buffer-is-modified 0) ) ) ) (novalue) ) ) (autoload "followup-article" "followup.ml") (autoload "reply-by-mail" "reply.ml") (defun (init-article-buffer i (pop-to-buffer (arg 1 ": init-article-buffer ")) (setq needs-checkpointing 0) (setq mode-string "news") (setq i ' ') (while (< i 127) (local-bind-to-key "illegal-operation" i) (setq i (+ 1 i)) ) (local-bind-to-key "next-page-or-article" '^M') ; RETURN (local-bind-to-key "next-page-or-article" ' ') (local-bind-to-key "next-page-or-article" 'y') ; to be compatible (local-bind-to-key "news-help" '?') (local-bind-to-key "previous-article" '-') (local-bind-to-key "simple-next-article" '+') (local-bind-to-key "erase-article-read" 'e') (local-bind-to-key "followup-article" 'f') (local-bind-to-key "show-headers" 'h') (local-bind-to-key "full-headers" 'H') (local-bind-to-key "show-news-index" 'i') (local-bind-to-key "kill-newsgroup" 'K') (local-bind-to-key "next-article" 'n') (local-bind-to-key "next-newsgroup" 'N') (local-bind-to-key "quit-news" 'q') (local-bind-to-key "quit-news-save-files" "\^X\^F") (local-bind-to-key "reply-by-mail" 'r') (local-bind-to-key "save-article" 's') (local-bind-to-key "unsubscribe-newsgroup" 'U') (local-bind-to-key "exit-emacs" 'x') (novalue) ) ) (defun (read-news (save-window-excursion (init-article-buffer "news-index") (make-news-index) (init-article-buffer "news-headers") (init-article-buffer "news-article") (find-first-article) (show-article) (recursive-edit) ) ) ) (defun (read-news-then-exit (argc) ; To avoid editing files from .emacs_NN (NN = your uid) (pop-to-buffer "news-index") (read-news) (exit-emacs) ) ) (setq-default news-library "~guido/lib/") ; Change this line! (message "") (novalue) XyZZy.enews.ml echo 'x reply.ml' cat >reply.ml <<'XyZZy.reply.ml' ; Supplement to enews.ml for reply by mail. (defun (reply-by-mail old-case-fold-search (pop-to-buffer "news-reply") (erase-buffer) (pop-to-buffer "news-article") (beginning-of-file) (set-mark) (if (error-occured (re-search-forward "^$")) (end-of-file) ) (narrow-region) (setq old-case-fold-search case-fold-search) (setq case-fold-search 1) (app-news-header "^Path" "news-reply") (app-news-header "^Subject" "news-reply") (app-news-header "^Title" "news-reply") (app-news-header "^Reference" "news-reply") (app-news-header "Message-id" "news-reply") (widen-region) (setq case-fold-search old-case-fold-search) (pop-to-buffer "news-reply") (beginning-of-file) (setq old-case-fold-search case-fold-search) (setq case-fold-search 1) (error-occured (re-replace-string "^Path" "To")) (error-occured (re-replace-string "^Title" "Subject")) (error-occured (re-replace-string "^Subject" "Subject: Re")) (error-occured ; Remove multiple 'Re: Re:' (re-replace-string "^Subject: Re:[ ]*Re" "Subject: Re")) (if (! (error-occured (re-search-forward "^Reference"))) (if (! (error-occured (re-search-forward "^Message-id"))) (progn (beginning-of-line) (re-replace-string "^Message-id:[ ]*" " ") (beginning-of-line) (delete-previous-character) ) ) ;else (error-occured (re-replace-string "^Message-id" "References")) ) (end-of-file) (setq case-fold-search old-case-fold-search) (insert-character '\n') (message "Type reply; to post it, type ctrl-X-ctrl-C.") (delete-other-windows) (setq buffer-is-modified 0) (recursive-edit) (if buffer-is-modified (progn (beginning-of-file) (set-mark) (end-of-file) (message "Posting reply, please wait...") (sit-for 0) (filter-region "/usr/lib/news/recmail -t") ) ) (show-article) (novalue) ) ) XyZZy.reply.ml echo 'x followup.ml' cat >followup.ml <<'XyZZy.followup.ml' ; Supplement to enews.ml for followup article. (defun (followup-article old-case-fold-search (pop-to-buffer "news-followup") (erase-buffer) (pop-to-buffer "news-article") (beginning-of-file) (set-mark) (if (error-occured (re-search-forward "^$")) (end-of-file) ) (narrow-region) (setq old-case-fold-search case-fold-search) (setq case-fold-search 1) (app-news-header "^Newsgroup" "news-followup") (app-news-header "^Subject" "news-followup") (app-news-header "^Title" "news-followup") (app-news-header "^Reference" "news-followup") (app-news-header "Message-id" "news-followup") (widen-region) (setq case-fold-search old-case-fold-search) (pop-to-buffer "news-followup") (beginning-of-file) (setq old-case-fold-search case-fold-search) (setq case-fold-search 1) (error-occured (re-replace-string "^Title" "Subject")) (error-occured (re-replace-string "^Subject" "Subject: Re")) (error-occured ; Remove multiple 'Re: Re:' (re-replace-string "^Subject:[ ]*Re:[ ]*Re" "Subject: Re")) (if (! (error-occured (re-search-forward "^Reference"))) (if (! (error-occured (re-search-forward "^Message-id"))) (progn (beginning-of-line) (re-replace-string "^Message-id:[ ]*" " ") (beginning-of-line) (delete-previous-character) ) ) ;else (error-occured (re-replace-string "^Message-id" "References")) ) (end-of-file) (setq case-fold-search old-case-fold-search) (insert-character '\n') (message "Type followup article; to post it, type ctrl-X-ctrl-C.") (delete-other-windows) (setq buffer-is-modified 0) (recursive-edit) (if buffer-is-modified (progn (beginning-of-file) (set-mark) (end-of-file) (message "Posting followup article, please wait...") (sit-for 0) (filter-region "inews -h") ) ) (show-article) (novalue) ) ) XyZZy.followup.ml echo 'x enews-help' cat >enews-help <<'XyZZy.enews-help' space shows more of the article, or the next article if there isn't more. - previous article. + next article, even if this was already read. e ('erase') forget the article was read. h toggles between window containing headers and article. H goes to top of article buffer, (where full headers are shown). n next unread article (assuming the current article has been read). q quits Emacs and updates ".newsrc". s save (append) the article to a file, whose name is prompted for. x quits without updating. (ESC-^V for more help) ? shows help window. N next newsgroup. U Unsubscribe current newsgroup. K Kill newsgroup: assume 'n' for all articles in it this time. f Issue follow-up article. r Reply by mail to article's author. XyZZy.enews-help