[net.sources] use MH as an apointment diary

ken@rochester.UUCP (Ipse dixit) (03/15/86)

References:

I'm getting tired of seeing discussion in this group - they belong in
net.sources.d (hint, hint). So I'm posting something.

Here is a quick hack I did to use MH as an appointment diary. It is
only known to work on 4.2 BSD. Yes, it could be faster. Yes, there are
many things lacking. No, I don't have the time now. So if you make any
improvements, do let me know and I will incorporate your ideas/work in
the next version, if there is one.

	Have fun, Ken

#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	mem
#	rem
#	promptdate.c
#	getopt.c
#	promptdate.1
# This archive created: Thu Mar 13 00:29:30 1986
# By:	Ipse dixit ()
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(265 characters)'
if test -f 'README'
then
	echo shar: over-writing existing file "'README'"
fi
cat << \SHAR_EOF > 'README'
I haven't made manual entries for mem and rem - they are pretty short
shell scripts. Rem is used to enter reminders and mem is used to show
reminders. All the normal MH commands can be used on the appointment
folder.

	Ken Yap
	ken@rochester.arpa

	13th March 1986
SHAR_EOF
if test 265 -ne "`wc -c 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 265 characters)'
fi
echo shar: extracting "'Makefile'" '(81 characters)'
if test -f 'Makefile'
then
	echo shar: over-writing existing file "'Makefile'"
fi
cat << \SHAR_EOF > 'Makefile'
CFLAGS		= -O

promptdate:	promptdate.o getopt.o
		cc -o $@ promptdate.o getopt.o
SHAR_EOF
if test 81 -ne "`wc -c 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 81 characters)'
fi
echo shar: extracting "'mem'" '(451 characters)'
if test -f 'mem'
then
	echo shar: over-writing existing file "'mem'"
fi
cat << \SHAR_EOF > 'mem'
#! /bin/sh
remfolder="+reminders"
printformat="%3msg%-2.2flags%3day %02mon/%02mday %02hour%02min%<date %|*%>%subject%body"
case $# in
    0)
	while when=`promptdate`
	do
		2>/dev/null scan `2>/dev/null pick $remfolder -after yesterday -and -before "$when"` -format "$printformat"
	done
	;;
    *)
	when=`promptdate "$*"`
	2>/dev/null scan `2>/dev/null pick $remfolder -after yesterday -and -before "$when"` -format "$printformat"
	;;
esac
SHAR_EOF
if test 439 -ne "`wc -c 'mem'`"
then
	echo shar: error transmitting "'mem'" '(should have been 439 characters)'
fi
chmod +x 'mem'
echo shar: extracting "'rem'" '(314 characters)'
if test -f 'rem'
then
	echo shar: over-writing existing file "'rem'"
fi
cat << \SHAR_EOF > 'rem'
#! /bin/sh
remfolder="+reminders"
umask 077
case $# in
    0)
	while when=`promptdate`
	do
		remfile=`mhpath $remfolder new`
		echo "Date: $when

" > $remfile
		prompter $remfile
	done
	;;
    *)
	when=`promptdate "$*"`
	remfile=`mhpath $remfolder new`
	echo "Date: $when

" > $remfile
	prompter $remfile
	;;
esac
SHAR_EOF
if test 314 -ne "`wc -c 'rem'`"
then
	echo shar: error transmitting "'rem'" '(should have been 314 characters)'
fi
chmod +x 'rem'
echo shar: extracting "'promptdate.c'" '(5729 characters)'
if test -f 'promptdate.c'
then
	echo shar: over-writing existing file "'promptdate.c'"
fi
cat << \SHAR_EOF > 'promptdate.c'
#include	<stdio.h>
#include	<time.h>
#include	<ctype.h>

/*
**	promptdate
**	prompt user for a date specification which can be quite minimal
**	and print it in a form suitable for parsing by MH
*/

#define		MAXLINE		128

char		*mname[12]	={
		"Jan","Feb","Mar","Apr","May","Jun",
		"Jul","Aug","Sep","Oct","Nov","Dec"};
