[comp.sources.misc] v06i099: Happy birthday to you...

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (05/15/89)

Posting-number: Volume 6, Issue 99
Submitted-by: rsalz@bbn.com (Rich Salz)
Archive-name: bday


There used to be a machine called oz.ai.mit.edu, and it used to have a
wizard who knew when it was your birthday.  He was a friendly wizard, not
at all deserving of the name "daemon," because he'd send you a nice little
note when that special day came around...

I had been saving one of my messages for some time, but only just now got
around to writing this program.  It reads a file with email names and
birthdays, and sends mail.  It should be run out of cron early every
morning.  If WEEKEND_HACK is #define'd, then on Friday bday will send mail
to people with birthdays on Saturday and Sunday.

The program uses getopt and sendmail, but hacking around the latter should
be pretty easy.

	/rich $alz

#! /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 <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  MANIFEST Makefile README bday.8 bday.c bdaylist bdaymesg
# Wrapped by rsalz@papaya.bbn.com on Mon May  8 11:17:21 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(459 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   1	Makefile for birthday greetings
X README                     1	Random stuff
X bday.8                     1	Manual page
X bday.c                     1	Send birthday greetings to users
X bdaylist                   1	Control file for bday
X bdaymesg                   1	Message file for bday
END_OF_FILE
if test 459 -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'\" \(489 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X##
X##  Makefile for birthday greetings.
X##  Written by Rich $alz, <rsalz@bbn.com>.  This package has no copyright.
X##  $Header: Makefile,v 1.1 89/05/08 11:07:14 rsalz Exp $
X##
X
XCFLAGS	= -O
X
Xall:		bday bday.8
X
Xinstall:	all
X	@echo "Install according to local convention"
X
Xclean:
X	rm -f foo core SHAR tags lint a.out *.o bday
X
Xbday:		bday.c
X	@rm -f bday
X	$(CC) -o bday $(CFLAGS) bday.c
X
X##  I know it fits in one kit, so...
Xkit:
X	@rm -f SHAR
X	makekit -m
X	@rm -f MANIFEST.BAK
X	@mv Part01 SHAR
END_OF_FILE
if test 489 -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'\" \(699 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
XThere used to be a machine called oz.ai.mit.edu, and it used to have a
Xwizard who knew when it was your birthday.  He was a friendly wizard, not
Xat all deserving of the name "daemon," because he'd send you a nice little
Xnote when that special day came around...
X
XI had been saving one of my messages for some time, but only just now got
Xaround to writing this program.  It reads a file with email names and
Xbirthdays, and sends mail.  It should be run out of cron early every
Xmorning.  If WEEKEND_HACK is #define'd, then on Friday bday will send mail
Xto people with birthdays on Saturday and Sunday.
X
XThe program uses getopt and sendmail, but hacking around the latter should
Xbe pretty easy.
X
X	/r$
END_OF_FILE
if test 699 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'bday.8' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bday.8'\"
else
echo shar: Extracting \"'bday.8'\" \(1936 characters\)
sed "s/^X//" >'bday.8' <<'END_OF_FILE'
X.\" $Header: bday.8,v 1.1 89/05/08 11:07:38 rsalz Exp $"
X.TH BDAY 8
X.SH NAME
Xbday \- birthday daemon
X.SH SYNOPSIS
X.B bday
X[
X.BI \-b birthdays
X] [
X.BI \-m message
X] [
X.BI \-r report_to
X] [
X.BI \-s sendmail_cmd
X]
X.SH DESCRIPTION
X.I Bday
Xis a program intended to be run every day out of
X.IR cron (1).
XIt reads a file containing a list of user names and birthdays,
Xand sends a message to anyone whose birthday is today.
XThe ``\-r'' option may be used to specify a person who should receive
Xa report whenever a greeting is sent.
X.PP
XThe ``\-b'' option may be used to specify the file containing the list
Xof people's birthdays; the default is
X.IR /usr/adm/bdaylist .
XThis file should consist of a series of lines of three fields, each separated
Xby whitespace:
X.RS
X.nf
X.ta \w'#emailname   'u +\w'month   'u
X#emailname	month	day
Xrsalz	2	10
X.fi
X.RE
XThe first column is the name of the person to send the email message to.
XThe second column is the month in which the person's birthday occurs;
X1 is for January, and so on up to 12 for December.
XThe third column is the day of the month on which their birthday occurs.
XBlank lines, and any text after a pound sign is ignored.
X.PP
XThe ``\-m'' option may be used to specify the file with the birthday message;
Xthe default is
X.IR /usr/adm/bdaymesg .
XThis is useful, for example, to send different greetings to people who's
Xbirthday falls on a weekend.
X.PP
XThe command used to send the mail may be changed by giving the ``\-s''
Xoption; this is useful for debugging.
XNote that the details of sending the message (such as the mail headers)
Xare compiled into the code, but that's okay because this is just a short
Xfun hack, anyhow.
X.SH AUTHOR
XWritten by Rich $alz, <rsalz@bbn.com>, after receiving just such a message
Xfrom one of the machine at MIT.  This package has no copyright.
X.SH FILES
X.ta \w'/usr/adm/bdaylist   'u
X/usr/adm/bdaylist	Default birthday list
X/usr/adm/bdaymesg	Default birthday message
END_OF_FILE
if test 1936 -ne `wc -c <'bday.8'`; then
    echo shar: \"'bday.8'\" unpacked with wrong size!
