[net.sources] Re-posting of 'calend'

israel@umcp-cs.UUCP (Bruce Israel) (09/27/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting calend.c'
sed 's/^X//' <<'//go.sysin dd *' >calend.c
X/* Copyright 1983 - University of Maryland */

X/*
 *
 * calend - An appointment calendar maintainer
 *
 * calend is used to remind a user about appointments.
 *
 * When calend is called, it looks at the users appointments
 * in the .calrc file in his home directory.  This file is made
 * up of lines of the form:
 *
 * <opts> <start-date> <end-date> <message string>
 *
 * where <opts> is a string made up of
 * any of 'm','a','1','r','d', 'x', '+', '*' and <start-date> and
 * and <end-date> are either days of the week, (e.g. 'Wed') or
 * a date of the year of the form:
 * mm/dd (eg 11/20), month dd (eg Nov 20), or dd Month (eg 20 Nov)
 *
 * In addition, <end-date> can be an '*' instead, signifying the
 * same day or date as start-date.  start-date and end-date must
 * both be either dates or days.
 *
 * When the current date is between <start-date> and <end-date>
 * inclusive (or if they are days then the day of the week is between
 * them) then the message will be processed according to the specified
 * options.  The meanings of the various options are as follows:
 *
 * 'm' : mail the message to the user.  The first time that 'calend'
 *	 is called during that period, send mail to the user using
 * 	 the message-string as mail-text.
 * 'a' : always print the message.  Every time 'calend' is called
 * 	 during that period, print the message string on the terminal.
 * '1' : print the message once.  Print the message on the terminal
 * 	 the first time 'calend' is run during that period.
 * 'r' : run the remind program.  The first four non-blank digits
 * 	 of the message string should be a time-of-day of the form
 * 	 'hhmm' which remind can use.
 * 'd' : delete this entry from the file when 'calend' is finished
 * 	 with it.
 * 'x' : the message is executed as a process.  The '1' or 'a' says
 *	 whether to do it once or always during a period.  If neither,
 *	 the default is 'a'.
 * '+' : move the message dates when it's done.  The first word of
 *	 the message should be the offset of the form:
 *			+ [ <months> {m,/} ] [ <days> [d] ]
 *	 where '[' & ']' mean optional, and '{' & '}' mean a choice.
 *	 For example, +14 (two weeks), +3m (three months), +3/2
 *	 (three months and two days), +3m2d (same).
 * '*' : 'pending' flag used by the program internally to indicate
 * 	 that more processing needs to be done.
 * '#' : comment flag to indicate that this line shouldn't be processed
 *	 at all.
 * ':' : comment flag, not deleted by 'calend -c'.
 *
 * Author: Bruce Israel, umcp-cs!israel, israel@Maryland
 */

#include <signal.h>
#include "globals.h"
#include "process.h"

struct tm	*localtime();

onintr()
{
    char tmname[BUFSIZ];
    int i;

	for (i = 0; i <= level; i++) {
	    sprintf(tmname,"%s-%c",tmplate,'A' + level);
	    unlink(tmname);
	}

	exit(1);
}

main(argc, argv)
int argc; char *argv[];
{
	char *ptr;
	char	fname[BUFSIZ];	/* file name */
	int f_opt = NO;
	long	tmptime;	/* place to put time */
	struct tm *curtim;	/* current time structure */
	bool	debugdate;	/* user-specified date? */
	int	userdate,userday; /* specifiable date for debugging */

	argc--, argv++;
	while (argc > 0) {
		ptr = *argv;
		while (*ptr) switch (*ptr++) {

		case '-':
			break;

		case 'C':
		case 'c':
			clean = YES;
			break;

		case 'f':
		case 'F':
			f_opt = YES;
			if (*ptr == 0) {
			    argv++;
			    if (*argv == 0) {
				fprintf(stderr,
					"calend: no file given with '-f'.\n");
				exit(1);
			    }
			    strcpy(fname,*argv);
			}
			else {
			    strcpy(fname,ptr);
			    *ptr = 0;
			}
			break;

		case 'I':
		case 'i':
			i_opt = YES;
			break;

		case 'R':
		case 'r':
			remonly = YES;
			break;

		case 'D':
		case 'd':
			debugdate = YES;
			if (*ptr == 0) {
			    fprintf(stderr,
					"calend: no date given with '-D'.\n");
			    exit(1);
			}
			userday = *ptr++ - '0';
			userdate = atoi(ptr);
			*ptr = 0;
			break;

		default:
			fprintf(stderr,
			"Unknown option '%c' - ignored\n",ptr[-1]);
		}
		argc--, argv++;
	}

	/* get current date */

	if (debugdate) {
	    cdate = userdate;
	    cwday = userday;
	}
	else {
	    time(&tmptime);
	    curtim = localtime(&tmptime);
	    cwday = curtim -> tm_wday;
	    cdate = (curtim -> tm_mon) * 100 + curtim -> tm_mday + 100;
	}

	setgid(getegid());
	setuid(geteuid());
	
	/* get dates file name */

	if (! f_opt) sprintf(fname,"%s/%s",getenv("HOME"),RCFILE);

	sprintf(tmplate,"/tmp/cal-%d",getpid());

	level = 0;

	signal(SIGINT,onintr);

	process_file(fname,1);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 calend.c
	/bin/echo -n '	'; /bin/ls -ld calend.c
fi
/bin/echo 'Extracting process.c'
sed 's/^X//' <<'//go.sysin dd *' >process.c
#include "globals.h"
#include "process.h"

#define CKPERIOD(start,end,t) ((start <= end) ? ((t >= start) && (t <= end)) \
					      : ((t >= start) || (t <= end)))
#define CURSIZE(a,b)	((a >= b) ? (a - b) : (1200 + a - b))

process_file(fname,writeerrors)
char *fname;
int writeerrors;
{
    char	tmname[1024];	/* temp file name */
    char	*ptr;
    int		i,j;		/* counters for for loops */
    int		plusmoved;	/* has entry been shifted because of aa '+' */
    int		msgptr = 0;	/* pointer to message in 'line' */
    char	flags[BUFSIZ];	/* options per line */
    FILE	*fil;		/* dates file descriptor */
    FILE	*tfil;		/* temp file descriptor */
    int		plusbmsg;	/* message plus blanks for plus option */
    int		modflag;	/* has dates file been modified? */
    int		modline;	/* has current line been modified? */
    char	incname[BUFSIZ]; /* include file name */

    /* Command option flags */

    bool	execute;	/* execute message line as process? */
    bool	mail;		/* send mail? */
    bool	always;		/* always print? */
    bool	once;		/* print first time only? */
    bool	remind;		/* run remind program? */
    bool	plusdate;	/* move date when ready? */
    bool	pending;	/* should something be done with this line? */
    bool	newpending;	/* pending afterwards? */
    bool	ignore;		/* commented out? */
    bool	ignoresave;	/* keep comment? */
    bool	delete;		/* delete later? */
    bool	includefile;	/* additionally use other dates file? */
    bool	nodates;	/* no dates for parsing on line */
    int		in_period;	/* currently within period? */
    int		real_in_period;
    char	cmdbuf[BUFSIZ];
    int		canwrite;

	fil = fopen(fname,"r");
	if (fil == NULL) {
		perror(fname);
		exit(errno);
	}

	canwrite = 1;

	if (access(fname,2)) {
	    canwrite = 0;
	    if (writeerrors)
		fprintf(stderr,"calend: can't write out %s.\n", fname);
	}

	/* open temp for copying */

	sprintf(tmname,"%s-%c",tmplate,'A'+level);
	tfil = fopen(tmname,"w");
	if (tfil == NULL) {
		perror(tmname);
		exit(errno);
	}

	modflag = 0;

	/* Main loop */

	while(fgets(cmdbuf,sizeof cmdbuf,fil)) {

		/* Reset flags */

		strcpy(flags,"#");
		strcpy(line,"");

		if (*cmdbuf == ':' || *cmdbuf == '#' || *cmdbuf == '<')
		     sscanf(cmdbuf,"%1s%[^\n]\n",flags,line);
		else sscanf(cmdbuf,"%s%[^\n]\n",flags,line);

		mail = NO;
		always = NO;
		once = NO;
		remind = NO;
		plusdate = NO;
		execute = NO;
		delete = NO;
		pending = NO;
		newpending = NO;
		ignore = NO;
		ignoresave = NO;
		nodates = NO;
		includefile = NO;
		ptr = flags;
		lineptr = 0;
		monthmax = 0;
		daymax = 0;
		in_period = 0;
		modline = 0;

		while(*ptr) switch(*ptr++) {
			case 'M' :
			case 'm' :
				mail = YES;
				break;

			case 'A' :
			case 'a' :
				always = YES;
				break;

			case '1' :
				once = YES;
				break;

			case 'D' :
			case 'd' :
				delete = YES;
				break;

			case 'R' :
			case 'r' :
				remind = YES;
				break;

			case '+' :
				plusdate = YES;
				break;

			case 'X' :
			case 'x' :
				execute = YES;
				break;

			case '*' :
				pending = YES;
				break;

			case '<' :
				includefile = YES;
				nodates = YES;
				break;

			case ',' :
				break;

			case ':' :
				ignoresave = YES;
			case '#' :
				ignore = YES;
				nodates = YES;
				break;

			default :
				fprintf(stderr,"Illegal flag %c\n",*(ptr-1));
		}

		if (nodates) {
		    if (includefile) {
			sscanf(line,"%s",incname);
			level++;
			process_file(incname,0);
			level--;
		    }
		    goto copy;
		}

		/* ignore everything but remind lines if remind-only is set */

		if (remonly && !remind)
		{
		    newpending = pending;
		    goto copy;
		}

		/* ignore comments and error lines */

		if (ignore || yyparse()) goto copy;

		plusbmsg = lineptr;

		while (line[lineptr] == ' ' || line[lineptr] == '\t')
			lineptr++;

		msgptr = lineptr;

		if (plusdate) get_plus_parts();

		while (line[lineptr] == ' ' || line[lineptr] == '\t')
			lineptr++;

		if (plusdate && offmonth == 0 && offday == 0)
		    fprintf(stderr,"No offset given for '+' option in: %s\n",
					line+msgptr);

		/* handle '*[{+-}offset]' appropriately */

		if ((type1 * type2) == 0) {
		    dtype = 0;
		    monthmax = 1;
		    month1[0] = 0;
		    month2 = 0;
		}
		else dtype = 1;

		tdate = dtype ? cdate : cwday;

		if ((type1 == -1) || (type2 == -1)) {
		    if ((type1 == 2) || (type2 == 2))
			for (i=0;(i < monthmax) && (! in_period);i++) {
			    dayt1 = month1[i] * 100 + day1[i];
			    dayt2 = add_date(dayt1,star);

			    if (type1 == -1) SWAP(dayt1,dayt2);

			    in_period = CKPERIOD(dayt1,dayt2,tdate);
			}
		    else {
			for (i=0;(i < monthmax) && (! in_period);i++)
			for (j=0;(j < daymax) && (! in_period);j++) {
			    dayt1 = month1[i] * 100 + day1[j];
			    dayt2 = add_date(dayt1,star);

			    if (type1 == -1) SWAP(dayt1,dayt2);

			    in_period = CKPERIOD(dayt1,dayt2,tdate);
			}
		    }
		}
		else {
		    dayt1 = month1[0]*100+day1[0];
		    dayt2 = month2 * 100 + day2;

		    in_period = CKPERIOD(dayt1,dayt2,tdate);
		}

		mptr = message;

		plusmoved = 0;

		if (plusdate && in_period && (newpending = 1,dtype == 0)) {
		    int diff1;
		    int diff2;

		    diff1 = dayt1 - tdate - ((dayt1 <= tdate) ? 0 : 7);
		    diff2 = dayt2 - tdate + ((tdate <= dayt2) ? 0 : 7);

		    dtype = 1;
		    tdate = cdate;

		    plusmoved = 1;

		    dayt1 = add_date(cdate,diff1);
		    dayt2 = add_date(cdate,diff2);

		}

		if (plusdate && (dtype == 1) && (! in_period)
			&& ((monthmax * daymax) == 1)
			&& (offday > 0 || offmonth > 0)) {
		    int diff,newdate,tmon,tday,oldsize, pnd = pending;

		    diff = ((dayt2 < dayt1) ? (dayt1 - dayt2)
					    : (365 - (1 + dayt2 - dayt1))) / 2;
		    newdate = add_date(dayt2,diff);

		    oldsize = 1200;

		    while (CKPERIOD((dayt2+1),newdate,tdate) &&
			   (CURSIZE(newdate,(dayt2+1)) < oldsize)) {

			oldsize = CURSIZE(newdate,(dayt2+1));

			if (! pnd && ! i_opt && ! execute && ! remind) {

			    if (mptr != message) *mptr++ = '\n';

			    create_message(line+lineptr);
			    create_message(" [old message from %2]");
			    real_in_period = in_period;
			    in_period = 1;
			}

			pnd = 0;

			plusmoved = 1;
			modline++;

			tmon = dayt1 / 100 + offmonth;
			tday = dayt1 % 100 + offday;
			dayt1 = add_date((tmon * 100 + tday),0);

			tmon = dayt2 / 100 + offmonth;
			tday = dayt2 % 100 + offday;
			dayt2 = add_date((tmon * 100 + tday),0);

		    }

		    if (CKPERIOD(dayt1,dayt2,tdate)) {
			if (mptr != message) *mptr++ = '\n';
			create_message(line+lineptr);
			in_period = 1;
			pending = 0;
		    }
		}
		else if (in_period) create_message(line+lineptr);

		if (in_period) {

		    if (execute) {
			if (! once) always = YES;
			if (always || (once && ! pending)) execute_thing();
			if (!always) newpending = YES;
		    }
		    else {
			if (remind) run_remind();
			if (mail && ! pending) send_mail();
			if (once && ! pending) pr_message();
			if (always) pr_message();
			if (once || mail) newpending = YES;
			if ((once || mail) &&
			    (!remind)	   &&
			    (!always)	   &&
			    delete) {
				ignore = YES;
				modline++;
			    }
		    }
		    if (delete) newpending = YES;
		    if (plusdate && real_in_period) newpending = YES;
		}
		else {
			newpending = NO;
			if (delete && pending) {
			    ignore = YES;
			    modline++;
			}
		}

	copy:
		if (canwrite) {
		    char opts[10];

		    if (newpending != pending) modline++;

		    if (! modline) {
			if (!clean || !ignore || ignoresave)
			    fprintf(tfil,"%s",cmdbuf);
			continue;
		    }
		    modflag++;

		    ptr = opts;
		    if (ignore) *ptr++ = ignoresave ? ':' : '#';
		    if (execute) *ptr++ = 'x';
		    if (once) *ptr++ = '1';
		    if (always) *ptr++ = 'a';
		    if (remind) *ptr++ = 'r';
		    if (mail) *ptr++ = 'm';
		    if (delete) *ptr++ = 'd';
		    if (plusdate) *ptr++ = '+';
		    if (newpending && ! ignore) *ptr++ = '*';
		    *ptr = 0;

		    if (!clean || !ignore || ignoresave) {
			fprintf(tfil,"%s", opts);

			if (ignore || ! plusmoved) fprintf(tfil,"%s\n", line);
			else {
			    char *l = line;

			    while (*l == ' ' || *l == '\t') putc(*l++,tfil);

			    if (type1 == -1) {
				if (star == 0) fprintf(tfil,"*");
				else if (star < 0) fprintf(tfil,"*%d",star);
				else fprintf(tfil,"*+%d",star);
			    }
			    else {
				mptr = message;
				create_message("%1");
				fprintf(tfil,"%s",message);
			    }
			    fprintf(tfil,"\t");
			    if (type2 == -1) {
				if (star == 0) fprintf(tfil,"*");
				else if (star < 0) fprintf(tfil,"*%d",star);
				else fprintf(tfil,"*+%d",star);
			    }
			    else {
				mptr = message;
				create_message("%2");
				fprintf(tfil,"%s",message);
			    }

			    if (lastchar != '}') putc(lastchar,tfil);
			    fprintf(tfil,"%s\n",line+msgptr);
			}
		    }
		}
	}

	fclose(tfil);

	if (canwrite && modflag) copy_file(tmname,fname);
	unlink(tmname);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 process.c
	/bin/echo -n '	'; /bin/ls -ld process.c
fi
/bin/echo 'Extracting util.c'
sed 's/^X//' <<'//go.sysin dd *' >util.c
X/* Copyright 1983 - University of Maryland */

#include <signal.h>
#include <ctype.h>
#include "globals.h"

FILE	*popen();
char	*getlogin();
struct passwd	*getpwuid();
int	onintr();

#ifdef USG
#define MAIL "/usr/bin/mailx -s 'Reminder Service' %s"
#else
#define MAIL "/usr/ucb/mail -s 'Reminder Service' %s"
#endif

char *mon_nm[]={
    "January","February","March","April","May","June","July","August",
    "September","October","November","December"
};

char *relative[]={
    "the day before yesterday", "yesterday", "today", "tomorrow",
    "the day after tomorrow"
};

int mon_len[]={
    31,28,31,30,31,30,31,31,30,31,30,31
};

char *day_nm[]={
    "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
};

get_plus_parts()
{
    int num = 0;
    char c;

    offmonth = 0;
    offday = 0;

    if (line[lineptr] != '+') return;

    lineptr++;

    while (isdigit(c = line[lineptr++])) num = num * 10 + c - '0';

    lineptr--;

    if (line[lineptr] == 'd' || line[lineptr] == 'D') {
	lineptr++;
	offday = num;
	return;
    }

    if (line[lineptr] != '/' && line[lineptr] != 'm' && line[lineptr] != 'M') {
	offday = num;
	return;
    }

    offmonth = num;
    num = 0;

    lineptr++;

    while (isdigit(c = line[lineptr++])) num = num * 10 + c - '0';
    lineptr--;

    if (line[lineptr] == 'd' || line[lineptr] == 'D') lineptr++;
    offday = num;
    return;
}

char *datestr(datenum,mtype,forward)
int datenum,mtype,forward; {
    register char dstri[BUFSIZ];
    int ddiff;

    if ((mtype == 3) && (abs(ddiff = reldiff(datenum,tdate,forward)) < 3))
    {
	sprintf(dstri,"%s", relative[ddiff+2]);
	return(dstri);
    }
    if (dtype) {
	if (mtype == 2) sprintf(dstri,"%d",datenum % 100);
	else if (mtype == 1)
	sprintf(dstri,"%s",mon_nm[(datenum / 100 - 1) % 12]);
	else sprintf(dstri,"%s %d",mon_nm[(datenum / 100 - 1) % 12],
				   datenum % 100);
	return(dstri);
    }
    if (mtype == 1) return("");
    return day_nm[datenum % 7];
}

int reldiff(d1,d2,firstmore)
int d1,d2,firstmore;
{
    int count = 0;

    if (firstmore) SWAP(d1,d2);
    while ((d1 != d2) && (count < 3)) {
	count++;
	d1 = add_date(d1,1);
    }
    if (! firstmore) count = 0 - count;
    return(count);
}

int add_date(date, offset)
int date, offset;
{
    int mo, da, sum;

    if (!dtype) {
	sum = date + offset;
	while (sum < 0) sum = sum + 7;
	sum = sum % 7;
	return (sum);
    }

    mo = date / 100;
    da = date % 100 + offset;

    mo = mo % 12;
    if (mo == 0) mo = 12;

    while (da < 1) {
	mo--;
	if (mo == 0) mo = 12;
	da = da + mon_len[mo - 1];
    }

    while (da > mon_len[mo - 1]) {
	da = da - mon_len[mo - 1];
	mo++;
	if (mo == 13) mo = 1;
    }

    return (mo * 100 + da);
}

getnextchar() {
	return(lastchar = line[lineptr++]);
}

create_message(str)
char *str;
{
    register char c, *cstr, nstr[20], m;
    int i,mt;

    while (c = *str++) {
	if (c == '%') {
	    if (((m = *str++) == 'm') || (m == 'M')) mt = 1;
	    else if ((m == 'd') || (m == 'D')) mt = 2;
	    else if ((m == 'r') || (m == 'R')) mt = 3;
	    else {
		str--;
		mt = 0;
	    }
	    switch (c = *str++) {
	    case '1' :
	    	cstr = datestr(dayt1,mt,0);
		break;
	    case '2' :
	    	cstr = datestr(dayt2,mt,1);
		break;
	    case 'c' :
	    case 'C' :
	    	cstr = datestr(tdate,mt,0);
		break;
	    case '%' :
	    	cstr = "%";
		break;
	    default :
	    	cstr = "%";
		str--;
		if (mt) str--;
		break;
	    }
	    while(*mptr++ = *cstr++);
	    mptr--;
	}
	else *mptr++ = c;
    }
    *mptr = '\0';
}

send_mail ()
{
	register char *name = getlogin();
	register FILE *mpipe;
	char linebuf[BUFSIZ];

	if (!name) name = getpwuid(getuid()) -> pw_name;
	sprintf(linebuf,MAIL,name);
	mpipe = popen(linebuf,"w");
	fprintf(mpipe,"%s\n",message);
	pclose(mpipe);
}

run_remind() {
	register char time[BUFSIZ];
	register char restmsg[BUFSIZ];
	int tmp;

	sscanf(message,"%s %[^\n]",time,restmsg);

	strcpy(message,restmsg);	/* get new message for other options */

	if((tmp = fork()) == 0) {
		execl(REMIND,"remind","-f",time,restmsg,0);
		_exit(-999);
	}
	if (tmp == -1) {
		fprintf(stderr,"Help, I'm unforkable!\n");
	}
}

execute_thing()
{
	int pid;

	if ((pid = fork()) == 0) {
		execl("/bin/csh", "csh", "-cf", message, 0);
		perror("Fork of /bin/csh");
		_exit(1);
	}
	else if (pid == -1)
		fprintf(stderr,"Couldn't fork to run '%s'.\n",message);
}

pr_message() {
	printf("%s\n",message);
}

copy_file(from,to)
char *from, *to;
{
    FILE *fromfile, *tofile;
    char fline[BUFSIZ];

    signal(SIGINT,SIG_IGN);

    if ((fromfile = fopen(from,"r")) == NULL) {
	perror(from);
	onintr();
	exit(errno);
    }

    if ((tofile = fopen(to,"w"))  == NULL) {
	perror(to);
	fclose(fromfile);
	signal(SIGINT,onintr);
	return;
    }
    
    while (fgets(fline, sizeof fline, fromfile))
	fputs(fline,tofile);

    fclose(fromfile);
    fclose(tofile);
    signal(SIGINT,onintr);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 util.c
	/bin/echo -n '	'; /bin/ls -ld util.c
fi
/bin/echo 'Extracting parser'
sed 's/^X//' <<'//go.sysin dd *' >parser
X/* Copyright 1983 - University of Maryland */

%{

#include "globals.h"
#include "pars.h"

%}

%start pgm

%token MONTH NUM DAY STAR OCURL CCURL SLASH DASH

%%

pgm	: MONTH numsecd
				{ ADDMONTH($1); type1= 1; YYACCEPT;}
	| NUM mosecd
				{ if (monthmax) ADDDAY($1);
				  else ADDMONTH($1); type1= 1; YYACCEPT;}
	| scurly lstsec
				{ type2 = -1; YYACCEPT;}
	| DAY secday
				{ type1= 0; ADDDAY($1); YYACCEPT; }
	| STAR either
				{ type1 = -1; star = $1; YYACCEPT;}
	;

numsecd	: NUM	secdate
				{ ADDDAY($1); }
	| scurly numlst CCURL STAR
				{ copydays(0); type2 = -1; star = $4; }
	;

mosecd	: MONTH secdate
				{ ADDMONTH($1); }
	| scurly monlst CCURL STAR
				{ type2 = -1; star = $4; }
	| sldash NUM secdate
				{ ADDDAY($2); }
	| sldash scurly numlst CCURL STAR
				{ copydays(0); type2 = -1; star = $5; }
	;

lstsec	: monlst CCURL nums STAR
				{ type1 = 1; copydays(0); star = $4; }
	| numlst CCURL sldash nums STAR
				{ type1 = 1; copymonths(0);
				  copydays(1); star = $5; }
	| numlst CCURL months STAR
				{ type1 = 1; copydays(0); star = $4; }
	| daylst CCURL STAR
				{ type1 = 0; star = $3; }
	| datl CCURL STAR
				{ type1 = 2; star = $3; }
	;

secday	: STAR
				{ type2 = -1; star = $1; }
	| DAY
				{ type2 = 0; day2 = $1; }
	;

either	: MONTH nums
				{ ADDMONTH($1); copydays(0); type2 = 1; }
	| NUM sldash nums
				{ ADDMONTH($1); copydays(0); type2 = 1; }
	| NUM months
				{ ADDDAY($1); type2 = 1; }
	| DAY
				{ ADDDAY($1); type2 = 0; }
	| scurly restlst
	;

secdate	: MONTH NUM
				{ month2 = $1; day2 = $2; type2 = 1; }
	| NUM sldash NUM
				{ month2 = $1; day2 = $3; type2 = 1; }
	| NUM MONTH
				{ month2 = $2; day2 = $1; type2 = 1; }
	| STAR
				{ type2 = -1; star = $1; }
	;

months	: MONTH
				{ ADDMONTH($1); }
	| scurly monlst CCURL
	;

nums	: NUM
				{ allocnum(); ADDNUM($1); }
	| scurly numlst CCURL
	;

restlst	: restdat
				{ type2 = 1;}
	| daylst CCURL
				{ type2 = 0;}
	| datl CCURL
				{ type2 = 2;}
	;

restdat	: monlst CCURL nums
				{ copydays(0); }
	| numlst CCURL sldash nums
				{ copymonths(0); copydays(1); }
	| numlst CCURL months
				{ copydays(0); }
	;

datl	: nnl
	| mnl
	| nml
	;

nnl	: NUM SLASH NUM
				{ ADDMONTH($1); ADDDAY($3); }
	| nnl NUM SLASH NUM
				{ ADDMONTH($2); ADDDAY($4); }
	;

mnl	: MONTH NUM
				{ ADDMONTH($1); ADDDAY($2); }
	| mnl MONTH NUM
				{ ADDMONTH($2); ADDDAY($3); }
	;

nml	: NUM MONTH
				{ ADDMONTH($2); ADDDAY($1); }
	| nml NUM MONTH
				{ ADDMONTH($3); ADDDAY($2); }
	;

numlst	: NUM
				{ ADDNUM($1); }
	| numlst NUM
				{ ADDNUM($2); }
	| NUM DASH NUM
				{ addnumseq($1,$3); }
	;

monlst	: MONTH 
				{ ADDMONTH($1); }
	| monlst MONTH
				{ ADDMONTH($2); }
	| MONTH DASH MONTH
				{ addmonseq($1,$3); }
	;

daylst	: DAY
				{ ADDDAY($1); }
	| daylst DAY
				{ ADDDAY($2); }
	| DAY DASH DAY
				{ adddayseq($1,$3); }
	;

sldash	: SLASH
	| DASH
	;

scurly	: OCURL
				{ allocnum(); }
	;

%%

#include "lex.yy.c"

yyerror(s)
char *s;
{
	fprintf(stderr,"%s:(%s)\n",s,line);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 parser
	/bin/echo -n '	'; /bin/ls -ld parser
fi
/bin/echo 'Extracting scanner'
sed 's/^X//' <<'//go.sysin dd *' >scanner
X/* Copyright 1983 - University of Maryland */

%{

#include "y.tab.h"

#undef  input
#undef  output
#undef  unput
#define allprint(c) (putchar(c))
#define sprint(str) (printf("%s",str))
#define output(c) (putchar(c))
#define input() (yytchar=yysptr>yysbuf?U(*--yysptr):getnextchar())
#define unput(c) (*yysptr++=(c))
#define yywrap() 1

%}
star	\*
dash	\-
slash	\/
plus	\+
ocurl	\{
ccurl	\}
space	[ \t] 
nu	[0-9][0-9]*
arb	[a-zA-Z]*
a	[aA]
b	[bB]
c	[cC]
d	[dD]
e	[eE]
f	[fF]
g	[gG]
h	[hH]
i	[iI]
j	[jJ]
k	[kK]
l	[lL]
m	[mM]
n	[nN]
o	[oO]
p	[pP]
q	[qQ]
r	[rR]
s	[sS]
t	[tT]
u	[uU]
v	[vV]
w	[wW]
x	[xX]
y	[yY]
z	[zZ]

%%

{star}{space}		{yylval=0;return(STAR);}
{star}{plus}{nu}	{sscanf(yytext,"*%d",&yylval);
			 return(STAR);}
{star}{dash}{nu}	{sscanf(yytext,"*%d",&yylval);
			 return(STAR);}

{nu}			{sscanf(yytext,"%d",&yylval);
			 return(NUM);}
{ocurl}			{return(OCURL);}
{ccurl}			{return(CCURL);}
{dash}			{return(DASH);}
{slash}			{return(SLASH);}

{j}{a}{n}{arb}	{yylval=1;return(MONTH);}
{f}{e}{b}{arb}	{yylval=2;return(MONTH);}
{m}{a}{r}{arb}	{yylval=3;return(MONTH);}
{a}{p}{r}{arb}	{yylval=4;return(MONTH);}
{m}{a}{y}{arb}	{yylval=5;return(MONTH);}
{j}{u}{n}{arb}	{yylval=6;return(MONTH);}
{j}{u}{l}{arb}	{yylval=7;return(MONTH);}
{a}{u}{g}{arb}	{yylval=8;return(MONTH);}
{s}{e}{p}{arb}	{yylval=9;return(MONTH);}
{o}{c}{t}{arb}	{yylval=10;return(MONTH);}
{n}{o}{v}{arb}	{yylval=11;return(MONTH);}
{d}{e}{c}{arb}	{yylval=12;return(MONTH);}

{s}{u}{n}{arb}	{yylval=0;return(DAY);}
{m}{o}{n}{arb}	{yylval=1;return(DAY);}
{t}{u}{e}{arb}	{yylval=2;return(DAY);}
{w}{e}{d}{arb}	{yylval=3;return(DAY);}
{t}{h}{u}{arb}	{yylval=4;return(DAY);}
{f}{r}{i}{arb}	{yylval=5;return(DAY);}
{s}{a}{t}{arb}	{yylval=6;return(DAY);}

[ \t,]	;
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 scanner
	/bin/echo -n '	'; /bin/ls -ld scanner
fi
/bin/echo 'Extracting globals.h'
sed 's/^X//' <<'//go.sysin dd *' >globals.h
X/* Copyright 1983 - University of Maryland */

#include <stdio.h>
#include <pwd.h>
#include <time.h>

#define NO	0
#define YES	1
#define RCFILE	".calrc"	/* user's rc file */
#define REMIND "/usr/local/remind"

#define SWAP(a,b) { int temp; temp = a; a = b; b = temp; }

typedef	char	bool;
extern	int	errno;

char	line[BUFSIZ];		/* dates and string on rest of line */
int	lineptr;		/* current location in 'line' */
char	lastchar;		/* last character sent to 'yacc' */

int	type1, type2;		/* date types for first and second date,
				   -1 - *, 0 - day, 1 - date, 2 - date_list */
int	dtype;			/* day or date type for line, 0,1 as above */
int	star;			/* offset if relative dates */
int	month1[35],day1[35];	/* arrays for month and day elements */
int	monthmax, daymax;	/* max locations for days and months arrays */
int	month2, day2;		/* second day items */
int	tnum[2][35];		/* temp nums list */
int	tnummax[2];		/* max location for tnums */
int	cnum;			/* current number array */

int tdate;
char message[BUFSIZ];		/* buffer for creating the message */
char *mptr;			/* pointer into message array */
int dayt1, dayt2;
int offmonth, offday;		/* offsets for '+' option */
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 globals.h
	/bin/echo -n '	'; /bin/ls -ld globals.h
fi
/bin/echo 'Extracting process.h'
sed 's/^X//' <<'//go.sysin dd *' >process.h
X/* Copyright 1983 - University of Maryland */

int level;		/* level of recursion on included files '<' */
char tmplate[BUFSIZ];	/* temp file name template "/tmp/cal-<pid>-A,B ..." */
bool clean;		/* was '-c' (clean up) option given? */
bool i_opt;		/* was '-i' (ignore old messages) given? */
bool remonly;		/* '-r' (remind only) */
int cdate,cwday;	/* current date and weekday */
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 process.h
	/bin/echo -n '	'; /bin/ls -ld process.h
fi
/bin/echo 'Extracting pars.h'
sed 's/^X//' <<'//go.sysin dd *' >pars.h
X/* Copyright 1983 - University of Maryland */

#define ADDMONTH(var) month1[monthmax++]=var
#define ADDDAY(var) day1[daymax++]=var
#define ADDNUM(var) tnum[cnum][tnummax[cnum]++]=var

allocnum()
{
    if (tnummax[0] > 0) cnum = 1;
    else cnum = 0;
}

addnumseq(st,fin)
int st,fin;
{
    int i;
    for (i=st; i <= fin; i++) ADDNUM(i);
}

addmonseq(st,fin)
int st,fin;
{
    int i;

    fin++;
    if (fin > 12) fin = 1;

    ADDMONTH(st);

    for (i = st + 1; i != fin; i = (i >= 12) ? 1 : i+1) ADDMONTH(i);
}

adddayseq(st,fin)
int st,fin;
{
    int i;

    fin++;
    if (fin > 7) fin = 1;

    ADDDAY(st);

    for (i = st + 1; i != fin; i = (i >= 7) ? 1 : i+1) ADDDAY(i);
}

copydays(ar)
int ar;
{
    int i;

    for (i = 0; i < tnummax[ar]; i++) day1[i] = tnum[ar][i];
    daymax = i;
    tnummax[ar] = 0;
}

copymonths(ar)
int ar;
{
    int i;

    for (i = 0; i < tnummax[ar]; i++) month1[i] = tnum[ar][i];
    monthmax = i;
    tnummax[ar] = 0;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 pars.h
	/bin/echo -n '	'; /bin/ls -ld pars.h
fi
/bin/echo 'Extracting calend.1l'
sed 's/^X//' <<'//go.sysin dd *' >calend.1l
X.TH CALENDAR 1 
X.SH NAME
calend \- An appointment calendar maintainer
X.SH SYNOPSIS
X.B calend
[
X.B \-c
]
[
X.B \-i
]
[
X.B \-r
]
[
X.B \-DdMMDD
]
[
X.B \-F <file>
]
X.SH DESCRIPTION
X.IR calend
is used to remind a user about appointments.
When calend is called, it looks at the users appointments
in the .calrc file in his home directory.  This file is made
up of lines of the form:
X.sp
X.ce
<opts> <start-date> <end-date> <message string>
X.sp
where <opts> is a string made up of any of
X.B 'xma1rd+*'
either contiguous or separated by commas,
and <start-date> and <end-date> are either days of the week,
(e.g. 'Wed'), dates of the year of the form:
mm/dd (eg 11/20), month dd (eg Nov 20), or dd Month (eg 20 Nov),
or a relative offset.  A relative offset is either an '*', which
means replace this date by the other date, '*+<num>', which means
add the number to the other date to get this date, or '*-<num>', which
means that <num> should be subtracted from the other date to get this
date.  Both dates must be of the same type, either both days, or both dates,
and they cannot both be relative offsets.
X.PP
If one of the dates is a relative offset, then the other can be a set
specification.  For days of the week a set specification is a list or
range of days within curly
braces, (e.g. "{ sun tues thurs }" or "{tues - fri}").  For a date of
the year specification, Any or all of the month and dates can be sets,
(e.g. "{3 7 11}/1", "Jan {1 3 5}", or "{Jan - March} 1").  You can also
have dates of the form "{1/3 2/14 11/22}" or "{jan 25, mar 3, sep 24}"
but you cannot nest them.
X.PP
When the current date is between any element of <start-date> and <end-date>
inclusive (or if they are days then the day of the week is between
them) then the message will be processed according to the specified
options.  The meanings of the various options are as follows:
X.br
X.TP
X.I x
execute the message as a process.  If the 'x' option is given, all other
options except '1', 'a', '+', and 'd' will be ignored.  The 'd' option will
act as normal, but the '1' and 'a' options will indicate whether to do
the execution the first time in the period or always during the period.
  [If neither is given, then always is assumed.]
X.TP
X.I <
The rest of the line is expected to be a file name that
X.B calend
will recursively process as a dates file.  This is useful for groups
of people with many entries in common (i.e. meetings and talks announcements,
paydays, etc.)
X.TP
X.I m
mail the message to the user.  The first time that 'calend'
is called during that period, send mail to the user using
the message-string as mail-text.
X.TP
X.I a
always print the message.  Every time 'calend' is called
during that period, print the message string on the terminal.
X.TP
X.I 1
print the message once.  Print the message on the terminal
the first time 'calend' is run during that period.
X.TP
X.I r
run the remind program.  The first four non-blank digits of the message
string should be a time-of-day of the form 'hhmm' which remind can use.
X.TP
X.I d
delete this entry from the file when 'calend' is finished
with it.  When an entry is deleted, it is actually commented out
from the file with a '#' flag.
X.TP
X.I +
move the dates of this entry forward when done.  The first word of
the message is the amount of months and days to move the message
forward by (i.e. +14 or 14d (two weeks), +1/2 (one month and two days),
 +3m2d (three months and two days) etc).  The dates are expected to
be dates of the year (i.e. month and date of month) and can only be
single dates or a '*' specification.  Sets of dates will not
be moved.  If the period is specified in days of the week, then they
will automatically be converted to dates the first time
X.B calend
is run in that period.
X.sp
X.B calend
uses a simple heuristic to decide when to move the dates of a line
with the '+' option.  The dates of a line will be moved when the current
date is in the first half of the inverse of the period.  For example,
If you have the line:
X.sp
X.nf
a+	sept 23	*	+14 Payday today
X.fi
X.sp
then the dates will be moved to October 7 (or later) when
X.B calend
is executed during the first half of the 364 day period from september 24
through September 22.  If the current date was after October 7, the
dates will continue to be moved until the current date is within or before
that period.  This is used when
X.B calend
is not run within the period.  If the -i option is not set, then each old
message that was missed will be printed out (marked as such).
X.TP
X.I *
\&'pending' flag used by the program internally to indicate
that more processing needs to be done.
X.PP
If a line in the .calrc flag starts with a '#' or a ':', that
line is considered to be a comment and is not processed.
X.PP
The '-c' (clean) option says to actually delete all lines beginning with
a '#' rather than just ignoring them.  Comments beginning with ':' are
still kept in the file.
X.PP
The '-i' (ignore old messages) option says not to print out previously
missed messages from the '+' option.
X.PP
The '-r' (remind only) option says to only process lines that are
remind calls, and not to process everything.
X.PP
The '-f <file>' option instructs 
X.B calend
to use an alternate file instead of using $HOME/.calrc.
X.PP
The '-D' (set date) option is for debugging a .calrc.  It should be
immediately followed by a decimal of the form: dMMDD where 'd' is
a '0' - '6' meaning Sunday thru Saturday, and MMDD is the month and
date put together (e.g. -D31001 means run calend as if it were
Wednesday, October first).  These dates are not checked for validity.
X.PP
There are three variable specifications that can go in the message string.
These are '%1', '%2' and '%c'.  Any occurrences of '%1' in the
message string will be replaced by the starting  day or date.
All occurrences of '%2' will be replaced by the ending day or date from the
same line, and '%c' will be replaced by the current day or date.  If any
of these have the letter 'm' as a modifier (e.g.  %m1, %m2, %mc) then the
month name of that date will be substituted.  If the modifier is 'd', then
the numeric day of the month will be used (note that the %d specification
will print out the numeric version of the day if the date specification was
in days of the week.)  If the modifier is 'r', then a relative (to the
current date) term will be used.  The available relative terms are, the day
before yesterday, yesterday, today, tomorrow, and the day after tomorrow.
If none of these are applicable, the the regular date will be used.  In
addition, '%%' will insert a '%' sign into that location in the message
text.  All other two character pairs starting with '%' will be inserted as
is.  An example .calrc is as follows:
X.sp
X.in +5
X.nf
: temporary dates
ad	*-7	7/21		Party at bill's on %2
: permanent reminders
am	*-7	{jan - dec} 1	%m2's Rent is due
r	{mon wed fri}	*	1358 Go to math class
1m	*-7	11/14		mother's birthday is %r2
1m	*-7	{jan 12, apr 14, sept 15, dec 10}  insurance payment by %2
X.fi
X.in -5
X.sp
which says to a) print out the message "Party at bill's on July 21" every time
X.B calend
is called from July 14 through July 21 and then delete it after July 21, b)
print out "<month>'s rent is due" the week before the first of the month,
and also mail it to me, tell me to log off the system and go to my math
class at 1:58 in the afternoon on Mondays, Wednesdays, and Fridays, d)
inform me the week before my mother's birthday that it's coming up, and e)
tell me the week before an insurance payment is due.
X.SH AUTHOR
Bruce Israel
X.SH FILES
\&.calrc which contains the file of dates.
X.br
X/tmp/cal-*
X.br
remind, mail subprocesses
X.SH "SEE ALSO"
mail(1), remind(1l)
X.SH BUGS
X.PP
Poor error messages for illegal dates.
X.PP
Leap years are not recognized, so if a date is moved past February 29
of a leap year by the '+' option, that date will be off by one.
X.PP
Deletes on a line with multiple dates, (e.g. {july 21, sept 10}) will not
work properly.
X.PP
The arguments of
X.IR Remind
are not checked for validity.
X.PP
X.IR Remind
processes don't delete themselves until just before they are ready to print
out a message, so extra processes could be floating around.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 calend.1l
	/bin/echo -n '	'; /bin/ls -ld calend.1l
fi
/bin/echo 'Extracting Makefile'
sed 's/^X//' <<'//go.sysin dd *' >Makefile
SRCS=calend.c process.c util.c parser scanner globals.h process.h pars.h 
MISC=calend.1l Makefile remind.c remind.1l README addcal addcal.1l addcal.usg
LOCDIR=/usr/local
CFLAGS = -O

calend: calend.o process.o util.o y.tab.o
	cc $(CFLAGS) -o calend calend.o process.o util.o y.tab.o

calend.c: globals.h process.h

process.c: globals.h process.h

util.c: globals.h

y.tab.c: parser lex.yy.c
	yacc -d parser

lex.yy.c: scanner
	lex scanner

parser: globals.h pars.h

remind: remind.c
	cc $(CFLAGS) -o remind remind.c

clean:
	rm -f y.* lex.* *.o calend remind

print:
	pr $(SRCS) Makefile | lpr

$(LOCDIR)/calend: calend
	rm -f $(LOCDIR)/calend
	cp calend $(LOCDIR)/calend

inst-calend: $(LOCDIR)/calend

$(LOCDIR)/addcal: addcal
	rm -f $(LOCDIR)/addcal
	cp addcal $(LOCDIR)/addcal

inst-addcal: $(LOCDIR)/addcal

$(LOCDIR)/remind: remind
	rm -f $(LOCDIR)/remind
	cp remind $(LOCDIR)/remind

inst-remind: $(LOCDIR)/remind

inst-man: /usr/man/man1/calend.1l /usr/man/man1/remind.1l \
	  /usr/man/man1/addcal.1l

X/usr/man/man1/calend.1l: calend.1l
	cp calend.1l /usr/man/man1/calend.1l

X/usr/man/man1/addcal.1l: addcal.1l
	cp addcal.1l /usr/man/man1/addcal.1l

X/usr/man/man1/remind.1l: remind.1l
	cp remind.1l /usr/man/man1/remind.1l

all: calend remind

inst-all: inst-calend inst-addcal inst-remind inst-man
	@echo Installed
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 Makefile
	/bin/echo -n '	'; /bin/ls -ld Makefile
fi
/bin/echo 'Extracting remind.c'
sed 's/^X//' <<'//go.sysin dd *' >remind.c
X/* Copyright 1983 - University of Maryland */

X/*
 * remind [-f] [-#otlm] [time] ["reminder message"]
 *
 * (where time is in the form or hhmm or +Nm (minutes) or +Nh (hours))
 *
 * Reminds you when you have to leave, and why you have to leave.
 * Remind prompts for input and goes away if you hit return.
 * It nags you like a Nice Jewish Mother should.
 *
 * printing added 19 January 1982	by umcp-cs!andrew
 * "You have to <>" printing added	by umcp-cs!andrew
 * +N[hm] added 13sep83			by umcp-cs!andrew
 * 24hr time understanding added	by umcp-cs!andrew
 * -f option added 25sep83		by umcp-cs!andrew
 *
 * last update->Sun Sep 25 15:01:13 1983
 *
 * this was originally the leave(1) program
 *
 */

#include <stdio.h>
#include <signal.h>

static char	*sccsid = "@(#)remind.c      1.1 (U of MuD) 09/13/83";
char		origlogin[20], thislogin[20];
char		*whenleave;
char		buff[100], buff1[100];
int		ntimes = -1;		/* # of times to bother */

char	*ctime();
char	*getlogin();
int	hoursp();

main(argc, argv)
int	argc;
char	**argv; {
	long	when, tod, now, diff, hours, minutes;
	int	*nv;
	int	atoi();
	int	*localtime();
	char	absolute=0;		/* 1 if 24 hour time */
	char	quiet=0;		/* 1 if not to bitch */

	if(argv[1][0] == '-') {		/* set ntimes */
		ntimes = atoi(argv[1]+1);	/* hoser! give it a pointer! */
		if(argv[1][1] == 'f') {
			quiet=1;	/* invocation from a program */
			argv++;		/* sneakily hack argv */
			*argv = argv[-1]; /* hurrah for no bounds checking! */
			argc--;
			ntimes = -1;
			if(argv[1][0] == '-') {
				ntimes=atoi(argv[1]+1);
				argv++;
				*argv = argv[-1];
				argc--;
			}
		}
		else {
			argv++;		/* sneakily hack argv */
			*argv = argv[-1];
			argc--;		/* don't forget argc, twit! */
		}
	}

	if(argc < 2) {
		printf("When do you have to leave? ");
		fflush(stdout);
		buff[read(0, buff, sizeof buff)] = 0;	/* get answer */

		if(*buff == '\n') exit(0);	/* if \n exit */

		if(!*buff) exit(0);		/* if eof exit */

		printf("What do you have to do? ");
		fflush(stdout);
		buff1[read(0, buff1, sizeof buff1)-1] = 0; /*kill the \n*/
	}

	else if(argc == 2) {			/* remind time */
		printf("What do you have to do? ");
		fflush(stdout);
		buff1[read(0, buff1, sizeof buff1)-1] = 0; /*kill the \n*/

		if(*buff1 == '\n') exit(0);

		strcpy(buff, argv[1]);
	} else {			/* remind time "message" */
		strcpy(buff, argv[1]);
		strcpy(buff1, argv[2]);
	}

	strcpy(origlogin, getlogin());	/* do getlogin here so we won't barf */

	if(*buff == '+') {
		diff = atoi(buff+1);
		if(hoursp(buff+1)) diff *= 60;	/* 60 mins/hour */
		doalarm(diff);
	}

	if(*buff < '0' || *buff > '9') {
		if(!quiet) printf(
			"usage: %s [ -#otlm ] [time] [\"reminder-message\"]\n",
			*argv);
		exit(1);
	}

	tod = atoi(buff);
	hours = tod / 100;

	/*
	 * times like 2300 or 0730 don't get frobbed to the nearest 12 hours
	 *
	 */

	if(hours > 12) absolute = 1;
	if(*buff == '0') absolute = 1;

	if(!absolute && hours == 12) hours = 0;
	minutes = tod % 100;

	if(hours < 0 || hours > (absolute ? 24 : 12)
	   || minutes < 0 || minutes > 59) {
		if(!quiet) printf(
			"usage: %s [ -#otlm ] [time] [\"reminder message\"]\n",
			*argv);
		exit(1);
	}

	time(&now);
	nv = localtime(&now);
	when = (60 * hours) + minutes;
	if(!absolute && nv[2] > 12) nv[2] -= 12;	/* do am/pm bit */
	now = (60 * nv[2]) + nv[1];
	diff = when - now;

	if (now > when && absolute) {
	    if (! quiet) printf("It is already past that time.\n");
	    exit(0);
	}

	while(diff < 0)
		diff += (absolute ? 24 : 12) * 60;

	if((diff > (absolute ? 23 : 11) * 60) && !quiet) printf(
	    "That time has recently passed, but I'll remind you anyway.\n");

	doalarm(diff);
	exit(0);
}

doalarm(nmins)
long	nmins; {
	char		*msg1, *msg2, *msg3, *msg4;
	register int	i;
	long		slp1, slp2, slp3, slp4;
	long		seconds, gseconds;
	long		daytime;
	int		pid;

	seconds = 60 * nmins;

	if(seconds <= 0) seconds = 1;

	gseconds = seconds;

	msg1 = "You have to %s in 5 minutes!";

	if(seconds <= 60*5) {
		slp1 = 0;
	} else {
		slp1 = seconds - 60*5;
		seconds = 60*5;
	}

	msg2 = "Just one more minute before you have to %s!";

	if(seconds <= 60) {
		slp2 = 0;
	} else {
		slp2 = seconds - 60;
		seconds = 60;
	}

	msg3 = "Time to leave and %s!";
	slp3 = seconds;

	msg4 = "You're going to be too late to %s!";
	slp4 = 60;

	time(&daytime);
	daytime += gseconds;
	whenleave = ctime(&daytime);
	printf("You have to %s at %s", buff1, whenleave);
	if(pid=fork()) {
		if(pid == -1) {
			perror("can't fork");
			exit(1);
		}

		exit(0);
	}

	signal(SIGINT,  SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGTTOU, SIG_IGN);
#ifdef SIGTSTP
	signal(SIGTSTP, SIG_IGN);
#endif

	if(slp1) bother(slp1, msg1);
	if(slp2) bother(slp2, msg2);

	bother(slp3, msg3);

	if(ntimes != -1) {
		for(;ntimes--;) bother(slp4, msg4);
		exit(0);
	}

	for(;;) {
		bother(slp4, msg4);		/* be annoying */
	}
}

bother(slp, msg)
long	slp;
char	*msg; {

	delay(slp);
	printf("\7\7\7");
	printf(msg, buff1);
	printf("\r\n");
}

X/*
 * delay is like sleep but does it in 100 sec pieces and
 * knows what zero means.
 */
delay(secs)
long	secs; {
	int	n;

	while(secs > 0) {
		n = 100;
		secs = secs - 100;

		if(secs < 0) {
			n = n + secs;
		}

		if(n > 0) sleep(n);

		strcpy(thislogin, getlogin());
		if(strcmp(origlogin, thislogin)) exit(0);
	}
}

#ifdef V6
char *getlogin() {
#include <utmp.h>

	static struct utmp ubuf;
	int ufd;

	ufd = open("/etc/utmp", 0);
	seek(ufd, ttyn(0)*sizeof(ubuf), 0);
	read(ufd, &ubuf, sizeof(ubuf));
	ubuf.ut_name[sizeof(ubuf.ut_name)] = 0;
	return(&ubuf.ut_name);
}
#endif

X/*
 * return 1 if s is in the form of +[0-9]*h, 0 otherwise
 *
 */

int	hoursp(s)
char	*s; {
	char	numflg = 0;

	if(!*s) return 0;

	while(*s >= '0' && *s <= '9') s++;	/* skip the number */

	if(*s == 'h') return 1;

	return 0;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 remind.c
	/bin/echo -n '	'; /bin/ls -ld remind.c
fi
/bin/echo 'Extracting remind.1l'
sed 's/^X//' <<'//go.sysin dd *' >remind.1l
X.TH REMIND 1
X.UC
X.SH NAME
remind \- remind you when you have to leave and why
X.SH SYNOPSIS
X.B remind
[ -f ]
[ -#otlm ]
[ time ]
["reminder-message"]
X.SH DESCRIPTION
X.I Remind
waits until the specified time or for the specified interval,
then reminds you that you
have to leave.
You are reminded 5 minutes and 1 minute before the actual
time, at the time, and every minute thereafter, unless you give the
#otlm parameter, which is the number of messages telling you that you're
too late that
X.I remind
will print before giving up.
X.I Remind
won't complain about errors if you give the -f option (ala rm(1)).
When you log off,
X.I Remind
exits just before it would have
printed the next message.
X.PP
Time can be expressed in a number of formats. The simplest is hhmm, where
hh is the hour that you want to be reminded about, and mm is the minute.
X.PP
If you give hh as a 24 hour time (i.e.  0700 for 7 am, or 1900 for 7 pm),
you will be reminded at that exact time of the current day.  If it is past
that time on the same day, then you will not be reminded.
X.PP
Otherwise (you gave it something
like 700), you will be reminded within the next twelve hours.
An alternate form for the tod is +mm where mm is the number of
minutes for remind to hang out for; ie remind +240 "go to lunch"
will tell remind to remind you in 4 hours to go to lunch. A much
simpler way to do this is remind +4h "go to lunch", which will
also remind you to go to lunch in four hours.
X.PP
If not enough arguments are given,
X.I remind
prompts with "When do you
have to leave?". A reply of newline causes
X.I remind
to exit,
otherwise the reply is assumed to be a time. Then,
X.I remind
prompts with "What do you have to do?".
A reply of newline will cause remind to exit.
This form is suitable for inclusion in a
X.I .login
or
X.I .profile.
X.PP
Remind ignores interrupts, quits, and terminates.
To get rid of it you should either log off or kill it 
giving its process id.
X.SH SEE ALSO
calendar(1), leave(1)
X.SH AUTHORS
Mark Horton
X.PP
Andrew Scott Beals
X.SH BUGS
X.I Remind
is a pain in the ``neck''.
X.PP
If you do something like remind +24 "you've been hacking too long\\!",
logout, login within remind's 100 sec sleep cycle, and login to the terminal
that you started remind from, you will get reminded none the less. Perhaps
X.I remind
should look in utmp and check the login date instead of just checking to
see if the same person is logged in.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 remind.1l
	/bin/echo -n '	'; /bin/ls -ld remind.1l
fi
/bin/echo 'Extracting README'
sed 's/^X//' <<'//go.sysin dd *' >README
This directory contains the source for two programs; 
	1) calend - an appointment calendar maintainer
	2) remind - a program that reminds you to log off the
		    system and why;

Calend is a program that takes apointments specified in a .calrc
and notifies the user about them when the user wants to be notified.
An example .calrc is as follows:

: print out a message about bill's party on July 21 the week
: before it; delete when done.
ad	*-7	7/21		Party at bill's on %2
: tell me by mail the week before the monthly rent is due.
m	*-7	{jan - dec} 1	%m2's Rent is due
: remind me to get off the system for math class every mon wed and fri at 2.
r	{mon wed fri}	*	1400 Go to math class
: the week before its due, tell me about my insurance payment once
1	*-7	{jan 12, apr 14, sept 15, dec 10}  insurance payment by %2
: tell me on the next payday, and then move the message forward two weeks.
: (i.e. tell me about bi-weekly paydays)
a+	sept 23	*	+14 Payday today

In addition, there is a shell script here called 'addcal' which makes
it a little easier to add an entry to your .calrc.

The remind program nags you to get off the system at some time.  An example
call is:
	remind 1830 'go home for dinner'
which will nag you to get off the system for dinner at 6:30pm.  This
program is a modification of the 'leave' program.

To install:

1) First modify the variable LOCDIR in the file "Makefile"
   to reflect, where on your system the executables should go.