char		*dname[7]	={
		"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
struct tm	now;
int		dayspast	= 0;
int		monthlen[2][12]	={
		31,28,31,30,31,30,31,31,30,31,30,31,
		31,29,31,30,31,30,31,31,30,31,30,31};
char		*defaultformat	= "%d %N %y 00:00";

main(argc, argv)
	int		argc;
	char		**argv;
{	
	register int	c;
	struct tm	then;
	extern int	optind;		/* defined in getopt */
	extern char	*optarg;	/* defined in getopt */
	int		getopt();
	long		secsfr70();

	while ((c = getopt (argc, argv, "f:")) != EOF)
	{
		switch (c)
		{
		case 'f':
			defaultformat = optarg;
			break;
		default:
			fprintf(stderr, "usage: %s [-f format] [datespec]\n", argv[0]);
			exit (1);
		}
	}
	argc -= optind;
	argv += optind;

	finddate(&now, dayspast = (int)(secsfr70()/86400L));

	if (argc <= 0)			/* get from user */
	{
		if (!promptdate(&then))
			exit(1);
		printdate(&then, defaultformat);
	}
	else				/* get from command line */
	{
		if (!decodedate(argv[0], &then))
			exit(1);
		printdate(&then, defaultformat);
	}
	exit(0);
}

int promptdate(when)
	struct tm	*when;
{
	char		line[MAXLINE];
	int		decodedate();
	char		*gets();

	for (;;)
	{
		fprintf(stderr, "When? ");
		if (gets(line) == NULL)
		{
			fprintf(stderr, "\n");
			return (0);
		}
		if (decodedate(line, when))
			return (1);
	}
/*NOTREACHED*/
}

int decodedate(line, when)
	char		*line;
	struct tm	*when;
/*
**	accept spec for date in several forms
**	legal are: sun,mon,tue,wed,thu,fri,sat,today,tomorrow,
**	<date><month>,+<relative number of days>
**	<month> should be alpha
**	upper case accepted too
*/
{
	char		s[4];
	register int	i,targetdate;
	int		tem;
	register char	*lptr;

	when->tm_year = now.tm_year;
	when->tm_mon = now.tm_mon;
	targetdate = dayspast;
	for (lptr = line; isspace(*lptr); lptr++)
		;
	if (isdigit(*lptr))
	{
		i = sscanf(lptr, "%d%3s%d", &when->tm_mday, s, &tem);
		switch(i)
		{
		    case 3:
			when->tm_year = tem;
		    case 2:
			fold(s);
			when->tm_mon = monthofyear(s);
			if (i == 3)
				break;
			if (when->tm_mday != 0 && when->tm_mon != 0 && daysfr70(when) < dayspast)
				when->tm_year++;
			break;
		    case 1:
			if (when->tm_mday != 0 && when->tm_mday < now.tm_mday)
			{
				if (++when->tm_mon > 12)
				{
					when->tm_mon = 1;
					when->tm_year++;
				}
			}
		}
		return (validate(when));
	}
	if (isalpha(*lptr))
	{
		sscanf(lptr, "%3s", s);
		fold(s);
		if ((tem = dayofweek(s)) >= 0)
			targetdate += (tem -= now.tm_wday) <= 0 ? tem + 7 : tem;
		else if (strcmp(s, "Tom") == 0)
			targetdate++;
		else if (strcmp(s, "Tod") == 0)
			;
		else	/* mistake */
			return (0);
	}
	else if (*lptr == '+')
	{
		if (sscanf(++lptr, "%d", &tem) == 0 || tem < 0)	/* mistake */
			return (0);
		targetdate += tem;
	}
	else	/* mistake by default */
		return (0);
	finddate(when, targetdate);
	return (when->tm_mday != 0);
}

int validate(datetm)
/*
**	check that a given date and month combination is legal
**	datetm->tm_year must hold the year in question
*/
	register struct tm	*datetm;
{

	return (datetm->tm_mday <= monthlen[leapyear(datetm->tm_year)]
		[datetm->tm_mon] && datetm->tm_mday > 0);
}

finddate(datetm, df70)
/*
**	convert days from 1 jan 1970 to a date in struct datetm
*/
	register int		df70;
	register struct tm	*datetm;
{
	register struct tm	*tdtm;
	long			longtime;
	struct tm		*gmtime();

	longtime = df70 * 86400L;
	tdtm = gmtime(&longtime);
	datetm->tm_yday = tdtm->tm_yday;
	datetm->tm_wday = tdtm->tm_wday;
	datetm->tm_year = tdtm->tm_year + 1900;
	datetm->tm_mon = tdtm->tm_mon;
	datetm->tm_mday = tdtm->tm_mday;
	datetm->tm_hour = tdtm->tm_hour;
	datetm->tm_min = tdtm->tm_min;
	datetm->tm_sec = tdtm->tm_sec;
}

fold(s)
/*
**	convert first character to uppercase
**	convert rest of string from uppercase to lower case
*/
	register char	*s;
{
	register char	c;

	if ((c = *s) != '\0')
		*s++ += islower(c) ? 'A' - 'a' : 0;
	while ((c = *s) != '\0')
		*s++ += isupper(c) ? 'a' - 'A' : 0;
}

int leapyear(y)
/*
**	returns 1 if leapyear 0 otherwise
*/
	register int	y;
{

	return (((y % 4) == 0 && (y % 100) != 0) || (y % 400) == 0);
}

int daysfr70(datetm)
/*
**	returns the number of days from 1 Jan 1970
**	no checking for illegal date at all
*/
	register struct tm	*datetm;
{
	register int		i, totdays;


	totdays = 0;
	for (i = 1970; i <= 2050 && i < datetm->tm_year; i++)	/* prevent overflow */
		totdays += 365 + leapyear(i);
	for (i = 0; i < 12 && i < datetm->tm_mon; i++)
		totdays += monthlen[leapyear(datetm->tm_year)][i];
	totdays += datetm->tm_mday - 1;
	return (totdays);
}

int monthofyear(s)
/*
**	returns month of year in numeric form when given
**	the first three letters
*/
	register char	*s;
{
	register int	i;

	fold(s);
	for (i = 12; i-- && strcmp(s,mname[i]); )
		;
	return (i);
}

int dayofweek(s)
/*
**	sunday = 0,...,saturday = 6, nomatch = -1
*/
	register char	*s;
{
	register int	i;

	fold(s);
	for (i = 7; i-- && strcmp(s,dname[i]); )
		;
	return (i);
}

printdate(date, format)
/*
**	print date in MH acceptable format
**	kludge - general formats are not implemented
*/
	struct tm	*date;
	char		*format;
{
	printf("%d %s %d 00:00\n",
		date->tm_mday, mname[date->tm_mon], date->tm_year);
}

long secsfr70()
/*
**	This is system dependent
*/
{
	register int		dst;
	struct timeval		tv;
	struct timezone		tz;
	struct tm		*localtime();

	gettimeofday(&tv, &tz);
	dst = localtime(&tv.tv_sec)->tm_isdst;
	return (tv.tv_sec - tz.tz_minuteswest * 60 + (dst ? 3600 : 0));
}
SHAR_EOF
if test 5729 -ne "`wc -c 'promptdate.c'`"
then
	echo shar: error transmitting "'promptdate.c'" '(should have been 5729 characters)'
fi
echo shar: extracting "'getopt.c'" '(1406 characters)'
if test -f 'getopt.c'
then
	echo shar: over-writing existing file "'getopt.c'"
fi
cat << \SHAR_EOF > 'getopt.c'
#include <stdio.h>

/*
 * get option letter from argument vector
 */
int	opterr = 1,		/* useless, never set or used */
	optind = 1,		/* index into parent argv vector */
	optopt;			/* character checked for validity */
char	*optarg;		/* argument associated with option */

#define BADCH	(int)'?'
#define EMSG	""
#define tell(s)	fputs(*nargv,stderr);fputs(s,stderr); \
		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);

