[net.sources] biff

larry@geowhiz.UUCP (Larry McVoy) (07/31/86)

[munch]

This is a hack that I wrote to replace biff(1) as available under the BSD 
systems.  It runs one process per user instead of just a daemon.  Instead
of listening to a socket it looks at yor mail file every 30 seconds to see
if it got bigger.  If so it will print out the header lines from the last
message in the file (bug: it won't catch multiple messages that come in at 
the same time, just the last one).  

The difference between biff and MAILCHECK is that biff is asynchronous -- it
will interrupt whatever you are doing (not really, you can control it with
mesg(1)) to tell you abot new mail.

You have to get root to install it unless you can create a dir in /usr/spool.

--- cut here ---
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# biff.biff biff.c biff.l biff.readme

echo x - biff.biff
cat > "biff.biff" << '//E*O*F biff.biff//'
^[>]From
^Subject
//E*O*F biff.biff//

echo x - biff.c
cat > "biff.c" << '//E*O*F biff.c//'
/* biff.c 1.3 - main, shh, cleanup, syserr, isthere, ping */

/*
 * biff -- be informed about incoming mail
 *
 * usage biff [y] [n]
 * bugs: it uses a stupid alg to see if you have new mail: stat the file,
 * sleep TIMEOUT seconds, stat it again & if it got bigger then new mail.
 * This can occassionally miss new mail.
 *
 * You can have this code, hack it, claim it as your own, and you don't have 
 * to keep any stupid little copyright notice at the top.  Can you deal?
 */

# include	<stdio.h>
# include	<varargs.h>
# include	<signal.h>
# include	<sys/types.h>
# include	<sys/stat.h>
# define	TIMEOUT		30
# define	isthere(x)	(access(x, 0) == 0)
# define	ping(id)	kill(id, 0)
# define	shhh(x)		(!(x.st_mode & 02)  &&  !getenv("BIFF"))
/* #define	strrchr	rindex	/* for 4.x */

char* biff =    "/usr/spool/biff/llllllllll0";		/* global for cleanup */

main(argc, argv)
    char** argv;
{
    char* biffdir = "/usr/spool/biff";
    char* mail =    "/usr/mail/llllllllll0";
    char* sysmatch = "/usr/skel/.biff";
    char* match;
    char usrmatch[100], line[100];
    int sh_id = getppid();
    struct stat old, new;
    int cleanup();
    FILE* popen();
    FILE* info;

    sprintf(strrchr(biff, '/') + 1, "%.10s", getlogin());
    sprintf(strrchr(mail, '/') + 1, "%.10s", getlogin());
    sprintf(usrmatch, "%s/.biff", getenv("HOME"));

    if (!isthere(biffdir)) 
	syserr("%s: can't access %s\n", argv[0], biffdir);

    if (argc == 1) {
	if (!isthere(biff) == 0)
	    printf("is y\n");
	else
	    printf("is n\n");
	exit(0);
    }
    else if (argc == 2 && *argv[1] == 'n') {
	    unlink(biff);
	    exit(0);
    }
    else if (*argv[1] != 'y') {
	fprintf(stderr, "usage: %s [y] [n]\n", argv[0]);
	exit(1);
    }
    signal(SIGINT, cleanup);
    signal(SIGQUIT, cleanup);
    signal(SIGHUP, cleanup);
    signal(SIGTERM, cleanup);
    close(creat(biff, 0644));
    if (fork())
	exit(0);
    if (stat(mail, &new)) {
	fprintf(stderr, "%s: can't stat %s\n", argv[0], mail);
	unlink(biff);
	exit(2);
    }
    while(1) {
	sleep(TIMEOUT);
	if (ping(sh_id)) {
	    unlink(biff);
	    exit(0);
	}
	if (!isthere(biff))
	    exit(0);
	stat(mail, &new);
	if (shhh(new))
	    continue;
	if (new.st_size > old.st_size) {
    	    if (isthere(usrmatch)) 
		match = usrmatch;
    	    else if (isthere(sysmatch))
		match = sysmatch;
    	    else
		match = "'^[>]*From'";		/* must be single quoted */
	   printf("\007\r\nNew mail has arrived...\r\n");
	   sprintf(line, "revgrep -t '^[>]*From' %s | egrep %s%s\n", 
		mail, match[0] == 047 ? "" : "-f ", match);
	   info = popen(line, "r");
	   while (fgets(line, sizeof(line), info)) {
		printf("%s\r", line);
	   }
	    pclose(info);
	}
    	old = new;		/* struct assign */
    }
}

