[gnu.emacs.gnus] Reading mail and news with a single GNUS

karl@godiva.cis.ohio-state.edu (Karl Kleinpaste) (09/23/89)

I get an awful lot of mail.  Lately, I've been pushing 300 pieces/day.
A lot of that's daemon-generated trash (read it fast and throw it
away), a lot of it is non-critical mailing list stuff, and the rest is
varyingly-important text intended specifically for myself.  The normal
mail tools I've found don't cut it for me, to make things manageable
so I can read mail and get some Real Work done, too.

So a few months back, under GNUS 3.11, I figured out how to convince
GNUS to read mail and news in a single incarnation.  I posted that
scheme here back then.  This is the same scheme, updated for GNUS
3.12, with what I hope is a bit more readable procedure for putting it
to use.  It requires one change to GNUS code proper (a very small
change to nnspool.el), a couple of shell scripts, some new .emacs
things, and a .forward file to aim your mail into the pseudonews
hierarchy.

I don't promise that this is trivial to install, or anything like
that.  Parts of it (especially the pmd [personal mail deliverer]
script) are extremely personalized, and how you put it to use depends
on how you intend to try to classify your mail.  The end result of the
mess is highly desirable, however - a lot of mail divvied up into
manageable categories that look for all the world like newsgroups.
You will find (it was to my own unending joy) that you can use KILL
files on your mail - what a neat idea.

