[net.sources] New CALENDAR

gregg@okstate.UUCP (03/12/86)

    This note contains a Shell archive (Bourne style) for the source
and documentation for a C version of the CALENDAR(1) program.  Unpack
the archive, and read the README file.  The program should MAKE(1) on
a System 5 machine without changes.  This particular version is upward
compatible with the standard CALENDAR(1), but also includes many
extensions that allow far more versatile calendars.  Supported notations
allow weekdays, as well as months to be used as the soul indentifier.
Read the README file, and the MAN(1) page for more details.

    Please send me bug reports and complaints to me.  The C code that
checks dates is very much simplified.  This helps to speed the search,
as well as open some doors for new formats.


Gregg Wonderly
Department of Computing and Information Sciences
Oklahoma State University

UUCP: {cbosgd, ea, ihnp4, isucs1, mcvax, uokvax}!okstate!gregg
ARPA:  gregg%okstate.csnet@csnet-relay.arpa  

-------------------------    CUT   HERE    ------------------------------
echo x - README
sed '1,$s/^X//' <<\!FUNKY!STUFF! > README
XThis is a substitute version of CALENDAR(1).  It is upward compatible
Xwith the standard UNIX version, but has several extensions, and is
Xmuch faster, as it does not envoke EGREP(1).  The executable generated
Xby compiling calendar.c (see the makefile) is envoked for each user.
XIt should be installed in /usr/lib/calendar, or the shell script
Xwill need to be changed.  If you have STRCHR(3) instead of V7's INDEX(3),
Xor you have some other function for finding a character in a string,
Xchange/remove the #define in calendar.c.  strchr() is used in the
XC code.  If you run calendar SETUSERID to ROOT, then change the man page
Xto reflect that fact that world read access is not needed.
X
XYou should be able to just type "make install" and get a proper install.
X
XSend complaints/bugs to:
X
XGregg Wonderly
XDepartment of Computing and Information Sciences
XOklahoma State University
X
XUUCP: {cbosgd, ea, ihnp4, isucs1, mcvax, uokvax}!okstate!gregg
XARPA:  gregg@A.CS.OKSTATE.EDU
!FUNKY!STUFF!
echo x - calendar.1
sed '1,$s/^X//' <<\!FUNKY!STUFF! > calendar.1
X.TH CALENDAR 1  "Oklahoma State University"
X.SH NAME
Xcalendar \- reminder service
X.SH SYNOPSIS
X.B calendar
X[ \- ]
X.SH DESCRIPTION
X.I Calendar 
Xconsults the file `calendar' in the user's home directory
Xand prints out lines that contain today's,
Xtomorrow's, or a proper future date at the beginning of the line.
XMost reasonable month-day dates such as `Dec. 7,'
X`december 7,' `12/7,' etc., are recognized, but not
X`7 December' or `7/12'.
XOn weekends `tomorrow' extends through Monday.
X.sp
X.I Calendar
Xalso supports months and days of the week by themselves.  This
Xallows you to specify things on a weekly or monthly basis.
XThe format for weekday names is the same
Xas that for month names; Mon., monday, tues, tue, wed, etc.
XThe month may be specified either by name, or by number.  The weekday
Xwill only be recognized by name.
X.PP
X.SH EXTENDED NOTIFICATION
XIf you wish to have
X.I extended 
Xnotification of a previous or upcoming
Xevent,
X.I calendar
Xsupports a notation that extends a date by a week.
X.PP
XIf "*w" is appended directly to the week day, I.E. 12/7*w, then
Xyou will be sent a reminder every day beginning approximately
X(depending on the weekend) 7 days prior to the date.
X.PP
XIf "*W" (note the significance of case "w" or "W") is appended
Xto the week day as in the above example, than you will be notified
Xbeginning one day before and extending up to 10 days after.
X.PP
XIf you wish to have both of these features, I.E. a 2 week window
Xaround the given date, you can append "*Ww" or "*wW" to the
Xweek day.
X.SH EXTENDED MESSAGES
X.I Calendar
Xwill recognize lines with leading blanks or tabs as extensions
Xto a line with a matched (the date given is "today") date.  This allows
Xcomments longer than one line to be present for any given date.  Below
Xis an example date specification.
X.sp
XWednesday
X.br
X	Backups...
X.br
X	Check all printers for paper, ribbons, etc.
X.sp
X.SH YOUR CALENDAR OR EVERYBODY'S
XWhen an argument is present,
X.I calendar
Xdoes its job for every user who has a file `calendar' in their
Xlogin directory and sends them any positive results by
X.IR mail (1).
XNormally this is done daily in the wee hours under control of
X.IR cron (8).
X.sp 2
X.SH SAMPLE FILE
X.nf
XJuly            Get the budget ready for the new fiscal year
X.br
XMonday          Check all printers for paper...
X.br
X                Check MMDF, MDQS, and UUCP
X.br
XWednesday       Backups are tonight...
X.br
XFriday          Lets go party....
X.br
X6/17            John's b'day
X.br
X                Mark's b'day
X.br
X.fi
X.SH FILES
X$HOME/calendar        The users' calendar, $HOME is taken
X.br
X                      from /etc/passwd
X.br
X/usr/lib/calendar     processes each users' `calendar' file.
X.br
X/etc/passwd           Source of home directories.
X.br
X/tmp/Cal*             Output data file used to assure that
X.br
X                      there are no null messages.
X.br
Xsed, mail             Subprocesses
X.SH "SEE ALSO"
Xat(1), cron(8), mail(1)
X.SH BUGS
XIn general, your calendar must be readable by the public.  See your system
Xadministrator for the specifics.
X.I Calendar's
Xextended idea of `tomorrow' doesn't account for holidays.
XLines containing erroneous data are silently ignored.
X.SH "CREDITS TO"
XGregg Wonderly
X.br
XOklahoma State University
!FUNKY!STUFF!
echo x - calendar.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > calendar.c
X/****************************************************************************
X
X            F A S T    C A L E N D A R
X
X        This C program is a faster more general version of the
X    standard UNIX utility CALENDAR(1).  It recognizes the following
X    formats for dates.
X
X        numeric/numeric                     As is 1/12, 01/12, 12/1, ...
X
X        3-charmonth[any chars] numeric      As in January 12, Jan. 12,
X                                            jan. 12, JAN. 12, ...
X
X        3-charweekday[any chars]            As in Friday, Monday, MON.,
X                                            mon., tue, wed, ...
X
X        numeric month                       A month from 1 to 12 as in
X                                            1, 2, 04, 12, ...
X
X    Any lines, following a line that is matched, that have "whitespace"
X    in the first column will be printed along with the matched line.
X
X****************************************************************************/
X
X#include    <stdio.h>
X#include    <ctype.h>
X#include    <sys/types.h>
X#include    <time.h>
X#include    <pwd.h>
X
X/*  Number of seconds in a day.  */
X
X#define     DAY     (24*3600)
X
X/*  Remove this for SYS5 machines, or others with strchr instead of index.  */
X
X/*  #define     strchr  index   */
X
X/*  Global input line.  */
X
Xchar datebuf[300], *getdate();
X
Xmain (argc, argv)
X    int argc;
X    char **argv;
X{
X    register char *s;
X    char success;
X    struct passwd *passwd, *getpwuid();
X    register char file[200];
X    register FILE *fp;
X    register int i;
X
X    /*
X            Use file passed as an argument if it is there,
X        otherwise use file in home directory.
X    */
X
X    if (argc <= 1) {
X        if ((passwd = getpwuid (getuid())) == (struct passwd *)0) {
X            perror ("Hi Dennis, the compiler is still broke\n");
X            exit(1);
X        }
X        if (passwd -> pw_dir != (char *)0)
X            strcpy (file, passwd -> pw_dir);
X        strcat (file, "/calendar");
X    } else {
X        strcpy (file, argv[1]);
X    }
X
X    /*  Open the input file.  */
X
X    if ((fp = fopen (file, "r")) == NULL) {
X        perror (file);
X        exit (1);
X    }
X    
X    /*  Initially, no success.  */
X
X    success = 0;
X
X    /*  Loop till EOF is encountered.  */
X
X    while (s = getdate(fp, &success)) {
X
X        /*  Check date for "NOW"  */
X
X        if (isnow (datebuf)) {
X
X            /*  Print it, it is valid.  */
X
X            printf(s);
X        } else {
X
X            /* Reset the "print lines with leading blanks" flag. */
X
X            success = 0;
X        }
X    }
X
X    /*  Close up and exit.  */
X
X    fclose (fp);
X    exit (0);
X}
X
X/*
X        Check the input line, and get only the lines without leading
X    "whitespace".  Conditionally print those lines with leading blanks,
X    based on the "success" flag.
X*/
X
Xchar *getdate(fp, success)
X    char *success;
X    register FILE *fp;
X{
X    register int c;
X    static char inbuf[300];
X    register char *t, *s;
X
X    t = inbuf;
X    s = datebuf;
X    
X    /*  Skip leading space.  */
X
X    while ((c = getc(fp)) <= ' ' && c != EOF) {
X        ungetc(c, fp);
X        fgets (inbuf, 300, fp);
X
X        /*  Print the line if we previously printed a match.  */
X
X        if (*success)
X            printf (inbuf);
X    }
X
X    /*  Assume success for now.  */
X
X    *success = 1;
X
X    /*  Return EOF if we are there.  */
X
X    if (c == EOF)
X        return ((char *)0);
X
X    /*  Unget the character.  */
X
X    ungetc (c, fp);
X
X    /*  Get the input line.  */
X
X    fgets (t, 300, fp);
X
X    /*  Make a copy of it.  */
X
X    strcpy (s, t);
X
X    return (inbuf);
X}
X
X/*  Month definitions.  */
X
Xchar *months[] = {
X    "JAN", "FEB", "MAR",
X    "APR", "MAY", "JUN",
X    "JUL", "AUG", "SEP",
X    "OCT", "NOV", "DEC"
X};
X
X/*  Weekday definitions.  */
X
Xchar *weekdays[] = {
X    "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"
X};
X
X/*  See if the passed string has a "NOW" date in it.  */
X
Xisnow (datebuf)
X    char *datebuf;
X{
X    char *s, *t;
X    long l;
X    register int before = 0, after = 0, i, offset, month, day;
X    register struct tm *tmptr;
X    long timenow;
X
X    /*  Get the current time.  */
X
X    time (&timenow);
X    tmptr = localtime (&timenow);
X
X    /*  Is the first character a digit in a Month?  */
X
X    t = datebuf;
X    if (isdigit (*t) && (month = getnum (&t) - 1) >= 0 && month < 12) {
X
X        /*  Find the separating slash.  */
X
X        if (s = (char *) strchr (t, '/'))
X            t = s + 1;
X        else if (tmptr -> tm_mon == month)
X            return (1);
X        else
X            return (0);
X
X    /*  Is the string a month name?  */
X
X    } else if ((month = ischmonth (t)) >= 0) {
X
X        /*  Skip till while space.  */
X
X        while (!isspace (*t) && *t)
X            ++t;
X
X        /*  Skip to first NON-white space.  */
X
X        while (isspace (*t) && *t)
X            ++t;
X
X    /*  Not a month, so try a DAY as in Friday, Saturday, etc...  */
X
X    } else if ((day = ischday (datebuf)) >= 0) {
X
X        /*  Is this today?  */
X
X        if (day == tmptr -> tm_wday || ((day + 6)%7) == tmptr -> tm_wday)
X            return (1);
X        else
X            return (0);
X    } else {
X
X        /*  Fail to recognize any other data as a valid date.  */
X
X        return (0);
X    }
X
X    /*  Is there only what looks to be a month?  If so, then OK.  */
X
X    if (!*t || !isdigit (*t)) {
X
X        /*  Is it this month?  */
X
X        if (tmptr -> tm_mon == month)
X            return (1);
X
X        return (0);
X    }
X
X    /*  Calculate the value of the day.  */
X
X    day = getnum (&t);
X
X    /*  Check for *w or *W extended week specifiers.  */
X
X    if (*t == '*') {
X        if (t[1] == 'w')
X            ++before;
X        if (t[1] == 'W')
X            ++after;
X        if (t[2] == 'W')
X            ++after;
X        if (t[2] == 'w')
X            ++before;
X    }
X            
X    /*  Set weekend offset.  */
X
X    offset = 1;
X
X    /*  Get offset based on the weekend.  */
X
X    switch (tmptr -> tm_wday) {
X        case 5:
X            ++offset;
X
X        case 6:
X            ++offset;
X
X        default:
X            ++offset;
X    }
X
X    /*  Step through all dates by DAY.  */
X
X    l = timenow - (after * 7 * DAY);
X
X    while (l < timenow + (before * 7 * DAY) + (offset * DAY)) {
X        if (istoday (l, month, day))
X            return (1);
X
X        l += DAY;
X    }
X
X    /*  Return failure.  */
X
X    return (0);
X}
X    
X/*
X        Check if the string s is a DAY of the week as in monday, tues,
X    wed, THU., or any derivative thereof.
X*/
X
Xischday (s)
X    register char *s;
X{
X    register char *tt;
X    register int i, found = 0, c;
X    register char t[30];
X
X    /*  Copy the input, and capitalize it.  */
X
X    for (tt = t, i=0; isalpha(*s) && i < 28; ++i)
X        *tt++ = islower (c = *s++) ? toupper (c) : c;
X
X    *tt = '\0';
X
X    /*  Search the list of valid day names for the string.  */
X
X    for (i = 0; i < 7; ++i) {
X        if (strncmp (t, weekdays[i], 3) == 0) {
X            found++;
X            break;
X        }
X    }
X
X    /*  Return the numeric equivalent if we found it.  */
X
X    if (found)
X        return (i);
X
X    /*  Return invalid day name.  */
X
X    return (-1);
X}
X
X/*
X        Check if the string s is a valid derivative of the name of a
X    month as in JUNE, jun, August, etc...
X*/
X
Xischmonth (s)
X    register char *s;
X{
X    register char *tt;
X    register int i, found = 0, c;
X    register char t[30];
X
X    /*  Copy and capitalize.  */
X
X    for (tt = t, i=0; isalpha(*s) && i < 28; ++i)
X        *tt++ = islower (c = *s++) ? toupper (c) : c;
X
X    *tt = '\0';
X
X    /*  Look through the list for a match.  */
X
X    for (i = 0; i < 12; ++i) {
X        if (strncmp (t, months[i], 3) == 0) {
X            found++;
X            break;
X        }
X    }
X
X    /*  Return the numeric equivalent if we found it.  */
X
X    if (found)
X        return (i);
X
X    /*  Return faliure.  */
X
X    return (-1);
X}
X
X/*
X        Scan the string *t, and evaluate any integer contained there.
X    It is assumed that at least 1 digit exists (**t).
X*/
X
Xgetnum (t)
X    char **t;
X{
X    register int i, c;
X    register char *s;
X
X    i = 0;
X
X    /*  Get the starting address.  */
X
X    s = *t;
X
X    /*  Get the starting value from the first digit.  */
X
X    i = (c = *s++) - '0';
X
X    /*  Evaluate the rest of the digits (if any).  */
X
X    while (isdigit (c = *s++))
X        i = i * 10 + (c - '0');
X
X    /*  Set the new address.  */
X
X    *t = --s;
X
X    /*  Return the value.  */
X
X    return (i);
X}
X
X/*
X    Return whether or not the time t corresponds to the month and day.
X*/
X
Xistoday (t, month, day)
X    long t;
X    register int month, day;
X{
X    register struct tm *tm;
X
X    tm = localtime(&t);
X
X    return (tm -> tm_mon == month && tm -> tm_mday == day);
X}
!FUNKY!STUFF!
echo x - calendar.doc
sed '1,$s/^X//' <<\!FUNKY!STUFF! > calendar.doc
X
X
X
X     CCCCAAAALLLLEEEENNNNDDDDAAAARRRR((((1111))))   UUUUNNNNIIIIXXXX	5555....0000 ((((OOOOkkkkllllaaaahhhhoooommmmaaaa SSSSttttaaaatttteeee UUUUnnnniiiivvvveeeerrrrssssiiiittttyyyy))))	   CCCCAAAALLLLEEEENNNNDDDDAAAARRRR((((1111))))
X
X
X
X     NNNNAAAAMMMMEEEE
X	  calendar - reminder service
X
X     SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X	  ccccaaaalllleeeennnnddddaaaarrrr [ - ]
X
X     DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X	  _C_a_l_e_n_d_a_r consults the	file `calendar'	in the user's home
X	  directory and	prints out lines that contain today's,
X	  tomorrow's, or a proper future date at the beginning of the
X	  line.	 Most reasonable month-day dates such as `Dec. 7,'
X	  `december 7,'	`12/7,'	etc., are recognized, but not `7
X	  December' or `7/12'.	On weekends `tomorrow' extends through
X	  Monday.
X
X	  _C_a_l_e_n_d_a_r also	supports months	and days of the	week by
X	  themselves.  This allows you to specify things on a weekly
X	  or monthly basis.  The format	for weekday names is the same
X	  as that for month names; Mon., monday, tues, tue, wed, etc.
X	  The month may	be specified either by name, or	by number.
X	  The weekday will only	be recognized by name.
X
X     EEEEXXXXTTTTEEEENNNNDDDDEEEEDDDD NNNNOOOOTTTTIIIIFFFFIIIICCCCAAAATTTTIIIIOOOONNNN
X	  If you wish to have _e_x_t_e_n_d_e_d notification of a previous or
X	  upcoming event, _c_a_l_e_n_d_a_r supports a notation that extends a
X	  date by a week.
X
X	  If "*w" is appended directly to the week day,	I.E. 12/7*w,
X	  then you will	be sent	a reminder every day beginning
X	  approximately	(depending on the weekend) 7 days prior	to the
X	  date.
X
X	  If "*W" (note	the significance of case "w" or	"W") is
X	  appended to the week day as in the above example, than you
X	  will be notified beginning one day before and	extending up
X	  to 10	days after.
X
X	  If you wish to have both of these features, I.E. a 2 week
X	  window around	the given date,	you can	append "*Ww" or	"*wW"
X	  to the week day.
X
X     EEEEXXXXTTTTEEEENNNNDDDDEEEEDDDD MMMMEEEESSSSSSSSAAAAGGGGEEEESSSS
X	  _C_a_l_e_n_d_a_r will	recognize lines	with leading blanks or tabs as
X	  extensions to	a line with a matched (the date	given is
X	  "today") date.  This allows comments longer than one line to
X	  be present for any given date.  Below	is an example date
X	  specification.
X
X	  Wednesday
X	       Backups...
X	       Check all printers for paper, ribbons, etc.
X
X
X
X
X     Page 1					   (last mod. 3/12/86)
X
X
X
X
X
X
X     CCCCAAAALLLLEEEENNNNDDDDAAAARRRR((((1111))))   UUUUNNNNIIIIXXXX	5555....0000 ((((OOOOkkkkllllaaaahhhhoooommmmaaaa SSSSttttaaaatttteeee UUUUnnnniiiivvvveeeerrrrssssiiiittttyyyy))))	   CCCCAAAALLLLEEEENNNNDDDDAAAARRRR((((1111))))
X
X
X
X     YYYYOOOOUUUURRRR CCCCAAAALLLLEEEENNNNDDDDAAAARRRR OOOORRRR EEEEVVVVEEEERRRRYYYYBBBBOOOODDDDYYYY''''SSSS
X	  When an argument is present, _c_a_l_e_n_d_a_r	does its job for every
X	  user who has a file `calendar' in their login	directory and
X	  sends	them any positive results by _m_a_i_l(1).  Normally	this
X	  is done daily	in the wee hours under control of _c_r_o_n(8).
X
X
X
X     SSSSAAAAMMMMPPPPLLLLEEEE FFFFIIIILLLLEEEE
X	  July		  Get the budget ready for the new fiscal year
X	  Monday	  Check	all printers for paper...
X			  Check	MMDF, MDQS, and	UUCP
X	  Wednesday	  Backups are tonight...
X	  Friday	  Lets go party....
X	  6/17		  John's b'day
X			  Mark's b'day
X
X     FFFFIIIILLLLEEEESSSS
X	  $HOME/calendar	The users' calendar, $HOME is taken
X				from /etc/passwd
X	  /usr/lib/calendar	processes each users' `calendar' file.
X	  /etc/passwd		Source of home directories.
X	  /tmp/Cal*		Output data file used to assure	that
X				there are no null messages.
X	  sed, mail		Subprocesses
X
X     SSSSEEEEEEEE AAAALLLLSSSSOOOO
X	  at(1), cron(8), mail(1)
X
X     BBBBUUUUGGGGSSSS
X	  In general, your calendar must be readable by	the public.
X	  See your system administrator	for the	specifics.  _C_a_l_e_n_d_a_r'_s
X	  extended idea	of `tomorrow' doesn't account for holidays.
X	  Lines	containing erroneous data are silently ignored.
X
X     CCCCRRRREEEEDDDDIIIITTTTSSSS TTTTOOOO
X	  Gregg	Wonderly
X	  Oklahoma State University
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X     Page 2					   (last mod. 3/12/86)
X
X
X
!FUNKY!STUFF!
echo x - calendar.sh
sed '1,$s/^X//' <<\!FUNKY!STUFF! > calendar.sh
X#
X#  Replacement calendar program
X#  By: Gregg Wonderly <gregg@okstate> on 01/24/86
X
XPATH=/bin:/usr/bin
Xtmp=/tmp/Cal$$
Xcal="calendar"
Xlib=/usr/lib
Xtrap "rm $tmp;exit" 1 2 13 15
X
Xcase $1 in
X-)
X	sed 's/\([^:]*\):.*:\(.*\):[^:]*$/dir=\2 login=\1/' /etc/passwd | \
X	while read x
X	do
X		eval $x
X		if test -r $dir/$cal; then
X			$lib/calendar $dir/$cal >$tmp
X			if test -s $tmp; then
X				/bin/mail $login -f "Reminder Service" <$tmp >/dev/null 2>&1
X			fi
X			rm $tmp
X		fi
X	done;;
X*)
X	$lib/calendar;;
Xesac
!FUNKY!STUFF!
echo x - makefile
sed '1,$s/^X//' <<\!FUNKY!STUFF! > makefile
XLIB=/usr/lib
XBIN=/bin
XCFLAGS=-i -O
X
Xcalendar:	calendar.c
X	cc $(CFLAGS) calendar.c -o calendar
X
Xinstall:	calendar calendar.sh
X	cp calendar.sh $(BIN)/calendar
X	cp calendar $(LIB)/calendar
X	chmod 511 $(LIB)/calendar $(BIN)/calendar
!FUNKY!STUFF!