2) Next, modify the config file globals.h to indicate where you chose
   to store the "remind" executable.

3) If you are on a USG system, then add a "#define USG" to globals.h
   and do a 'mv addcal.usg addcal'.

4) run 'make inst-all' which will make and install the executables and
   the manual entries.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 README
	/bin/echo -n '	'; /bin/ls -ld README
fi
/bin/echo 'Extracting addcal'
sed 's/^X//' <<'//go.sysin dd *' >addcal
#! /bin/csh -f
set rcfile=~/.calrc remtime="" movo="" movc="" mov="" remc="" noglob
if (x$1 != x) set rcfile = $1
echo -n "Flags [<ra1mxd] "
set flgs = $<
while ("$flgs" == '?' || "$flgs" == )
	echo "Flags:"
	echo "< - use alternate dates file" 
	echo "r - remind at certain time"
	echo "a - print always during period"
	echo "1 - print once during period" ; echo "m - send mail"
	echo "x - execute program - a,1 say how often"
	echo "d - delete when finished" ; echo ""
	echo -n "Flags [ra1mxd] " ; set flgs = $<
end
echo $flgs | grep -s "<"
if ($status == 0) then
	echo -n "Alternate file: "; set altfile = $<
	while ("$altfile" == '?' || "$altfile" == )
		echo "The alternate file will be used as another dates file."
		echo -n "Alternate file: "; set altfile = $<
	end
	echo "<	$altfile"
	echo "<	$altfile" >> $rcfile
	exit 0
