calendar.c revision 11334
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
351590Srgrimesstatic char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381590Srgrimes#endif /* not lint */
391590Srgrimes
401590Srgrimes#ifndef lint
411590Srgrimesstatic char sccsid[] = "@(#)calendar.c	8.3 (Berkeley) 3/25/94";
421590Srgrimes#endif /* not lint */
431590Srgrimes
441590Srgrimes#include <sys/param.h>
451590Srgrimes#include <sys/time.h>
461590Srgrimes#include <sys/stat.h>
471590Srgrimes#include <sys/uio.h>
481590Srgrimes#include <sys/wait.h>
491590Srgrimes
501590Srgrimes#include <ctype.h>
511590Srgrimes#include <err.h>
521590Srgrimes#include <errno.h>
531590Srgrimes#include <fcntl.h>
541590Srgrimes#include <pwd.h>
551590Srgrimes#include <stdio.h>
561590Srgrimes#include <stdlib.h>
571590Srgrimes#include <string.h>
581590Srgrimes#include <unistd.h>
591590Srgrimes
601590Srgrimes#include "pathnames.h"
611590Srgrimes
621590Srgrimesstruct passwd *pw;
631590Srgrimesint doall;
641590Srgrimes
651590Srgrimesvoid	 cal __P((void));
661590Srgrimesvoid	 closecal __P((FILE *));
671590Srgrimesint	 getday __P((char *));
681590Srgrimesint	 getfield __P((char *, char **, int *));
691590Srgrimesint	 getmonth __P((char *));
701590Srgrimesint	 isnow __P((char *));
711590SrgrimesFILE	*opencal __P((void));
721590Srgrimesvoid	 settime __P((void));
731590Srgrimesvoid	 usage __P((void));
749987Swollman#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
751590Srgrimes
761590Srgrimesint
771590Srgrimesmain(argc, argv)
781590Srgrimes	int argc;
791590Srgrimes	char *argv[];
801590Srgrimes{
811590Srgrimes	extern int optind;
821590Srgrimes	int ch;
831590Srgrimes
841590Srgrimes	while ((ch = getopt(argc, argv, "-a")) != EOF)
851590Srgrimes		switch (ch) {
861590Srgrimes		case '-':		/* backward contemptible */
871590Srgrimes		case 'a':
881590Srgrimes			if (getuid()) {
891590Srgrimes				errno = EPERM;
901590Srgrimes				err(1, NULL);
911590Srgrimes			}
921590Srgrimes			doall = 1;
931590Srgrimes			break;
941590Srgrimes		case '?':
951590Srgrimes		default:
961590Srgrimes			usage();
971590Srgrimes		}
981590Srgrimes	argc -= optind;
991590Srgrimes	argv += optind;
1001590Srgrimes
1011590Srgrimes	if (argc)
1021590Srgrimes		usage();
1031590Srgrimes
1041590Srgrimes	settime();
1051590Srgrimes	if (doall)
1061590Srgrimes		while ((pw = getpwent()) != NULL) {
1071590Srgrimes			(void)setegid(pw->pw_gid);
1081590Srgrimes			(void)seteuid(pw->pw_uid);
1091590Srgrimes			if (!chdir(pw->pw_dir))
1101590Srgrimes				cal();
1111590Srgrimes			(void)seteuid(0);
1121590Srgrimes		}
11311334Sache	else
1141590Srgrimes		cal();
1151590Srgrimes	exit(0);
1161590Srgrimes}
1171590Srgrimes
1181590Srgrimesvoid
1191590Srgrimescal()
1201590Srgrimes{
1211590Srgrimes	register int printing;
1221590Srgrimes	register char *p;
1231590Srgrimes	FILE *fp;
1241590Srgrimes	int ch;
1251590Srgrimes	char buf[2048 + 1];
1261590Srgrimes
1271590Srgrimes	if ((fp = opencal()) == NULL)
1281590Srgrimes		return;
1291590Srgrimes	for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
1301590Srgrimes		if ((p = strchr(buf, '\n')) != NULL)
1311590Srgrimes			*p = '\0';
1321590Srgrimes		else
1331590Srgrimes			while ((ch = getchar()) != '\n' && ch != EOF);
1341590Srgrimes		if (buf[0] == '\0')
1351590Srgrimes			continue;
1361590Srgrimes		if (buf[0] != '\t')
1371590Srgrimes			printing = isnow(buf) ? 1 : 0;
1381590Srgrimes		if (printing)
1391590Srgrimes			(void)fprintf(fp, "%s\n", buf);
1401590Srgrimes	}
1411590Srgrimes	closecal(fp);
1421590Srgrimes}
1431590Srgrimes
1441590Srgrimesstruct iovec header[] = {
1451590Srgrimes	"From: ", 6,
1461590Srgrimes	NULL, 0,
1471590Srgrimes	" (Reminder Service)\nTo: ", 24,
1481590Srgrimes	NULL, 0,
1491590Srgrimes	"\nSubject: ", 10,
1501590Srgrimes	NULL, 0,
1511590Srgrimes	"'s Calendar\nPrecedence: bulk\n\n",  30,
1521590Srgrimes};
1531590Srgrimes
1541590Srgrimes/* 1-based month, 0-based days, cumulative */
1551590Srgrimesint daytab[][14] = {
1561590Srgrimes	0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364,
1571590Srgrimes	0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365,
1581590Srgrimes};
1591590Srgrimesstruct tm *tp;
1601590Srgrimesint *cumdays, offset, yrdays;
1611590Srgrimeschar dayname[10];
1621590Srgrimes
1631590Srgrimesvoid
1641590Srgrimessettime()
1651590Srgrimes{
1661590Srgrimes	time_t now;
1671590Srgrimes
1681590Srgrimes	(void)time(&now);
1691590Srgrimes	tp = localtime(&now);
1701590Srgrimes	if (isleap(tp->tm_year + 1900)) {
1719987Swollman		yrdays = 366;
1721590Srgrimes		cumdays = daytab[1];
1731590Srgrimes	} else {
1749987Swollman		yrdays = 365;
1751590Srgrimes		cumdays = daytab[0];
1761590Srgrimes	}
1771590Srgrimes	/* Friday displays Monday's events */
1781590Srgrimes	offset = tp->tm_wday == 5 ? 3 : 1;
1791590Srgrimes	header[5].iov_base = dayname;
1801590Srgrimes	header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
1811590Srgrimes}
1821590Srgrimes
1831590Srgrimes/*
1841590Srgrimes * Possible date formats include any combination of:
1851590Srgrimes *	3-charmonth			(January, Jan, Jan)
1861590Srgrimes *	3-charweekday			(Friday, Monday, mon.)
1871590Srgrimes *	numeric month or day		(1, 2, 04)
1881590Srgrimes *
1891590Srgrimes * Any character may separate them, or they may not be separated.  Any line,
1901590Srgrimes * following a line that is matched, that starts with "whitespace", is shown
1911590Srgrimes * along with the matched line.
1921590Srgrimes */
1931590Srgrimesint
1941590Srgrimesisnow(endp)
1951590Srgrimes	char *endp;
1961590Srgrimes{
1971590Srgrimes	int day, flags, month, v1, v2;
1981590Srgrimes
1991590Srgrimes#define	F_ISMONTH	0x01
2001590Srgrimes#define	F_ISDAY		0x02
2011590Srgrimes	flags = 0;
2021590Srgrimes	/* didn't recognize anything, skip it */
2031590Srgrimes	if (!(v1 = getfield(endp, &endp, &flags)))
2041590Srgrimes		return (0);
2051590Srgrimes	if (flags & F_ISDAY || v1 > 12) {
2061590Srgrimes		/* found a day */
2071590Srgrimes		day = v1;
2081590Srgrimes		/* if no recognizable month, assume just a day alone */
2091590Srgrimes		if (!(month = getfield(endp, &endp, &flags)))
2101590Srgrimes			month = tp->tm_mon + 1;
2111590Srgrimes	} else if (flags & F_ISMONTH) {
2121590Srgrimes		month = v1;
2131590Srgrimes		/* if no recognizable day, assume the first */
2141590Srgrimes		if (!(day = getfield(endp, &endp, &flags)))
2151590Srgrimes			day = 1;
2161590Srgrimes	} else {
2171590Srgrimes		v2 = getfield(endp, &endp, &flags);
2181590Srgrimes		if (flags & F_ISMONTH) {
2191590Srgrimes			day = v1;
2201590Srgrimes			month = v2;
2211590Srgrimes		} else {
2221590Srgrimes			/* F_ISDAY set, v2 > 12, or no way to tell */
2231590Srgrimes			month = v1;
2241590Srgrimes			/* if no recognizable day, assume the first */
2251590Srgrimes			day = v2 ? v2 : 1;
2261590Srgrimes		}
2271590Srgrimes	}
2281590Srgrimes	if (flags & F_ISDAY)
2291590Srgrimes		day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
2301590Srgrimes	day = cumdays[month] + day;
2311590Srgrimes
2321590Srgrimes	/* if today or today + offset days */
2331590Srgrimes	if (day >= tp->tm_yday && day <= tp->tm_yday + offset)
2341590Srgrimes		return (1);
2351590Srgrimes	/* if number of days left in this year + days to event in next year */
2361590Srgrimes	if (yrdays - tp->tm_yday + day <= offset)
2371590Srgrimes		return (1);
2381590Srgrimes	return (0);
2391590Srgrimes}
2401590Srgrimes
2411590Srgrimesint
2421590Srgrimesgetfield(p, endp, flags)
2431590Srgrimes	char *p, **endp;
2441590Srgrimes	int *flags;
2451590Srgrimes{
2461590Srgrimes	int val;
2471590Srgrimes	char *start, savech;
2481590Srgrimes
2491590Srgrimes	for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
2501590Srgrimes	if (*p == '*') {			/* `*' is current month */
2511590Srgrimes		*flags |= F_ISMONTH;
2521590Srgrimes		*endp = p+1;
2531590Srgrimes		return (tp->tm_mon + 1);
2541590Srgrimes	}
2551590Srgrimes	if (isdigit(*p)) {
2561590Srgrimes		val = strtol(p, &p, 10);	/* if 0, it's failure */
2571590Srgrimes		for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
2581590Srgrimes		*endp = p;
2591590Srgrimes		return (val);
2601590Srgrimes	}
2611590Srgrimes	for (start = p; isalpha(*++p););
2621590Srgrimes	savech = *p;
2631590Srgrimes	*p = '\0';
2641590Srgrimes	if ((val = getmonth(start)) != 0)
2651590Srgrimes		*flags |= F_ISMONTH;
2661590Srgrimes	else if ((val = getday(start)) != 0)
2671590Srgrimes		*flags |= F_ISDAY;
2681590Srgrimes	else {
2691590Srgrimes		*p = savech;
2701590Srgrimes		return (0);
2711590Srgrimes	}
2721590Srgrimes	for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
2731590Srgrimes	*endp = p;
2741590Srgrimes	return (val);
2751590Srgrimes}
2761590Srgrimes
2771590Srgrimeschar path[MAXPATHLEN + 1];
2781590Srgrimes
2791590SrgrimesFILE *
2801590Srgrimesopencal()
2811590Srgrimes{
2821590Srgrimes	int fd, pdes[2];
2831590Srgrimes
2841590Srgrimes	/* open up calendar file as stdin */
2851590Srgrimes	if (!freopen("calendar", "r", stdin)) {
2861590Srgrimes		if (doall)
2871590Srgrimes			return (NULL);
2881590Srgrimes		errx(1, "no calendar file.");
2891590Srgrimes	}
2908874Srgrimes	if (pipe(pdes) < 0)
2911590Srgrimes		return (NULL);
2921590Srgrimes	switch (vfork()) {
2931590Srgrimes	case -1:			/* error */
2941590Srgrimes		(void)close(pdes[0]);
2951590Srgrimes		(void)close(pdes[1]);
2961590Srgrimes		return (NULL);
2971590Srgrimes	case 0:
2981590Srgrimes		/* child -- stdin already setup, set stdout to pipe input */
2991590Srgrimes		if (pdes[1] != STDOUT_FILENO) {
3001590Srgrimes			(void)dup2(pdes[1], STDOUT_FILENO);
3011590Srgrimes			(void)close(pdes[1]);
3021590Srgrimes		}
3031590Srgrimes		(void)close(pdes[0]);
3045311Sache		execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL);
3051590Srgrimes		(void)fprintf(stderr,
3061590Srgrimes		    "calendar: execl: %s: %s.\n", _PATH_CPP, strerror(errno));
3071590Srgrimes		_exit(1);
3081590Srgrimes	}
3091590Srgrimes	/* parent -- set stdin to pipe output */
3101590Srgrimes	(void)dup2(pdes[0], STDIN_FILENO);
3111590Srgrimes	(void)close(pdes[0]);
3121590Srgrimes	(void)close(pdes[1]);
3131590Srgrimes
3141590Srgrimes	/* not reading all calendar files, just set output to stdout */
3151590Srgrimes	if (!doall)
3161590Srgrimes		return (stdout);
3171590Srgrimes
3181590Srgrimes	/* set output to a temporary file, so if no output don't send mail */
3191590Srgrimes	(void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);
3201590Srgrimes	if ((fd = mkstemp(path)) < 0)
3211590Srgrimes		return (NULL);
3221590Srgrimes	return (fdopen(fd, "w+"));
3231590Srgrimes}
3241590Srgrimes
3251590Srgrimesvoid
3261590Srgrimesclosecal(fp)
3271590Srgrimes	FILE *fp;
3281590Srgrimes{
3291590Srgrimes	struct stat sbuf;
3301590Srgrimes	int nread, pdes[2], status;
3311590Srgrimes	char buf[1024];
3321590Srgrimes
3331590Srgrimes	if (!doall)
3341590Srgrimes		return;
3351590Srgrimes
3361590Srgrimes	(void)rewind(fp);
3371590Srgrimes	if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
3381590Srgrimes		goto done;
3398874Srgrimes	if (pipe(pdes) < 0)
3401590Srgrimes		goto done;
3411590Srgrimes	switch (vfork()) {
3421590Srgrimes	case -1:			/* error */
3431590Srgrimes		(void)close(pdes[0]);
3441590Srgrimes		(void)close(pdes[1]);
3451590Srgrimes		goto done;
3468874Srgrimes	case 0:
3471590Srgrimes		/* child -- set stdin to pipe output */
3481590Srgrimes		if (pdes[0] != STDIN_FILENO) {
3491590Srgrimes			(void)dup2(pdes[0], STDIN_FILENO);
3501590Srgrimes			(void)close(pdes[0]);
3511590Srgrimes		}
3521590Srgrimes		(void)close(pdes[1]);
3531590Srgrimes		execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
3541590Srgrimes		    "\"Reminder Service\"", "-f", "root", NULL);
3551590Srgrimes		(void)fprintf(stderr,
3561590Srgrimes		    "calendar: %s: %s.\n", _PATH_SENDMAIL, strerror(errno));
3571590Srgrimes		_exit(1);
3581590Srgrimes	}
3591590Srgrimes	/* parent -- write to pipe input */
3601590Srgrimes	(void)close(pdes[0]);
3611590Srgrimes
3621590Srgrimes	header[1].iov_base = header[3].iov_base = pw->pw_name;
3631590Srgrimes	header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
3641590Srgrimes	writev(pdes[1], header, 7);
3651590Srgrimes	while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
3661590Srgrimes		(void)write(pdes[1], buf, nread);
3671590Srgrimes	(void)close(pdes[1]);
3681590Srgrimesdone:	(void)fclose(fp);
3691590Srgrimes	(void)unlink(path);
3701590Srgrimes	while (wait(&status) >= 0);
3711590Srgrimes}
3721590Srgrimes
3731590Srgrimesstatic char *months[] = {
3741590Srgrimes	"jan", "feb", "mar", "apr", "may", "jun",
3751590Srgrimes	"jul", "aug", "sep", "oct", "nov", "dec", NULL,
3761590Srgrimes};
3771590Srgrimes
3781590Srgrimesint
3791590Srgrimesgetmonth(s)
3801590Srgrimes	register char *s;
3811590Srgrimes{
3821590Srgrimes	register char **p;
3831590Srgrimes
3841590Srgrimes	for (p = months; *p; ++p)
3851590Srgrimes		if (!strncasecmp(s, *p, 3))
3861590Srgrimes			return ((p - months) + 1);
3871590Srgrimes	return (0);
3881590Srgrimes}
3891590Srgrimes
3901590Srgrimesstatic char *days[] = {
3911590Srgrimes	"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
3921590Srgrimes};
3931590Srgrimes
3941590Srgrimesint
3951590Srgrimesgetday(s)
3961590Srgrimes	register char *s;
3971590Srgrimes{
3981590Srgrimes	register char **p;
3991590Srgrimes
4001590Srgrimes	for (p = days; *p; ++p)
4011590Srgrimes		if (!strncasecmp(s, *p, 3))
4021590Srgrimes			return ((p - days) + 1);
4031590Srgrimes	return (0);
4041590Srgrimes}
4051590Srgrimes
4061590Srgrimesvoid
4071590Srgrimesusage()
4081590Srgrimes{
4091590Srgrimes	(void)fprintf(stderr, "usage: calendar [-a]\n");
4101590Srgrimes	exit(1);
4111590Srgrimes}
412