There is a downside or two to it, as well:
[1] The idea of "deleting" a message isn't the same as for a
conventional mailer interface; a message is marked `D' but that
doesn't mean it disappears, so you have to do some sort of an "expire"
from time to time.  I do this every couple of weeks, spending half an
hour or so trimming down the mass of gobbledygook I've received
recently into just the most recent stuff that I really do want to keep
around just-in-case.
[2] There is a bug in GNUS somewhere which I haven't been able to
trace, where replying to a message via mail causes the trailing
character of either the originator's address or the subject to be
stripped off; this doesn't happen with real newsgroups, nor with all
mail, so I suspect it's either POM-dependent or else it depends on
some quirky header ordering which GNUS doesn't expect.  If anyone gets
around to fixing this, please let me know.

Have fun,
--Karl

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	.emacs
#	.forward
#	make-active
#	nnspool-diff
#	pmd
# This archive created: Sat Sep 23 00:09:16 1989
# By:	Karl Kleinpaste (OSU)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(4451 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^KK//' << \GNUSMAIL > 'README'
KKThis file documents a scheme whereby GNUS can become one's universal
KKnews and mail interface.  Use of this scheme depends on the following:
KK	[a] News-reading using a local news spool; NNTP is not possible.
KK	[b] Availability of Berkeley symbolic links, to fool GNUS into
KK	    reading news in two places at once.
KK	[c] Support of .forward files for mail delivery, and in
KK	    particular piped aliases in .forward files.
KK	[d] GNUS 3.12.
KK
KKPlease read this entire description (several times) before putting any
KKof it to use, to be sure you understand what it is that you are
KKaccomplishing along the way.
KK
KKPROCEDURE:
KK
KK[1] Build a newsgroup symlink tree.
KK	mkdir ~/Memos
KK	cd ~/Memos
KK	foreach i (/usr/spool/news/*)
KK		ln -s $i
KK	end
KKNow the variable nnspool-spool-directory can be set to the value
KK"~/Memos/" and things would work just like they always have.  But of
KKcourse we're nowhere near done yet.
KK
KK[2] Decide on a news directory hierarchy to be placed in parallel with
KKnormal news.  I use "personal.*" (for personal mail), "list.*" (for
KKmailing-list-related things), and "mailer-daemon" (so that bounces
KKfrom mailer daemons around the world don't clutter up your Real Mail
KKthat you really want to read).  Make news-like directories for these.
KK	mkdir personal list mailer-daemon
KK	mkdir personal/general personal/special-person-1 personal/s-p-2
KK	mkdir list/general list/listname-1 list/listname-2 list/listname-3
KK
KK[3] Initialize a pair of counter files for each newsgroup.  These will
KKbe used for the max and min fields in the fake active file.  Also
KKcreate "KEY" files, used in mailbox delivery locking.
KK	foreach i (personal/* list/* mailer-daemon)
KK		echo 0 > $i/.first
KK		echo 0 > $i/.last
KK		touch $i/KEY
KK	end
KK
KK[4] Copy the "make-active" script into your private bin directory.
KK(_Everyone_ has a private bin directory, right?)  Also copy the "pmd"
KKscript (personal mail deliverer) to the same place, and edit it for
KKwhatever personalizations you wish to give it.  I detect my usual
KKpseudonewsgroup pattern of personal.*, list.*, and mailer-daemon.
KKThis script can be arbitrarily complex; have a blast.  BEWARE of the
KKfact that certain incarnations of csh are [cough] deficient and cannot
KKcope successfully with the long "mailer-daemon" regexp; case in point,
KKHP-UX 6.2's csh.
KK	cp make-active pmd ~/bin
KK	emacs ~/bin/pmd
KK
KK[5] Edit the sample .emacs stuff into your own .emacs.  Note that a
KKnumber of items in there are peculiar to myself.  (I never said that
KKsetting this up was going to be easy.  But neither is it exceptionally
KKpainful, just somewhat tedious.)  Change function names and variable
KKvalues as appropriate to be useful to yourself.
KK	emacs ~/.emacs
KK
KK[6] Apply the enclosed patch to nnspool.el, and recompile it.
KK	patch -d /usr/local/lib/emacs/lisp < nnspool-diff
KKThis makes the functions and other stuff you've done to your .emacs
KKuseful.  The patch causes nnspool to look for a user-defined function
KKwhich supplies the name of the active file to the nnspool functions.
KKThe nnspool-personal-active-file function builds a new active file
KKevery time called, and then returns the name of that file to the
KKcalling function.  (People within Ohio State Computer Science can skip
KKthis patch-&-recompile step, since it's already done.)
KK
KK[7] SAVE COPIES OF YOUR .newsrc AND RELATED FILES.  One never knows
KKjust how far one might screw oneself up, and it would be nice to be
KKable to recover from disaster, don't you agree?
KK
KK[8] Install a .forward like the one supplied.  Make it look right for
KKyour home directory and login name, of course.  Send yourself some
KKtest mail, and check to see that it gets delivered appropriately in
KKyour new pseudonewsgroup hierarchy.
KK
KK[9] Fire up a fresh Emacs and invoke GNUS.  You will observe that
KKgetting new news (at the stage "Reading active file...") takes a bit
KKlonger, because nnspool-personal-active-file spawns make-active, which
KKis hacking up a fresh active file for you.  You will also observe that
KKyou are magically subscribed to a whole slew of new "newsgroups" which
KKdon't really exist.
KK
KK[10] You know that weird guy that's been writing you mail every other
KKday for the last month?  The one you never want to hear from again?
KKNow you don't have to hear from him - ever.  The first time you see
KKmail from him (probably in personal.general), do this:
KK	M-k
KK	C-c C-k C-a
KK	C-c C-c
KKHe's history as far as mail from him is concerned.
KK
KKBug reports, improvements, suggestions
KKto the scheme => karl@cis.ohio-state.edu.
GNUSMAIL
if test 4451 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 4451 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'.emacs'" '(1074 characters)'
if test -f '.emacs'
then
	echo shar: will not over-write existing file "'.emacs'"
else
sed 's/^KK//' << \GNUSMAIL > '.emacs'
KK;
KK; GNUS things: Mail and news in one incantation.
KK; Definitely not for the weak of heart.
KK;
KK(setq gnus-nntp-server "")
KK(setq gnus-nntp-service nil)
KK(load "gnus") ; I don't see any point in autoloading this.
KK(setq gnus-ignored-headers
KK      (concat gnus-ignored-headers
KK	      "\\|^Errors-To:\\|^Precedence:\\|^Received:\\|^UNIX-From:"))
KK(setq nnspool-spool-directory "~/Memos/") ; note trailing `/'
KK(defvar karl-active-file-maker "~/bin/make-active"
KK  "*Personalized active file generator")
KK(defvar karl-active-file "~/Memos/active"
KK  "*Personalized active file")
KK(defun nnspool-personal-active-file ()
KK  "Returns a filename string for a personal active file."
KK  (call-process karl-active-file-maker nil nil nil
KK		; remaining stuff is args to the active file maker.
KK		nnspool-spool-directory
KK		"personal/general" "personal/bob" "personal/romig"
KK		"personal/george" "personal/paul" "personal/zwicky"
KK		"personal/northrup" "personal/uucp" "personal/karl"
KK		"personal/amanda"
KK		"list/general" "list/firearms/politics" "list/firearms"
KK		"mailer-daemon")
KK  karl-active-file
KK  )
GNUSMAIL
if test 1074 -ne "`wc -c < '.emacs'`"
then
	echo shar: error transmitting "'.emacs'" '(should have been 1074 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'.forward'" '(35 characters)'