fi
# end of 'bday.8'
fi
if test -f 'bday.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bday.c'\"
else
echo shar: Extracting \"'bday.c'\" \(6081 characters\)
sed "s/^X//" >'bday.c' <<'END_OF_FILE'
X/*
X**  When you care enough to send the very best....
X**  Written by Rich $alz, <rsalz@bbn.com>, after seeing the MIT program
X**  in action.  This package has no copyright.
X*/
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <time.h>
X#include <fcntl.h>
X#include <sgtty.h>
X#ifndef	lint
X#ifndef	SABER
Xstatic char RCS[] =
X	"$Header: bday.c,v 1.1 89/05/08 11:13:22 rsalz Exp $";
X#endif	/* SABER */
X#endif	/* lint */
X
X/* Compilation control. */
X#define STRCHR		strchr		/* Maybe index			*/
X#define STRRCHR		strrchr		/* Maybe rindex			*/
X#undef WEEKEND_HACK			/* Work harder on Friday?	*/
X
X/* Manifest constants. */
X#define TRUE		1
X#define FALSE		0
X#define LEN		128
X#define FROM		"The Birthday Wizard <wizard@pineapple.bbn.com>"
X
X/* Syntactic sugar. */
X#define WHITE(c)	((c) == ' ' || (c) == '\t')
X#define Fprintf		(void)fprintf
X#ifdef	lint
X#undef	putc
X#endif	/* lint */
X
X
X/* Global data. */
Xstatic char	*progname;
Xstatic char	*ReportTo	= NULL;
Xstatic char	*Birthdays	= "/usr/adm/bdaylist";
Xstatic char	*Message	= "/usr/adm/bdaymesg";
Xstatic char	*sendmail	= "/usr/lib/sendmail.sun -t -oi";
X/*static char	*sendmail	= "/usr/lib/sendmail -t -oi"; */
X
X
X/* Linked in later. */
Xextern int	 errno;
Xextern char	*optarg;
Xextern time_t	 time();
Xextern char	*sprintf();		/* Too painful, my ass		*/
Xextern char	*strcpy();
Xextern char	*STRCHR();
Xextern char	*STRRCHR();
X
X
X/*
X**  Return the text string that corresponds to errno.
X*/
Xstatic char *
Xstrerror(e)
X    int		 e;
X{
X    extern int	 sys_nerr;
X    extern char	*sys_errlist[];
X    static char	 buff[30];
X
X    if (e > 0 && e < sys_nerr)
X	return sys_errlist[e];
X    (void)sprintf(buff, "Error code %d", e);
X    return buff;
X}
X
X
X/*
X**  Send mail to the person.
X*/
Xstatic void
XDoit(Name)
X    char	*Name;
X{
X    register FILE	*F;
X    register FILE	*M;
X    register int	 c;
X
X    /* Prep the pipe. */
X    if ((F = popen(sendmail, "w")) == NULL) {
X	Fprintf(stderr, "%s: Doit(%s) failed to popen \"%s\", %s.\n",
X		progname, Name, sendmail, strerror(errno));
X	return;
X    }
X
X    /* Write headers. */
X    Fprintf(F, "From: %s\n", FROM);
X    Fprintf(F, "To: %s\n", Name);
X    Fprintf(F, "Subject: Happy birthday!\n");
X    Fprintf(F, "\n");
X
X    /* Feed in the message. */
X    if (M = fopen(Message, "r")) {
X	while ((c = getc(M)) != EOF)
X	    (void)putc(c, F);
X	(void)fclose(M);
X    }
X
X    /* Close up.  Done? */
X    (void)pclose(F);
X    if (ReportTo == NULL)
X	return;
X
X    /* Report it. */
X    if ((F = popen(sendmail, "w")) == NULL) {
X	Fprintf(stderr, "%s: Doit(%s) couldn't report, %s.\n",
X		progname, Name, strerror(errno));
X	return;
X    }
X
X    /* Write the report. */
X    Fprintf(F, "To: %s\n", ReportTo);
X    Fprintf(F, "Subject: Greetings sent\n");
X    Fprintf(F, "\n");
X    Fprintf(F, "Sent a message to %s.\n", Name);
X
X    /* Close up.  Definitely done. */
X    (void)pclose(F);
X}
X
X
X/*
X**  Return pointer to first non-whitespace.
X*/
Xstatic char *
XSkip(p)
X    register char	*p;
X{
X    while (*p && WHITE(*p))
X	p++;
X    return p;
X}
X
X
X/*
X**  Check the list in the file for birthdays.
X*/
Xstatic void
XScanForWork(ThisMonth, ThisDay)
X    int			 ThisMonth;
X    int			 ThisDay;
X{
X    register FILE	*F;
X    register char	*p;
X    int			 UserMonth;
X    int			 UserDay;
X    char		 UserName[LEN];
X    char		 buff[LEN];
X
X    /* Open the list of birthdays. */
X    if ((F = fopen(Birthdays, "r")) == NULL) {
X	Fprintf(stderr, "%s: ScanForWork can't open \"%s\" for reading, %s.\n",
X		progname, Birthdays, strerror(errno));
X	return;
X    }
X
X    /* Scan all lines. */
X    while (fgets(buff, sizeof buff, F)) {
X	/* Clobber comment markers and newlines. */
X	if (p = STRCHR(buff, '#'))
X	    *p = '\0';
X	if (p = STRCHR(buff, '\n'))
X	    *p = '\0';
X
X	/* Skip blank or comment lines. */
X	if (buff[0] == '\0')
X	    continue;
X
X	/* First field is the user name. */
X	for (p = buff; *p && !WHITE(*p); p++)
X	    ;
X	*p++ = '\0';
X	(void)strcpy(UserName, buff);
X	if (UserName[0] == '\0')
X	    continue;
X
X	/* Second field is the month of the user's birthday. */
X	p = Skip(p);
X	if (*p == '\0')
X	    continue;
X	for (UserMonth = atoi(p); *p && isdigit(*p); p++)
X	    ;
X	if (UserMonth == 0)
X	    continue;
X
X	/* Third field is the date of the user's birthday. */
X	p = Skip(p);
X	if (*p == '\0')
X	    continue;
X	UserDay = atoi(p);
X	if (UserDay == 0)
X	    continue;
X
X	/* Is this one for us? */
X	if (UserMonth == ThisMonth && UserDay == ThisDay)
X	    Doit(UserName);
X    }
X
X    (void)fclose(F);
X}
X
X
X/*
X**  Make sure the file was specified and is readable.
X*/
Xstatic void
XCheckFile(Name, Purpose)
X    char	*Name;
X    char	*Purpose;
X{
X    FILE	*F;
X
X    if (Name == NULL) {
X	Fprintf(stderr, "%s:  No %s file given.\n", progname, Purpose);
X	exit(1);
X    }
X
X    if ((F = fopen(Name, "r")) == NULL) {
X	Fprintf(stderr, "%s: Can't open \"%s\" for %s, %s.\n",
X		progname, Name, Purpose, strerror(errno));
X	exit(1);
X    }
X
X    (void)fclose(F);
X}
X
X
Xmain(ac, av)
X    int		 ac;
X    char	*av[];
X{
X    int		 i;
X    time_t	 t;
X    struct tm	*tm;
X    char	*p;
X
X    /* Parse JCL. */
X    progname = (p = STRRCHR(av[0], '/')) ? p + 1 : av[0];
X    while ((i = getopt(ac, av, "b:m:r:s:")) != EOF)
X	switch (i) {
X	default:
X	    Fprintf(stderr, "Usage:\n\t%s %s\n",
X		    progname,
X		    "[-b list] [-m message] [-s sendcmd] [-r admin]");
X	    exit(1);
X	case 'b':
X	    Birthdays = optarg;
X	    break;
X	case 'm':
X	    Message = optarg;
X	    break;
X	case 'r':
X	    ReportTo = optarg;
X	    break;
X	case 's':
X	    sendmail = optarg;
X	    break;
X	}
X
X    /* Make sure the user gave us readable files. */
X    CheckFile(Birthdays, "birthday list");
X    CheckFile(Message, "message");
X
X    /* What time is it? */
X    t = time((time_t *)NULL);
X    tm = localtime(&t);
X    ScanForWork(tm->tm_mon + 1, tm->tm_mday);
X
X#ifdef	WEEKEND_HACK
X    /* If it's Friday, check for Saturday and Sunday birthdays. */
X    if (tm->tm_wday == 5) {
X	/* This is easy/sleazy, we avoid checking for end of month... */
X	t += 24 * 60 * 60;
X	tm = localtime(&t);
X	ScanForWork(tm->tm_mon + 1, tm->tm_mday);
X	t += 24 * 60 * 60;
X	tm = localtime(&t);
X	ScanForWork(tm->tm_mon + 1, tm->tm_mday);
X    }
X#endif	/* WEEKEND_HACK */
X
X    exit(0);
X}
END_OF_FILE
if test 6081 -ne `wc -c <'bday.c'`; then
    echo shar: \"'bday.c'\" unpacked with wrong size!
