[net.sources] calendar-like program

oscar@utcsrgv.UUCP (Oscar M. Nierstrasz) (05/12/84)

echo README
sed 's/^|//' > README << 'Rosebud'
|Here are a few shell scripts and a C program for dealing with future
|events.  `dates' is a program similar to `calendar' except that you can
|ask it to report future events as well as today's.  I find it useful
|for birthdays, seminars, and anything that needs reminding more than a
|day in advance.  See `dates.1'.  I've been using the program for quite
|a while now, and believe it to be stable (recently caught a leap-year bug).
|
|`events' is a shell script that calls `dates' on a single, publicly
|accessible file.  A flag lets you edit it while temporarily locking out
|other attempts to do so.  I use it for keeping track of upcoming films,
|but it clearly has other applications.
|
|`remind' is a shell script that does more-or-less what `leave' does.
|The difference is that it won't go away if you log out, and it will
|contact you even if you change terminals.  It calls the enclosed shell
|script `hi' to do this.
|
|Note: fix paths in `events' and `remind'.
|
|Enjoy.
|					Oscar Nierstrasz
|
|{ akgua allegra decvax decwrl floyd ihnp4 linus ... }!utcsrgv!oscar
Rosebud
chmod 0644 README
echo dates.1
sed 's/^|//' > dates.1 << 'Rosebud'
|.TH DATES 1
|.UC
|.SH NAME
|dates \- report upcoming events
|.SH SYNOPSIS
|.B dates 
|[ -t days ] file ...
|.SH DESCRIPTION
|.I Dates
|examines a file containing information about upcoming events
|and reports any that are to take place within the next seven days.
|.PP
|The event file is expected to contain entries of the form:
|.IP
|mmdd
|.br
|event information
|.br
| ...
|.PP
|if that event takes place on a single day, or:
|.IP
|mmdd-mmdd
|.br
|event information
|.br
| ...
|.PP
|if it takes place over a range of dates.
|Comment lines beginning with a '#' are ignored.
|.PP
|For example, if your event file contains an entry for:
|.IP
|1206
|.in +5
|Saint Nicholas' Day!
|.PP
|then
|.IP
|dates eventfile
|.PP
|will yield:
|.IP
|Tue Dec 6:
|.in +5
|Saint Nicholas' Day!
|.PP
|assuming, of course, that it falls within seven days.
|Events occurring the same day or the next are reported as
|taking place "Today" or "Tomorrow".
|.PP
|You can shorten or lengthen the time period for which events are
|reported by using the '-t' flag.
|For example:
|.IP
|dates -t 0 eventfile
|.PP
|will report only those events taking place today.
|.PP
|Event entries may be listed in any order, but be warned that
|.I dates
|does not sort its output.
|Upcoming events are reported in the order that they appear.
|Obsolete entries are not deleted by
|.I dates
|since it has no way of telling that an event will not recur
|next year (like birthdays).
|.PP
|It is often useful to put a call to
|.I dates
|in your .login or .logout (with the "C" shell) or your .profile 
|(with the Bourne shell).
|.SH BUGS
|Doesn't seem to work quite right at midnight or New Year's.
|.SH AUTHOR
|Oscar Nierstrasz at the University of Toronto.
|.SH SEE ALSO
|events(1), calendar(1), at(1), leave(1), time(2).
Rosebud
chmod 0644 dates.1
echo dates.c
sed 's/^|//' > dates.c << 'Rosebud'
|
|/*	FILE:	DATES.C				Jan. 1983
| *
| *	AUTHOR: OSCAR NIERSTRASZ
| *
| *	Usage: dates [-t <days>] <file> ... 
| *
| *	Checks files of events and warns you if they
| *	are coming up soon.
| *	Flags and files may appear in any order.
| *
| *	-t sets the number of days
| *
| *	Assumes files have either structure:
| *	mmdd
| *		event
| *		...
| *	mmdd-mmdd
| *		event
| *		...
| */
|
|#include <stdio.h>
|#include <sgtty.h>
|#include <sys/types.h>
|#include <sys/timeb.h>
|#define	TRUE	1
|#define	FALSE	0
|#define	MAX	500
|
|#define	EOS	'\0'
|#define	RANGE	'-'
|#define	COMMENT	'#'
|
|#define DLEN	30	/* date buffer length */
|
|#define	NEXT	1	/* NEXT: use next year's date if this	*/
|#define	THIS	0	/* year's has passed; THIS: don't	*/
|
|#define	TDIF	5	/* 5 hours earlier than Greenwich	*/
|#define	DAYS	7	/* default warning period		*/
|#define	num(n)	(n - '0')
|#define	dysize(yr)	(365 + (((yr%4)==0)?1:0))
|#define	dmsize(yr, m)	(dm[m] + (((yr % 4) == 0) && m >= 2))
|#define	month_name(n)	((n < 1 || n > 12) ? name[0] : name[n])
|#define	day_name(n)	weekday[((n)%7+7)%7]
|
|long	*time();
|int	days = DAYS, putline = FALSE;
|
|struct	ymdhms{
|	int	year;
|	int	month;
|	int	day;
|	int	hour;
|	int	min;
|	int	sec;
|	int	wday;
|}	t;	/* Today's date */
|
|static	int	dm[13] =
|{
|	0,
|	31,
|	59,
|	90,
|	120,
|	151,
|	181,
|	212,
|	243,
|	273,
|	304,
|	334,
|	365
|};
|
|static	char	*name[] = {
|		"illegal month",
|		"Jan",
|		"Feb",
|		"March",
|		"April",
|		"May",
|		"June",
|		"July",
|		"Aug",
|		"Sept",
|		"Oct",
|		"Nov",
|		"Dec"
|	};
|
|static	char	*weekday[] = {
|		"Wed",
|		"Thu",
|		"Fri",
|		"Sat",
|		"Sun",
|		"Mon",
|		"Tue"
|	};
|
|char	*nom(s)
|char	*s;
|{
|	char	*c;
|
|	c = s;
|	while(*c != EOS){
|		if(*c == ','){
|			*c = EOS;
|			c++;
|			while(*c == ' ')
|				c++;
|			return(c);
|		}
|		c++;
|	}
|	return(c);
|}
|
|main(argc, argv)
|int	argc;
|char	*argv[];
|{
|	FILE	*fp;
|	int	i=1, ll;
|	char	s[MAX];
|
|	if(argc == 1){
|		fprintf(stderr, "Usage: dates [-t <days>] <file> ...\n");
|		exit(1);
|	}
|	rtime();
|	while(i<argc){
|		if(argv[i][0] == '-'){
|			switch(argv[i][1]){
|			case 't':
|				i++;
|				if(i == argc){
|					fprintf(stderr, "dates: missing arg\n");
|				}
|				else	days = atoi(argv[i]);
|				break;
|			default:
|				fprintf(stderr, "dates: unknown flag %s\n", argv[i]);
|			}
|		}
|		else{
|			if((fp = fopen(argv[i], "r")) == NULL)
|				fprintf(stderr, "dates: can't open %s\n", argv[i]);
|			else{
|				while((ll = getline(s, fp)) != EOF){
|					if(ll == MAX)
|				fprintf(stderr, "dates: line too long: %s\n", s);
|					if(s[0] != COMMENT)
|						if(data(s)){
|							if (putline)
|							printf("%s\n", s);
|						}
|						else	tcheck(s);
|				}
|				fclose(fp);
|			}
|		}
|		i++;
|	}
|}
|
|rtime()
|{
|/*	sets today's date in t.		*/
|
|	int	i, days;
|	long	tbuf;
|
|	time(&tbuf);		/*	seconds	*/
|	t.sec = tbuf % 60;
|	tbuf = tbuf / 60;	/*	minutes	*/
|	t.min = tbuf % 60;
|	tbuf = tbuf / 60 - TDIF; /*	hours	*/
|	t.hour = tbuf % 24;
|	tbuf = 1 + tbuf / 24;	/*	days	*/
|	t.wday = tbuf % 7;
|	t.year = 1970;
|	days = dysize(t.year);
|	while(tbuf > days){
|		(t.year)++;
|		tbuf -= days;
|		days = dysize(t.year);
|	}
|	for(i=1; i<=12; i++){
|		days = dmsize(t.year, i);
|		if(tbuf < days)
|			break;
|	}
|	t.month = i;
|	t.day = tbuf - dmsize(t.year, i-1);
|}
|
|tcheck(s)
|char	*s;
|{
|	char	*sn, *f, date1[DLEN], date2[DLEN];
|	int	dif1, dif2;
|
|	getday(s, &dif1, date1, NEXT);
|	if(s[4] == EOS){
|		if(dif1 <= days){
|		putline = TRUE;
|		printf("%s :\n", date1);
|		}
|		else	putline = FALSE;
|	}
|	else	if(s[4] == RANGE){
|		getday(s+5, &dif2, date2, NEXT);
|		/* if date1 has passed but date2 hasn't,	*/
|		/* force getday to use this year for date1	*/
|		if(dif2 < dif1)
|			getday(s, &dif1, date1, THIS);
|		if((dif1 <= days) && (dif2 >= 0)){
|		putline = TRUE;
|		printf("%s to %s :\n", date1, date2);
|		}
|		else	putline = FALSE;
|	}
|}
|
|getday(s, pdif, date, next)
|char	*s, *date;
|int	*pdif, next;
|{
|	int	m, d, dif;
|
|	m = num(s[0])*10 + num(s[1]);
|	d = num(s[2])*10 + num(s[3]);
|	*pdif = dif = dmsize(t.year, m-1) + d
|		- (dmsize(t.year, t.month-1) + t.day);
|	if(next && dif < 0)
|		*pdif = dif = dysize(1+t.year) + dmsize(1+t.year, m)
|		+ d - (dmsize(t.year, t.month) + t.day);
|	if(dif == -1)
|		sprintf(date, "Yesterday");
|	else if(dif == 0)
|		sprintf(date, "Today");
|	else if(dif == 1)
|		sprintf(date, "Tomorrow");
|	else
|		sprintf(date, "%s %s %d", day_name(dif+t.wday),
|		month_name(m), d);
|}
|data(s)
|char	*s;
|{
|	/* Returns TRUE if line is data		*/
|	/* FALSE if date line			*/
|	/* Acceptable formats are "mmdd"	*/
|	/* and "mmdd-mmdd"			*/
|	int	i;
|
|	for(i=0; i<4; i++)
|		if(s[i] < '0' || '9' < s[i])
|			return(TRUE);
|	if(s[4] == EOS)
|		return(FALSE);
|	/* date may be a range */
|	if(s[4] == RANGE){
|	/* Can't have "nested" ranges */
|		if(s[9] != EOS)
|			return(TRUE);
|		return(data(s+5));
|	}
|	return(TRUE);
|}
|getline(s,file)
|	char	*s;
|	FILE	*file;
|{
|	/*	get a line of input up to MAX characters	*/
|	/*	return length of input or EOF or MAX		*/
|	int	i=0;
|	char	c;
|
|	while((c=fgetc(file)) != '\n'){
|		if(c == EOF){
|			rewind(file);
|			return(EOF);
|		}
|		s[i] = c;
|		i++;
|		if(i == MAX){
|			s[i-1] = '\0';
|			return(MAX);
|		}
|	}
|	s[i] = '\0';
|	return(i);
|}
Rosebud
chmod 0644 dates.c
echo events
sed 's/^|//' > events << 'Rosebud'
|#! /bin/sh
|#	events [N]|-[vhe]|[-em]|[-rm]
|#	List today's events or events for the next N days.
|#	With the -v, -h, -e or -em flags, vi, hed, ed or emacs
|#	are invoked on the database.
|#	The -rm flag removes the lock file.
|#	Make sure that $e is writeable by all.
|#	$B is the location of the `dates' program.
|#	Author : Oscar Nierstrasz @ ..!utcsrgv!oscar
|e=/usr/pub/events
|B=/usr/local
|l=/tmp/lock_events
|u="Usage: events [#days] | [-vi] | [-ed] | [-hed] | [-emacs]"
|case $# in
|0 )	arg=0 ;;
|1 )	arg=$1 ;;
|* )	echo "$u" 1>&2
|	exit ;;
|esac
|case $arg in
|-v )	ed=vi ;;
|-vi )	ed=vi ;;
|-h )	ed=hed ;;
|-hed )	ed=hed ;;
|-e )	ed=ed ;;
|-ed )	ed=ed ;;
|-em )	ed=emacs ;;
|-emacs ) ed=emacs ;;
|-rm )	rm -f $l
|	exit ;;
|-* )	echo "events : unknown flag" 1>&2
|	exit ;;
|[0-9]* ) $B/dates -t $arg $e
|	exit ;;
|* )	echo "$u" 1>&2
|	exit ;;
|esac
|
|umask 0777
|if (echo > $l) 2> /dev/null
|then
|	umask 022
|	$ed $e
|	rm -f $l
|else
|	echo "Sorry, someone is already editing the events file!" 1>&2
|fi
Rosebud
chmod 0755 events
echo hi
sed 's/^|//' > hi << 'Rosebud'
|#! /bin/sh
|#	hi user msg
|#	Write a message on every tty that a user is logged onto.
|#	With the -m flag, the message will be mailed if the user
|#	is not logged in (useful in shell scripts).
|#	Author : Oscar Nierstrasz @ ..!utcsrgv!oscar
|
|case $# in
|0 )	echo "Usage: hi user [-m] msg"
|	echo "       hi user -f <file> ..."
|	echo "       <cmd> | hi user"
|	exit ;;
|esac
|u=$1
|shift
|case $# in
|0 )	f=/tmp/hi$$
|	cat > $f ;;
|1 )	;;
|* )	case $1 in
|	-f )	shift
|		f=$* ;;
|	-m )	shift
|		m=y ;;
|	esac ;;
|esac
|w=`who | fgrep $u`
|case $w in
|"" )	case $m in
|	y )	echo "$*" | mail $u ;;
|	*)	echo "$u not logged in" ;;
|	esac ;;
|* )	case $f in
|	"" )	echo "$w" | sed -e "s%^.*tty%/dev/tty%" -e "s/ .*$//" \
|			-e "s/^/echo \"$*\" > /" | /bin/sh ;;
|	* )	echo "$w" | sed -e "s%^.*tty%/dev/tty%" -e "s/ .*$//" \
|			-e "s%^%cat $f > %" | /bin/sh ;;
|	esac ;;
|esac
|rm -f /tmp/hi$$
Rosebud
chmod 0755 hi
echo remind
sed 's/^|//' > remind << 'Rosebud'
|#! /bin/sh
|#	remind [time] [-m|-c] [message] ...		840214
|#	Echo a message at a later time.
|#	With the -m flag, `mail' is used if you have logged off.
|#	With the -c flag, the message is executed as a command.
|#	Calls the shell script `hi' to get you wherever you are!
|#	SEE ALSO : at(1), leave(1).
|#	Author : Oscar Nierstrasz @ utcsrgv!oscar
|C=/u3/oscar/cmd
|case $# in
|0 )	w='{ split($4, from, ":") ; print from[1] ":" from[2] }'
|	echo "It's" `date | awk "$w"`
|	echo -n "When shall I call you? : "
|	read t ;;
|* )	t=$1
|	shift ;;
|esac
|case $t in
|[0-9] )		t=$t:00 ;;
|[0-1][0-9] )	t=$t:00 ;;
|2[0-4] )	t=$t:00 ;;
|[0-9]:[0-5][0-9] )	;;
|[0-1][0-9]:[0-5][0-9] )	;;
|2[0-9]:[0-5][0-9] )	;;
|* )	echo "Enter time as hh or hh:mm"
|	exit ;;
|esac
|case $# in
|0 )	echo -n "Message? : "
|	read m ;;
|* )	case $1 in
|	-c )	shift
|		c=$* ;;
|	-m )	hi=-m
|		shift
|		m=$* ;;
|	* )	m=$* ;;
|	esac ;;
|esac
|case $c in
|"" )	case $m in
|	"" )	m="Hi! -- It's $t" ;;
|	* )	m="It's $t -- $m" ;;
|	esac ;;
|esac
|#	Awk script:
|w='NR == 1 { split($4, from, ":") }
|NR == 2 { split($1, to, ":") }
|END {
|	hrs = to[1] - from[1]
|	mins = to[2] - from[2]
|	time = 60 * hrs + mins
|	if (time < 0) {
|		time += 60 * 12
|		if ((to[1] > 12) || (time < 0))
|			time += 60 * 12
|		}
|	print 60 * time
|	}'
|s=`(date ; echo $t ) | awk "$w"`
|case $s in
|60 )	;;
|* )	x="s" ;;
|esac
|echo "See you in" `expr $s / 60` "minute$x ..."
|case $c in
|"" )	(sleep $s ; $C/hi $USER $hi "$m") & ;;
|* )	(sleep $s ; echo "$c" | /bin/sh) & ;;
|esac
Rosebud
chmod 0755 remind