cleanup()
{
    unlink(biff);
    exit(0);
}

/* from "Advanced Unix Programming" (p13), M. Rochkind (modified) */
syserr(va_alist)
    va_dcl
{
    extern int errno, sys_nerr;
    extern char* sys_errlist[];
    va_list ap;
    char* fmt;

    va_start(ap);
    fmt = va_arg(ap, char*);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fprintf(stderr, "\terrno: (%d", errno);
    if (errno > 0  &&  errno < sys_nerr)
	fprintf(stderr, "; %s)\n", sys_errlist[errno]);
    else
	fprintf(stderr, ")\n");
    exit(errno ? errno : 1);
}
//E*O*F biff.c//

echo x - biff.l
cat > "biff.l" << '//E*O*F biff.l//'
.TH BIFF 1 LOCAL
.ad b
.SH NAME
biff \- be informed of new mail arrivals
.SH SYNOPSIS
\fBbiff\fR [\fBn\fR] [\fBy\fR]
.SH DESCRIPTION
.I Biff
will asynchronously print out the header lines of your latest mail
message soon after it arrives.  Biff without arguments tells you if it
is on or off.  Biff is not completly asynchronous, it will run but be
silent if messages are off (ie it's quiet when you're in vi).  
You may make biff fully asynchronous by creating an 
environment variable \fIBIFF\fR with an arbitrary value.
.PP
The header lines printed are matched based on a list of possibilities
that the user can create and store in a file called \fI$HOME/.biff\fR.
For example, to match the \fIFrom\fP, \fITO\fP and \fISubject\fP lines, 
the file contains
.sp 1
^[>]*From
.br
^To
.br
^Subject
.br
.sp 1
The carats (^) insure that only the header lines are matched.
.PP
If \fI$HOME/.biff\fR is not accessible then \fI/usr/skel/.biff\fR is
used.  The fields in that file are whatever the systems administrator
choses (From & Subject should be there and those make a pretty good
default). If neither \fI$HOME/.biff\fR nor \fI/usr/skel/.biff\fR is
accessible then the first \fIFrom\fR line is printed.
.PP
Available options are:
.TP
.B y
Turn on biff.
.TP
.B n
Turn off biff.
.SH BUGS
.I Biff 
should really be a daemon but that only works if mail comes in over a socket.
.PP
It would be nice if you could be informed about mail coming into multiple
mailboxes.
.SH FILES
.TP
/usr/spool/biff
directory for flag files.  Must be writable by users.
.TP
/usr/spool/biff/$LOGFILE	
flag file
.TP
$HOME/.biff				
list of fields, one per line, to print.
.TP
/usr/skel/.biff				
default list of fields, one per line, to print.
.SH "SEE ALSO"
revgrep(1), egrep(1), mesg(1), setenv(1)
.SH AUTHOR
Larry McVoy (idea from 4.2 biff(1))
//E*O*F biff.l//

echo x - biff.readme
cat > "biff.readme" << '//E*O*F biff.readme//'
OK, here's how you install it:

cc -O biff.c -o biff
chown bin biff; chgrp bin biff; mv biff /usr/local/bin
mkdir /usr/spool/biff; chmod 777 /usr/spool/biff
cp biff.biff /usr/skel/.biff
cp biff.l /usr/man/manl (or whatever you man local dir is)

Complain to larry@geowhiz.uucp or mcvoy@rsch.wisc.edu if it doesn't work.
//E*O*F biff.readme//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      2      2     18 biff.biff
    132    434   3263 biff.c
     65    302   1819 biff.l
      9     47    320 biff.readme
    208    785   5420 total
!!!
wc  biff.biff biff.c biff.l biff.readme | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
-- 
Larry McVoy
-----------
Arpa:  mcvoy@rsch.wisc.edu                              
Uucp:  {seismo, topaz, harvard, ihnp4}!uwvax!geowhiz!larry      

"Just remember, wherever you go -- there you are."
 	-Buckaroo Banzai