Subject: v25i028: fromwho - yet another replacement for from(1) Newsgroups: comp.sources.unix Approved: vixie@pa.dec.com Submitted-By: jearls@blackbird.csc.calpoly.edu Posting-Number: Volume 25, Issue 28 Archive-Name: fromwho This is a new 'from' program. Instead of just listing who your mail is from, it tells you how many total messages, how many are new, and for each person you have mail from, tells how many messages they sent, how many are new, and lists the subjects of the messages. [ This program requires an ANSI-compliant C compiler to build. --Nick ] #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'CHANGES' <<'END_OF_FILE' XRevision 1.3 91/12/03 11:14:15 jearls XPATCH3: Added `-v' option to display the version number. X XRevision 1.2 91/11/23 12:49:40 jearls XPATCH2: Fixed miscellanous bugs, added '-n' option to XPATCH2: list only new mail. X XRevision 1.1 91/10/19 17:06:12 jearls XPATCH1: Added support for AIX and other systems that don't XPATCH1: use a mail spool. X XRevision 1.0 91/10/19 14:40:25 jearls XInitial revision X END_OF_FILE if test 409 -ne `wc -c <'CHANGES'`; then echo shar: \"'CHANGES'\" unpacked with wrong size! fi # end of 'CHANGES' fi if test -f 'INSTALL' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'INSTALL'\" else echo shar: Extracting \"'INSTALL'\" \(2860 characters\) sed "s/^X//" >'INSTALL' <<'END_OF_FILE' XInstallation instructions for fromwho: X X XUpdating the `Tweakables' section of the Makefile: X X There are seven parameters in the Makefile that affect X the installation of fromwho: X X CC This is the C compiler you will compile X with. It must be ANSI-C compliant. X X CCFLAGS What default flags to send to the C X compiler. On some systems you may need X to add '-Dindex=strchr' to this line. X X MAIL How to find the incoming mailbox for a X user. If this is left blank, then the X incoming mailbox will be X X /usr/spool/mail/ X X If incoming mail is kept in a different X directory "/xyz/", then X set MAIL to X X -DMAILDIR=\"/xyz/\" X X If incoming mail is set up in some other X fashion (i.e. all mailboxes not in the X same directory) then set MAIL to X X -DMAILFILE=\"\" X X where is the path to the mail- X box file. may contain a ~ for X the user's home directory and a * for the X user's login name. For example, AIX keeps X it's incoming mail in the file `.newmail' X in the user's home directory. For this X setup, MAIL would be defined as X X -DMAILFILE=\"~/.newmail\" X X STRIP If you want the executable stripped after X being compiled, set this to `strip', other- X wise, set it to `touch'. X X SECTION The section of the manual to install as. X Normally either `1' (one), `l', or `n'. X X BIN The directory to install the executable X in. X MAN The directory to install the manpage in. X (The manpage will actaully be instaled X one level below this, in a directory called X manX, where X is the SECTION defined above) X X After setting up these parameters, build the executable X and manpage with `make' with no parameters. If there are X any problems in compiling, send mail to the address given X at the end of this file describing the errors and the X system you're trying to install on, and be prepared to X send a script of the `make' (but don't do this until I ask X for it :) X X If the executable builds correctly, test it out by running X `make verify'. It will run through the various test mail- X boxes to insure that the parameters work correctly and X that it is reading the mailboxes correctly. It will also X create a shell script to attempt to verify that the MAIL X setting is correct. X X When you are satisfied that it works correctly, type `make X info' to check that the directories are correct, and `make X install'. If the directories aren't correct, modify the X Makefile and re-make. X XIf you have any problems compiling or installing fromwho, Xmail a description of the problems encountered and a Xdescription of the system on which you are trying to Xinstall to: X X jearls@blackbird.csc.calpoly.edu X XBe prepared to send a script of the `make' process -- but Xnot until I ask you for it. :) X XSuggestions and comments are also welcome. END_OF_FILE if test 2860 -ne `wc -c <'INSTALL'`; then echo shar: \"'INSTALL'\" unpacked with wrong size! fi # end of 'INSTALL' fi if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(507 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X CHANGES 1 Changes since v1.0 X INSTALL 1 Installation instructions X MANIFEST 1 This file X Makefile 1 Build file for 'make' X README 1 Description of fromwho X fromwho.c 1 The program source. X fromwho.man 1 Template for the manpage. X patchlevel.h 1 The current patch level. END_OF_FILE if test 507 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(2304 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# X# Makefile for $(PROG) X# X# (See INSTALL file for installation instructions) X# X# Tweakables: X# X X# Your CC must grok ANSI C XCC = gcc X#CC = cc X XCCFLAGS = -O X#CCFLAGS = -g X XMAIL = X#MAIL = -DMAILDIR=\"/usr/mail/\" X#MAIL = -DMAILFILE=\"~/.newmail\" X XSTRIP = strip X#STRIP = touch # use if you don't want the X# # executable strip'd X XSECTION = n X XBIN = /usr/local/bin XMAN = /usr/local/man X X########### Don't change anything below this line ########## X XPROG = fromwho XOBJS = fromwho.o XSRCS = fromwho.c patchlevel.h XMANPAGE = $(PROG).$(SECTION) XMANDIR = $(MAN)/man$(SECTION) XCFLAGS = $(CCFLAGS) $(MAIL) X Xall: $(PROG) $(MANPAGE) X X$(PROG): $(OBJS) X $(CC) $(CFLAGS) -o $(PROG) $(OBJS) \ X $(LDFLAGS) $(LIBS) X $(STRIP) $(PROG) X X$(MANPAGE): $(PROG).man sedscript X ./sedscript < $(PROG).man > $(MANPAGE) X Xinfo: X @echo X @echo '`'"make install' will install" \ X "$(PROG) into $(BIN)" X @echo "and $(MANPAGE) into $(MANDIR)." X @echo X Xinstall: $(PROG) $(MANPAGE) X -if test ! -d $(BIN) ; then mkdir $(BIN) ; fi X -if test -r $(BIN)/$(PROG) ; then rm -f $(BIN)/$(PROG).old ; mv $(BIN)/$(PROG) $(BIN)/$(PROG).old ; fi X cp $(PROG) $(BIN)/$(PROG) X -if test ! -d $(MAN) ; then mkdir $(MAN) ; fi X -if test ! -d $(MANDIR) ; then mkdir $(MANDIR) ; fi X -if test -r $(MANDIR)/$(MANPAGE) ; then rm -f $(MANDIR)/$(MANPAGE).old ; mv $(MANDIR)/$(MANPAGE) $(MANDIR)/$(MANPAGE).old ; fi X cp $(MANPAGE) $(MANDIR)/$(MANPAGE) X Xsedscript: Makefile X @rm -f sedscript X @echo "#!/bin/sh" > sedscript X @echo 'set `date`' >> sedscript X @echo 'DATE="$$3 $$2 $$6"' >> sedscript X @echo 'set '"'"'"'"'"'`grep "Revision" patchlevel.h`'"'"'"'"'" >> sedscript X @echo 'VERSION="$$3"' >> sedscript X @echo 'sed "s/%V%/$$VERSION/g" | sed "s/%D%/$$DATE/g" | sed "s/%S%/$(SECTION)/g"' >> sedscript X @chmod 700 sedscript X Xclean: X rm -f *.o $(PROG) $(MANPAGE) sedscript X Xverify: $(PROG) X X############################################################ X# X# The following are what I use to make a distribution. They X# probably won't be very helpful to you. X# X############################################################ X Xpackage: $(SRCS) $(PROG).man Makefile MANIFEST CHANGES README INSTALL X @echo "Creating package..." X makekit -m X XCHANGES: $(SRCS) X sed '1,/$$Log:/d' patchlevel.h | sed '/\*\//,$$d' | sed 's/^....//' > CHANGES END_OF_FILE if test 2304 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(693 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' X fromwho -- 'from' with extras X X This is a new 'from' program. Instead of just listing who your mail is X from, it tells you how many total messages, how many are new, and X for each person you have mail from, tells how many messages they sent, X how many are new, and lists the subjects of the messages. X X This code is public domain. I might keep updating it, or I might forget X about it ]:) But feel free to mail comments / suggestions to me at X jearls@data.acs.calpoly.edu X X Usage: X fromwho [-l] [-s] [-n] [-f []] X X -f Sets the mailbox to read from. X -l Turns listing of users/subjects off. X -n Lists only new mail. X -s Turns listing of subjects off. END_OF_FILE if test 693 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'fromwho.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'fromwho.c'\" else echo shar: Extracting \"'fromwho.c'\" \(7101 characters\) sed "s/^X//" >'fromwho.c' <<'END_OF_FILE' X/* X fromwho -- 'from' with extras X X This is a new 'from' program. Instead of just listing who your mail is X from, it tells you how many total messages, how many are new, and X for each person you have mail from, tells how many messages they sent, X how many are new, and lists the subjects of the messages. X X This code is public domain. I might keep updating it, or I might forget X about it ]:) But feel free to mail comments / suggestions to me at X jearls@data.acs.calpoly.edu X X Usage: X fromwho [-l] [-s] [-n] [-f []] X X -l Turns listing of users/subjects off. X -s Turns listing of subjects off. X -f Sets the mailbox to read from. X -n List only new mail X X REVISION INFORMATION X X $Revision: 1.3 $ X X $Author: jearls $ X $Date: 91/12/03 11:14:33 $ X X Modification Log: X X $Log: fromwho.c,v $ X Revision 1.3 91/12/03 11:14:33 jearls X PATCH3: Added `-v' option to display the version number. X X Revision 1.2 91/11/23 12:49:37 jearls X PATCH2: Fixed miscellanous bugs, added '-n' option to X PATCH2: list only new mail. X X Revision 1.1 91/10/19 17:05:17 jearls X PATCH1: Added support for AIX and other systems that don't X PATCH1: use a mail spool. X X Revision 1.0 91/10/19 14:40:24 jearls X Initial revision X X*/ X X#include X#include X#include X#include X X#include "patchlevel.h" X X#ifndef MAILDIR X#define MAILDIR "/usr/spool/mail/" X#endif MAILDIR X Xtypedef struct { X int msgs; int new; X} mailinfo; X Xtypedef struct subjnode { X char *subj; X int new; X struct subjnode *next; X} subjnode; X Xtypedef struct userinfo { X char *name; X mailinfo info; X X subjnode *subjs, *lastsub; X X struct userinfo *next; X} userinfo; X Xchar *strdup(char *p) X{ X char *s = (char *)malloc(1+strlen(p)); X if (s) X (void)strcpy(s, p); X return(s); X} X Xchar *strdup2(char *p, char *q) X{ X char *s = (char *)malloc(1+strlen(p)+strlen(q)); X if (s) X (void)strcat(strcpy(s, p), q); X return(s); X} X X/* translate MAILFILE into a real path */ Xchar *translate(char *mf, char *n, char *h) X{ X int len; X char *s, *p, *q; X X len = strlen(mf); X if (*mf == '~') X len += strlen(h) - 1; X p = mf; X while (p = index(p, '*')) { X p++; X len += strlen(n)-1; X } X if (s = (char *)malloc(len+1)) { X p = s; X if (*mf == '~') { X while (*h) X *(p++) = *(h++); X mf++; X } else X p = s; X while (*mf) X if ((*p = *(mf++)) == '*') { X q = n; X while (*q) X *(p++) = *(q++); X } else X p++; X *p = '\0'; X } X return(s); X} X Xmailinfo mailstats(FILE *f, userinfo **ui) X{ X mailinfo *mp, mi; X char buf[1024]; X int stat, lp; X userinfo *p, *b, *n; X X mi.msgs = mi.new = 0; X X stat = 0; X while (fgets(buf, 1024, f)) { X if ((!stat) && (!strncmp(buf, "From ", 5))) { X for (lp=0; buf[lp+5] != ' '; lp++) X buf[lp] = buf[lp+5]; X buf[lp] = '\0'; X stat = 1; X mi.msgs++; X p = *ui; b = (userinfo *)NULL; X while (p && (strcmp(buf, p->name) > 0)) X p = (b=p)->next; X if (!p || (strcmp(buf, p->name))) { X n = (userinfo *)malloc(sizeof(userinfo)); X n->name = strdup(buf); X n->info.msgs = n->info.new = 0; X n->subjs = n->lastsub = (subjnode *)NULL; X n->next = p; X if (b) X b->next = n; X else X *ui = n; X p = n; X } X mp = &(p->info); X mp->msgs++; X if (p->lastsub) X p->lastsub = (p->lastsub->next = (subjnode *)malloc(sizeof(subjnode))); X else X p->subjs = p->lastsub = (subjnode *)malloc(sizeof(subjnode)); X p->lastsub->subj = (char *)NULL; X p->lastsub->new = 0; X p->lastsub->next = (subjnode *)NULL; X } X if ((stat) && (!strncmp(buf, "Subject:", 8))) X if (!p->lastsub->subj) { X buf[strlen(buf)-1] = '\0'; X p->lastsub->subj = strdup(buf+9); X } X if ((stat) && (!strncmp(buf, "Status:", 7))) { X stat++; X } X if ((stat) && (*buf == '\n')) { X if (stat == 1) { X mi.new++; X mp->new++; X p->lastsub->new = 1; X } X stat = 0; X } X } X X return(mi); X} X Xvoid usage(char *name) X{ X fprintf(stderr, "usage: %s [-l] [-s] [-n] [-f []]\n", name); X exit(-1); X} X Xint main(int argc, char **argv) X{ X mailinfo mi; X userinfo *ui = (userinfo *)NULL, *p; X char *mbox, *prog, myname[9], *home; X extern char *getenv(char *); X int flag, listflag = 1, subjflag = 1, newflag = 0; X int lp; X FILE *f; X subjnode *sp, *tp; X X extern int errno; X X/* get user name and home directory */ X f=(FILE *)fopen("/etc/utmp", "r"); X if (f==(FILE *)NULL) { X perror("Can't open /etc/utmp"); X exit(1); X } X fseek(f, 8+sizeof(struct utmp)*ttyslot(), 0); X myname[8]='\0'; X if (fread(myname, 8, 1, f) < 8) { X if (!(home = getenv("USER"))) X if (!(home = getenv("USERNAME"))) X if (!(home = getenv("LOGNAME"))) { X fprintf(stderr, "%s: Who are you?\n", *argv); X exit(1); X } X (void)strcpy(myname, home); X } X fclose(f); X X if (getpwnam(myname)) X home = strdup(getpwnam(myname)->pw_dir); X else X fprintf(stderr, "%s: Who are you?\n", *argv); X X#ifdef MAILFILE X mbox = translate(MAILFILE, myname, home); X#else X mbox = strdup2(MAILDIR, myname); X#endif X X/* parse arguments */ X prog = *argv; X while ((--argc) && (**(++argv) == '-')) { X switch(*(*argv+1)) { X case 'f' : (void)free(mbox); X if (--argc) X mbox = strdup(*(++argv)); X else { X mbox = strdup2(home, "/mbox"); X argc++; X } X break; X case 's' : subjflag = 0; X break; X case 'l' : listflag = 0; X break; X case 'n' : newflag = 1; X break; X case 'v' : puts("fromwho, by johnson earls. $Revision: 1.3 $"); X exit(0); X default : free(home); X free(mbox); X usage(prog); X } X } X X/* open the file */ X f = (FILE *)fopen(mbox, "r"); X if (f == (FILE *)NULL) { X perror(mbox); X free(home); X free(mbox); X exit(errno); X } X X/* get the info */ X mi = mailstats(f, &ui); X X (void)fclose(f); X X/* print everything out */ X if (newflag) X printf("%s contains %d new messages.\n", mbox, mi.new); X else X printf("%s contains %d messages, %d new.\n", mbox, mi.msgs, mi.new); X while (ui) { X if (listflag && (!newflag || ui->info.new)) X if (newflag) X printf(" %-36s: %d new messages.\n", ui->name, ui->info.new); X else X printf(" %-36s: %d messages, %d new.\n", ui->name, ui->info.msgs, ui->info.new); X for (sp = ui->subjs; sp; ) { X if (sp->subj) { X if (listflag && subjflag) X if (sp->new || !newflag) X printf(" %c %s\n", (sp->new ? '>' : ' '), sp->subj); X free(sp->subj); X } else X if (listflag && subjflag) X if (sp->new || !newflag) X printf(" %c \n", (sp->new ? '>' : ' ')); X sp = (tp=sp) -> next; X free(tp); X } X ui = (p=ui)->next; X free(p->name); X free(p); X } X X free(home); X free(mbox); X X exit(0); X} END_OF_FILE if test 7101 -ne `wc -c <'fromwho.c'`; then echo shar: \"'fromwho.c'\" unpacked with wrong size! fi # end of 'fromwho.c' fi if test -f 'fromwho.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'fromwho.man'\" else echo shar: Extracting \"'fromwho.man'\" \(1212 characters\) sed "s/^X//" >'fromwho.man' <<'END_OF_FILE' X.\" @(#)fromwho.%S% %V% created by: Johnson Earls %D% X.\" X.TH FROMWHO %S% "%D%" X.SH NAME X.B fromwho X.RB [ \-l ] X.RB [ \-s ] X.RB [ \-n ] X.RB [ \-f X.RI [ mailbox ]] X.SH DESCRIPTION X.LP X.B Fromwho Xis a new X.RB ` from ' Xutility that lists the contents of a mailbox arranged by the sender. XFor the mailbox and for each user, X.B fromwho Xreports the number of messages in the mailbox or from that user, and Xhow many of the messages are new. For each user, X.B fromwho Xthen lists the subjects of each message from that user, putting a X.B > Xbefore the messages that are new. X.SH OPTIONS X.TP X.B -l XTurns listing of users/subjects off. X.TP X.B -s XTurns listing of subjects off. X.TP X.B -n XList only new mail X.TP X.BI -f " " \fR[ mailbox \fR] XSets the mailbox to read from. If X.I mailbox Xis not specified then reads from the X.B mbox Xfile in your home directory. X.SH VERSION XThis describes X.B fromwho Xversion %V%. To find out what version you are running, Xtype X.B fromwho X.BR \-v . X.SH FILES X.PD 0 X.TP 20 X.RI /usr/spool/mail/ username X.TP X.RI /usr/mail/ username X.TP X$HOME/.newmail XOne of these should be your incoming mail spool. X.TP X$HOME/mbox XThe default mailbox for X.BR \-f . X.B X.SH SEE ALSO X.BR from (1) X\ X.BR mail (1) END_OF_FILE if test 1212 -ne `wc -c <'fromwho.man'`; then echo shar: \"'fromwho.man'\" unpacked with wrong size! fi # end of 'fromwho.man' fi if test -f 'patchlevel.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patchlevel.h'\" else echo shar: Extracting \"'patchlevel.h'\" \(595 characters\) sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE' X/* X fromwho patchlevel X X This includes the RCS log and the patchlevel define X X $Log: patchlevel.h,v $ X Revision 1.3 91/12/03 11:14:15 jearls X PATCH3: Added `-v' option to display the version number. X X Revision 1.2 91/11/23 12:49:40 jearls X PATCH2: Fixed miscellanous bugs, added '-n' option to X PATCH2: list only new mail. X X Revision 1.1 91/10/19 17:06:12 jearls X PATCH1: Added support for AIX and other systems that don't X PATCH1: use a mail spool. X X Revision 1.0 91/10/19 14:40:25 jearls X Initial revision X X*/ X X#define PATCHLEVEL 3 END_OF_FILE if test 595 -ne `wc -c <'patchlevel.h'`; then echo shar: \"'patchlevel.h'\" unpacked with wrong size! fi # end of 'patchlevel.h' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0