endif
echo $flgs | grep -s "r"
if ($status == 0) then
	echo -n "Remind time [hhmm]: " ; set remc = " " remtime = $<
	while ("$remtime" == '?')
		echo "Remind expects a four digit time, i.e. 2330."
		echo -n "Remind time [hhmm]: " ; set remtime = $<
	end
endif
echo $flgs | grep -s "d"
if ($status == 1) then
	echo -n "Move forward [+<num>]: "
	set movc = "" mov = $< 
	while ("$mov" == '?')
		echo "Enter months and days, eg: +3m2d, +14d, +1m"
		echo -n "Move forward [+<num>]: "
		set mov = $< 
	end
	echo $mov | egrep -s "^\+"
	if ($status == 1) then
		if ("$mov" != no && "$mov" != n && "$mov" != ) echo "bad date - $mov."
		set mov = ""
	endif
	if ("$mov" != ) set movo = "+" movc = " "
endif
echo -n "Start date: " ; set date1 = $<
while ("$date1" == '?')
	echo "Enter starting date; default is '*'"
	echo -n "Start date: " ; set date1 = $<
end
if ("$date1" == ) set date1 = "*"
echo -n "End date: " ; set date2 = $<
while ("$date2" == '?')
	echo "Enter ending date; default is '*'"
	echo -n "End date: " ; set date2 = $<