fi
# end of 'bday.c'
fi
if test -f 'bdaylist' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bdaylist'\"
else
echo shar: Extracting \"'bdaylist'\" \(227 characters\)
sed "s/^X//" >'bdaylist' <<'END_OF_FILE'
X##  Data file for birthday program.  Written by rsalz@bbn.com; this package
X##  has no copyright.
X##  $Header: bdaylist,v 1.1 89/05/08 11:07:48 rsalz Exp $
X##  Data lines look like this:
X##email			month	day
Xrsalz@bbn.com		2	10
END_OF_FILE
if test 227 -ne `wc -c <'bdaylist'`; then
    echo shar: \"'bdaylist'\" unpacked with wrong size!
fi
# end of 'bdaylist'
fi
if test -f 'bdaymesg' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bdaymesg'\"
else
echo shar: Extracting \"'bdaymesg'\" \(2880 characters\)
sed "s/^X//" >'bdaymesg' <<'END_OF_FILE'
X
X                                               YY
X                                              YYYY            YY
XHHHHH         HHHHH                             YYY          YYYY
XHHHHH         HHHHH                  PP PPPPPP   YYY         YYY
X  HHH         HHH                     PPP    PP   YYY       YYY
X  HHH         HHH    AAAAA  PP PPPPPP  PP    PP    YYY     YYY
X  HHH         HHH   AAAAAAA  PPP    PP PP    PP     YYY   YYY
X  HHH         HHH  AA     AA  PP    PP PP    PP      YYY YYY
X  HHH         HHH  AA     AA  PP    PP PPPPPPP        YYYYY
X  HHHHHHHHHHHHHHH  AA     AA  PP    PP PP             YYYY
X  HHHHHHHHHHHHHHH  AA     AA  PPPPPPP  PP            YYYY   -------
X  HHHHHHHHHHHHHHH  AAAAAAAAA  PP       PP           YYYY    -------
X  HHH         HHH  AAAAAAAAA  PP       PP          YYYY     -------
X  HHH         HHH  AA     AA  PP       PP         YYYY
X  HHH         HHH  AA     AA  PP       PP        YYYY
X  HHH         HHH  AA     AA  PP       PP      YYYYYY
X  HHH         HHH  AA     AA  PP       PP     YYYYYYY
X  HHH         HHH             PP       PP    YYYYYYY
XHHHHH         HHHHH           PP       PP   YYYYYYY
XHHHHH         HHHHH           PP       PP  YYYYYYY
X                                            YYYYY
XBBBBBBBBBBBBB                                YYY
XBBBBBBBBBBBBBB                                Y
X BBBB       BBB    II                                   YYY             YYY
X  BB         BB    II               DDDDDDDDDDDDD       YYYY           YYYY
X  BB         BB                     DDDDDDDDDDDDDD        YY            YY
X  BB         BB   III  RRR RRRR        DDD      DDD      A YY          YY
X  BB         BB    II   RRRR  RR       DDD      DDD     AAA YY        YY
X  BB        BBB    II    RRR           DDD      DDD    AAAAA YY      YY
X  BBB     BBBB     II    RR            DDD      DDD   AAAAAAA YY    YY
X  BBBBBBBBBBB      II    RR            DDD      DDD  AA     AA YY  YY
X  BBBBBBBBB        II    RR            DDD      DDD  AA     AA  YYYY
X  BBBBBBBBBBB      II    RR            DDD      DDD  AAAAAAAAA   YYY
X  BBB     BBBB    IIII  RRRR           DDD      DDD  AAAAAAAAA   YYY
X  BB        BBB             HHH        DDD      DDD  AA     AA   YYY
X  BB         BBB    TT     HHHH        DDD      DDD  AA     AA   YYY
X  BB         BBB    TT     HH          DDD      DDD  AA     AA   YYY
X  BB          BBB TTTTTT   HH          DDD      DDD  AA     AA   YYY
X  BB          BBB   TT     HH          DDD      DDD              YYY
X  BB          BBB   TT     HHHHHHHH  DDDDDDDDDDDDD               YYY
X  BB         BBB    TT     HH     HH DDDDDDDDDDDD               YYYY
X BBBB       BBBB    TT     HH     HH                           YYYY
XBBBBBBBBBBBBBBB     TT  TT HH     HH    YYYYYYYYYYYYYYYYYYYYYYYYYY
XBBBBBBBBBBBBBB       TTTT  HH     HH    YYYYYYYYYYYYYYYYYYYYYYYY
X                          HHHH   HHHH   YYYYYYYYYYYYYYYYYYYYYY
END_OF_FILE
if test 2880 -ne `wc -c <'bdaymesg'`; then
    echo shar: \"'bdaymesg'\" unpacked with wrong size!
fi
# end of 'bdaymesg'
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