getopt(nargc,nargv,ostr)
int	nargc;
char	**nargv,
	*ostr;
{
	static char	*place = EMSG;	/* option letter processing */
	register char	*oli;		/* option letter list index */
	char	*index();

	if(!*place) {			/* update scanning pointer */
		if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
		if (*place == '-') {	/* found "--" */
			++optind;
			return(EOF);
		}
	}				/* option letter okay? */
	if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
		if(!*place) ++optind;
		tell(": illegal option -- ");
	}
	if (*++oli != ':') {		/* don't need argument */
		optarg = NULL;
		if (!*place) ++optind;
	}
	else {				/* need an argument */
		if (*place) optarg = place;	/* no white space */
		else if (nargc <= ++optind) {	/* no arg */
			place = EMSG;
			tell(": option requires an argument -- ");
		}
	 	else optarg = nargv[optind];	/* white space */
		place = EMSG;
		++optind;
	}
	return(optopt);			/* dump back option letter */
}
SHAR_EOF
if test 1406 -ne "`wc -c 'getopt.c'`"
then
	echo shar: error transmitting "'getopt.c'" '(should have been 1406 characters)'
fi
echo shar: extracting "'promptdate.1'" '(1247 characters)'
if test -f 'promptdate.1'
then
	echo shar: over-writing existing file "'promptdate.1'"
fi
cat << \SHAR_EOF > 'promptdate.1'
.TH PROMPTDATE 1 3/13/86
.SH NAME
promptdate \- accept date entry and print out in specified format
.SH SYNOPSIS
.B promptdate
[
.B -f format
] [
.B datespec
]
.SH DESCRIPTION
If no format is specified, one suitable for parsing by MH
will be used.
.PP
If no date is given on the command line,
.I promptdate
will interactively prompt for one.
It will persist in asking for a date until
a legal date is supplied.
To exit, type the EOF character (normally control-d).
.PP
The syntax of an acceptable date specification is:
.IP
"today" | "tomorrow" | <dayofweek> | "+"<daysfromtoday>
| <date> [<month> [<year>]]
.PP
where:
.IP
<dayofweek> is "sunday",...,"saturday"
.IP
<daysfromtoday> is an unsigned decimal number
.IP
<date> is a valid date for that month
.IP
<month> is "january",...,"december"
.IP
<year> is > 1970
.PP
Alphabetic strings may be in
any mix of upper or lower case.
They may also be abbreviated to
the first three letters.
.PP
<dayofweek> is equivalent
to specifying from "+1" to "+7".
.SH AUTHOR
Ken Yap (University of Rochester)
.SH BUGS
This manual page lies - the format is hardwired for MH.
General date formatting is not implemented yet.
.PP
Should accept more date formats. Will be replaced by a
user-friendlier version soon.
SHAR_EOF
if test 1247 -ne "`wc -c 'promptdate.1'`"
then
	echo shar: error transmitting "'promptdate.1'" '(should have been 1247 characters)'
fi
#	End of shell archive
exit 0
-- 
UUCP: ..!{allegra,decvax,seismo}!rochester!ken ARPA: ken@rochester.arpa
Snail: CS Dept., U. of Roch., NY 14627. Voice: Ken!