end
if ("$date2" == ) set date2 = "*"
if ("$date1$date2" == "**") then
	echo "No dates given."
	exit 1
endif
echo $flgs | grep -s "x"
if ($status == 0) then
	set cmd
	echo -n "Command: "
else
	echo -n "Message: "
endif
set msg = $<
while ("$msg" == '?' || "$msg" == )
	if ($?cmd) then
		echo "Enter command to be executed between $date1 and $date2"
		echo -n "Command: "
	else
		echo "Any message you want printed between $date1 and $date2"
		echo -n "Message: "
	endif
	set msg = $<
end
echo "$flgs$movo	$date1	$date2		$mov$movc$remtime$remc$msg"
echo "$flgs$movo	$date1	$date2		$mov$movc$remtime$remc$msg" >>$rcfile
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 755 addcal
	/bin/echo -n '	'; /bin/ls -ld addcal
fi
/bin/echo 'Extracting addcal.1l'
sed 's/^X//' <<'//go.sysin dd *' >addcal.1l
X.TH CALENDAR 1 
X.SH NAME
addcal \- Add an appointment to an appointment calendar
X.SH SYNOPSIS
X.B addcal
[
X.B <file>
]
X.SH DESCRIPTION
X.IR addcal
is used to add entries to an appointment calendar file as used by
X.B calend.
X.PP
X.B addcal
is a simple shell script that makes sure that all required fields are
in the entry.  It prompts for each field and any prompt can be given
a "?" to get help.
X.PP
if an argument is given, then it uses that argument as the dates file.
Otherwise, it uses $HOME/.calrc.
X.SH AUTHOR
Bruce Israel
X.SH FILES
\&.calrc which contains the file of dates.
X.SH "SEE ALSO"
calend(1)
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 addcal.1l
	/bin/echo -n '	'; /bin/ls -ld addcal.1l