if test -f '.forward'
then
	echo shar: will not over-write existing file "'.forward'"
else
sed 's/^KK//' << \GNUSMAIL > '.forward'
KK"|/n/dinosaur/0/karl/bin/pmd karl"
GNUSMAIL
if test 35 -ne "`wc -c < '.forward'`"
then
	echo shar: error transmitting "'.forward'" '(should have been 35 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'make-active'" '(903 characters)'
if test -f 'make-active'
then
	echo shar: will not over-write existing file "'make-active'"
else
sed 's/^KK//' << \GNUSMAIL > 'make-active'
KK#!/bin/cshe -f
KK#
KK# Builds a pseudoactive file which represents personal mail under
KK# $1 plus pseudonews in $1/{comp,soc,rec,talk,sci,soc,misc,...}.
KK#
KKif ($#argv < 2) then
KK	echo make-active: usage: make-active directory-subdirs...
KK	exit 87
KKendif
KKset dir=$1
KKshift
KKset active=$dir/active
KKset nactive=/usr/lib/news/active
KK#
KK# Find directories and their associated .last files.  Then take
KK# base dirnames, change to newsgroup name format, and build the
KK# mail-based active file out of it all.
KK#
KKcd $dir
KKset list=($*)
KKset count=$#list
KKset nlist=()
KKset first=()
KKset last=()
KKforeach i ($list)
KK	set first=($first $i/.first)
KK	set last=($last $i/.last)
KK	set nlist=($nlist `echo $i | tr / .`)
KKend
KKcp /dev/null $active
KK@ i = 1
KKwhile ($i <= $count)
KK	echo $nlist[$i] `cat $last[$i]` `cat $first[$i]` n >> $active
KK	@ i ++
KKend
KK#
KK## Defunctitude:
KK# Now add the whole regular active file.
KKcat $nactive >> $active
KK#
KKexit 0
GNUSMAIL
if test 903 -ne "`wc -c < 'make-active'`"
then
	echo shar: error transmitting "'make-active'" '(should have been 903 characters)'
fi
chmod +x 'make-active'
fi # end of overwriting check
echo shar: extracting "'nnspool-diff'" '(1043 characters)'
if test -f 'nnspool-diff'
then
	echo shar: will not over-write existing file "'nnspool-diff'"
else
sed 's/^KK//' << \GNUSMAIL > 'nnspool-diff'
KKdiff -c nnspool.el~ nnspool.el
KK*** nnspool.el~	Thu Jul  6 09:29:59 1989
KK--- nnspool.el	Thu Jul  6 11:40:39 1989
KK***************
KK*** 242,248 ****
KK  (defun nnspool-request-list ()
KK    "List valid newsgoups."
KK    (save-excursion
KK!     (nnspool-find-file nnspool-active-file)))
KK  
KK  (defun nnspool-request-last ()
KK    "Set current article pointer to the previous article
KK--- 242,248 ----
KK  (defun nnspool-request-list ()
KK    "List valid newsgoups."
KK    (save-excursion
KK!     (nnspool-find-file (nnspool-pick-active-file))))
KK  
KK  (defun nnspool-request-last ()
KK    "Set current article pointer to the previous article
KK***************
KK*** 358,360 ****
KK--- 358,368 ----
KK        (setq idx (1+ idx)))
KK      string
KK      ))
KK+ 
KK+ (defun nnspool-pick-active-file ()
KK+   "Return a filename string for the active file."
KK+   ;; If the user has defined his own active file creator,
KK+   ;; use it.  Otherwise, return the "standard" active file.
KK+   (if (fboundp 'nnspool-personal-active-file)
KK+       (nnspool-personal-active-file)
KK+     nnspool-active-file))
GNUSMAIL
if test 1043 -ne "`wc -c < 'nnspool-diff'`"
then
	echo shar: error transmitting "'nnspool-diff'" '(should have been 1043 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'pmd'" '(2129 characters)'
if test -f 'pmd'
then
	echo shar: will not over-write existing file "'pmd'"
else
sed 's/^KK//' << \GNUSMAIL > 'pmd'
KK#!/bin/csh -f
KK#
KK# Configuration.
KKumask 077
KKset path=(/usr/local/bin /usr/ucb /bin /usr/bin /etc /usr/etc)
KKset dir=/n/dinosaur/0/karl/Memos
KKset tmp=/usr/tmp/karl-$$
KK#
KK# Save the mail momentarily, and set a Lines: count in it.
KKcat - > $tmp
KKset totlen=`wc -l < $tmp`
KKset lines=(`sed '1,/^$/d' < $tmp | wc -l`)
KK@ headerlen = $totlen - $lines
KKed - $tmp << EOF >& /dev/null
KK$headerlen
KKi
KKLines: $lines
KK.
KKw
KKq
KKEOF
KK#
KK# Deduce headers appropriately.
KKset nonomatch
KKset from=(`head -1 $tmp`)
KKif ($#from < 2) then
KK	echo Bogus mail: From_ line has $#from items.
KK	sed -e 's/^/|/' < $tmp | sed -e '/^$/,$d'
KK	exit 23
KKendif
KKset from=(`echo "$from[2]" | sed -e 's/@.*//' -e 's/\(.*\)!\(.*\)/\2/'`)
KKif ("$from" =~ *-[Rr][Ee][Qq][Uu][Ee][Ss][Tt]*) then
KK	# Mailing list stuff.
KK	switch ("$from")
KK		case firearms-request:
KK			set subdir=list/firearms
KK			breaksw
KK		case firearms-politics-request:
KK			set subdir=list/firearms/politics
KK			breaksw
KK		default:
KK			set subdir=list/general
KK			breaksw
KK	endsw
KKelse if ("$from" =~ *[Mm][Aa][Ii][Ll][Ee][Rr]-[Dd][Aa][Ee][Mm][Oo][Nn]*) then
KK	# BEWARE: Some (bad) csh's can't cope with the preceding regexp.
KK	# Mailer-Daemon bounces.
KK	set subdir=mailer-daemon
KKelse
KK	# Personal mail
KK	if ((-e $dir/personal/"$from") && (-d $dir/personal/"$from")) then
KK		set subdir=personal/"$from"
KK	else
KK		set subdir=personal/general
KK	endif
KKendif
KKunset nonomatch
KK#
KK# Now save the mail appropriately.
KK#
KK# Lock.
KKset keyf=$dir/$subdir/KEY
KKset seqfile=$dir/$subdir/.last
KKset success=no
KK@ retry = 0
KKwhile (($retry < 10) && ($success == no))
KK	rm $keyf
KK	if ($status == 0) then
KK		# The key was there when we wanted it.
KK		set success=yes
KK	else
KK		# The key was not there - someone else
KK		# had already taken it.  Wait and retry.
KK		sleep 10
KK		@ retry ++
KK	endif
KKend
KK#
KK# If we fall out to here without success, we failed 10 times to
KK# get the key.  Result: Who cares?  We'll go forward anyway,
KK# and re-assert the key when we're done.
KK#
KK# Get next filenumber.
KKset seq=(`cat $seqfile`)
KK@ seq ++
KKecho $seq > $seqfile
KK#
KK# Unlock and save.
KKtouch $keyf
KKsed -e '1s/^From /UNIX-From: /' < $tmp > $dir/"$subdir"/$seq
KK#
KK# Clean up and exit.
KKrm -f $tmp
KKexit 0
GNUSMAIL
if test 2129 -ne "`wc -c < 'pmd'`"
then
	echo shar: error transmitting "'pmd'" '(should have been 2129 characters)'
fi
chmod +x 'pmd'
fi # end of overwriting check
#	End of shell archive
exit 0