fi
/bin/echo 'Extracting addcal.usg'
sed 's/^X//' <<'//go.sysin dd *' >addcal.usg
#! /bin/sh	## modified for sh instead of csh by sfjec!mjm

doentry()	## mjm: function called once for each entry
{
    remtime=""
    movo=""
    movc=""
    mov=""
    remc=""
    set -f		## mjm: noglob ??


    echo "Flags [<ra1mxd]: \c"
    read flgs
    while	[ "$flgs" = "?" -o "$flgs" = "" ]
    do
	    echo "Flags:"
	    echo "< - use alternate dates file" 
	    echo "r - remind at certain time"
	    echo "a - print always during period"
	    echo "1 - print once during period"
	    echo "m - send mail"
	    echo "x - execute program - a,1 say how often"
	    echo "d - delete when finished"
	    echo "q - quit; exit $0 now"	## mjm
	    echo "\nFlags [<ra1mxdq]: \c"
	    read flgs
    done

    set -- `getopt "<ra1mxdq" "-$flgs"`
    if	[ $? != 0 ]
    then	echo "Usage: Legal flags are: <ra1mxdq"
	    return 1;
    fi

    aflg=0	## not used
    flg1=0	## not used
    mflg=0	## not used
    xflg=0
    dflg=0
    for OPT in $*
    do 
	case $OPT in
	     -q)  exit 255;;		## mjm: No more input
	    -\<)  echo "Alternate file: \c"; read altfile;
		  while    [ "$altfile" = "?" -o "$altfile" = "" ]
		  do
		      echo "The alternate file will be used as another dates file."
		      echo "Alternate file: \c"; read altfile;
		  done
		  echo "<	$altfile"
		  echo "<	$altfile" >> $rcfile
		  return 0;;
	     -r)  echo "Remind time [hhmm]: \c"; remc=" "; read remtime;
		  while	[ "$remtime" = "?" ]
		  do
		      echo "Remind expects a four digit time, i.e. 2330."
		      echo "Remind time [hhmm]: \c"; read remtime;
		  done;;
	     -a) aflg=1;;
	     -1) flg1=1;;
	     -m) mflg=1;;
	     -x) xflg=1;;
	     -d) dflg=1;;
	esac
    done

    if	[ "$dflg" = "0" ]
    then
	    echo "Move forward [+<num>]: \c"
	    movc=""; read mov
	    while	[ "$mov" = "?" ]
	    do
		echo "Enter months and days, eg: +3m2d, +14d, +1m"
		echo "Move forward [+<num>]: \c"
		read mov
	    done
	    case $mov in		## mjm was: echo $mov | egrep "^\+"
		+*) stat=0;;
		 *) stat=1;;
	    esac
	    if [ "$stat" = "1" ]
	    then
		if	[ "$mov" != "no" -a "$mov" != "n" -a "$mov" != "" ]
		then
		    echo "bad date - $mov."
		fi
		mov=""
	    fi
	    if	[ "$mov" != "" ]
	    then	movo="+"; movc=" ";
	    fi
    fi

    echo "Start date: \c"; read date1
    while [ "$date1" = "?" ]
    do
	    echo "Enter starting date; default is '*'"
	    echo "Start date: \c"; read date1;
    done
    if	[ "$date1" = "" ]
    then	date1="*";
    fi

    echo "End date: \c"; read date2;
    while [ "$date2" = "?" ]
    do
	    echo "Enter ending date; default is '*'"
	    echo "End date: \c"; read date2;
    done
    if	[ "$date2" = "" ]
    then	date2="*";
    fi

    if	[ "$date1$date2" = "**" ]
    then	echo "No dates given."
	    return 1;
    fi

    if	[ "$xflg" = "1" ]
    then	echo "Command: \c";
    else	echo "Message: \c";
    fi
    read msg;
    while [ "$msg" = "?" -o "$msg" = "" ]
    do
	    if [ "$xflg" = "1" ]
	    then
		echo "Enter command to be executed between $date1 and $date2"
		echo "Command: \c"
	    else
		echo "Any message you want printed between $date1 and $date2"
		echo "Message: \c"
	    fi
	    read msg;
    done
    echo "$flgs$movo	$date1	$date2	$mov$movc$remtime$remc$msg"
    echo "$flgs$movo	$date1	$date2	$mov$movc$remtime$remc$msg" >>$rcfile
    return 0;
}

## mjm: Begins here; call function doentry repeatedly.

rcfile=$HOME/.calrc
if	[ "x$1" != "x" ]
then	rcfile=$1
fi

E=1
EC=1
while [ "$E" = "0" -o "$E" = "1" ]
do
    doentry;
    E="$?"
    if		[ "$E" = "0" ]
    then	EC=0		## Got at least one success
    fi
    if		[ "$E" = "255" ]
    then	exit 0;
    fi
done
exit $EC;			## 0, at least one success; 1, no entries added
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 755 addcal.usg
	/bin/echo -n '	'; /bin/ls -ld addcal.usg
fi
-- 

Bruce Israel

University of Maryland, Computer Science
{rlgvax,seismo}!umcp-cs!israel (Usenet)    israel@Maryland (Arpanet)