Home | History | Annotate | Line # | Download | only in calendar
calendar.c revision 1.1
      1  1.1  cgd /*
      2  1.1  cgd  * Copyright (c) 1989 The Regents of the University of California.
      3  1.1  cgd  * All rights reserved.
      4  1.1  cgd  *
      5  1.1  cgd  * Redistribution and use in source and binary forms, with or without
      6  1.1  cgd  * modification, are permitted provided that the following conditions
      7  1.1  cgd  * are met:
      8  1.1  cgd  * 1. Redistributions of source code must retain the above copyright
      9  1.1  cgd  *    notice, this list of conditions and the following disclaimer.
     10  1.1  cgd  * 2. Redistributions in binary form must reproduce the above copyright
     11  1.1  cgd  *    notice, this list of conditions and the following disclaimer in the
     12  1.1  cgd  *    documentation and/or other materials provided with the distribution.
     13  1.1  cgd  * 3. All advertising materials mentioning features or use of this software
     14  1.1  cgd  *    must display the following acknowledgement:
     15  1.1  cgd  *	This product includes software developed by the University of
     16  1.1  cgd  *	California, Berkeley and its contributors.
     17  1.1  cgd  * 4. Neither the name of the University nor the names of its contributors
     18  1.1  cgd  *    may be used to endorse or promote products derived from this software
     19  1.1  cgd  *    without specific prior written permission.
     20  1.1  cgd  *
     21  1.1  cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  1.1  cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  1.1  cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  1.1  cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  1.1  cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  1.1  cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  1.1  cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  1.1  cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  1.1  cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  1.1  cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  1.1  cgd  * SUCH DAMAGE.
     32  1.1  cgd  */
     33  1.1  cgd 
     34  1.1  cgd #ifndef lint
     35  1.1  cgd char copyright[] =
     36  1.1  cgd "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
     37  1.1  cgd  All rights reserved.\n";
     38  1.1  cgd #endif /* not lint */
     39  1.1  cgd 
     40  1.1  cgd #ifndef lint
     41  1.1  cgd static char sccsid[] = "@(#)calendar.c	4.11 (Berkeley) 10/12/90";
     42  1.1  cgd #endif /* not lint */
     43  1.1  cgd 
     44  1.1  cgd #include <sys/param.h>
     45  1.1  cgd #include <sys/time.h>
     46  1.1  cgd #include <sys/stat.h>
     47  1.1  cgd #include <sys/file.h>
     48  1.1  cgd #include <sys/uio.h>
     49  1.1  cgd #include <pwd.h>
     50  1.1  cgd #include <errno.h>
     51  1.1  cgd #include <tzfile.h>
     52  1.1  cgd #include <stdio.h>
     53  1.1  cgd #include <ctype.h>
     54  1.1  cgd #include <unistd.h>
     55  1.1  cgd #include <string.h>
     56  1.1  cgd #include "pathnames.h"
     57  1.1  cgd 
     58  1.1  cgd extern int errno;
     59  1.1  cgd struct passwd *pw;
     60  1.1  cgd int doall;
     61  1.1  cgd 
     62  1.1  cgd main(argc, argv)
     63  1.1  cgd 	int argc;
     64  1.1  cgd 	char **argv;
     65  1.1  cgd {
     66  1.1  cgd 	extern int optind;
     67  1.1  cgd 	int ch;
     68  1.1  cgd 
     69  1.1  cgd 	while ((ch = getopt(argc, argv, "-a")) != EOF)
     70  1.1  cgd 		switch(ch) {
     71  1.1  cgd 		case '-':		/* backward contemptible */
     72  1.1  cgd 		case 'a':
     73  1.1  cgd 			if (getuid()) {
     74  1.1  cgd 				(void)fprintf(stderr,
     75  1.1  cgd 				    "calendar: %s\n", strerror(EPERM));
     76  1.1  cgd 				exit(1);
     77  1.1  cgd 			}
     78  1.1  cgd 			doall = 1;
     79  1.1  cgd 			break;
     80  1.1  cgd 		case '?':
     81  1.1  cgd 		default:
     82  1.1  cgd 			usage();
     83  1.1  cgd 		}
     84  1.1  cgd 	argc -= optind;
     85  1.1  cgd 	argv += optind;
     86  1.1  cgd 
     87  1.1  cgd 	if (argc)
     88  1.1  cgd 		usage();
     89  1.1  cgd 
     90  1.1  cgd 	settime();
     91  1.1  cgd 	if (doall)
     92  1.1  cgd 		while (pw = getpwent()) {
     93  1.1  cgd 			(void)setegid(pw->pw_gid);
     94  1.1  cgd 			(void)seteuid(pw->pw_uid);
     95  1.1  cgd 			if (!chdir(pw->pw_dir))
     96  1.1  cgd 				cal();
     97  1.1  cgd 			(void)seteuid(0);
     98  1.1  cgd 		}
     99  1.1  cgd 	else
    100  1.1  cgd 		cal();
    101  1.1  cgd 	exit(0);
    102  1.1  cgd }
    103  1.1  cgd 
    104  1.1  cgd cal()
    105  1.1  cgd {
    106  1.1  cgd 	register int printing;
    107  1.1  cgd 	register char *p;
    108  1.1  cgd 	FILE *fp, *opencal();
    109  1.1  cgd 	int ch;
    110  1.1  cgd 	char buf[2048 + 1];
    111  1.1  cgd 
    112  1.1  cgd 	if (!(fp = opencal()))
    113  1.1  cgd 		return;
    114  1.1  cgd 	for (printing = 0; fgets(buf, sizeof(buf), stdin);) {
    115  1.1  cgd 		if (p = index(buf, '\n'))
    116  1.1  cgd 			*p = '\0';
    117  1.1  cgd 		else
    118  1.1  cgd 			while ((ch = getchar()) != '\n' && ch != EOF);
    119  1.1  cgd 		if (buf[0] == '\0')
    120  1.1  cgd 			continue;
    121  1.1  cgd 		if (buf[0] != '\t')
    122  1.1  cgd 			printing = isnow(buf) ? 1 : 0;
    123  1.1  cgd 		if (printing)
    124  1.1  cgd 			(void)fprintf(fp, "%s\n", buf);
    125  1.1  cgd 	}
    126  1.1  cgd 	closecal(fp);
    127  1.1  cgd }
    128  1.1  cgd 
    129  1.1  cgd struct iovec header[] = {
    130  1.1  cgd 	"From: ", 6,
    131  1.1  cgd 	NULL, 0,
    132  1.1  cgd 	" (Reminder Service)\nTo: ", 24,
    133  1.1  cgd 	NULL, 0,
    134  1.1  cgd 	"\nSubject: ", 10,
    135  1.1  cgd 	NULL, 0,
    136  1.1  cgd 	"'s Calendar\nPrecedence: bulk\n\n",  30,
    137  1.1  cgd };
    138  1.1  cgd 
    139  1.1  cgd /* 1-based month, 0-based days, cumulative */
    140  1.1  cgd int daytab[][14] = {
    141  1.1  cgd 	0, 0, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364,
    142  1.1  cgd 	0, 0, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365,
    143  1.1  cgd };
    144  1.1  cgd struct tm *tp;
    145  1.1  cgd int *cumdays, offset, yrdays;
    146  1.1  cgd char dayname[10];
    147  1.1  cgd 
    148  1.1  cgd settime()
    149  1.1  cgd {
    150  1.1  cgd 	time_t now, time();
    151  1.1  cgd 
    152  1.1  cgd 	(void)time(&now);
    153  1.1  cgd 	tp = localtime(&now);
    154  1.1  cgd 	if (isleap(tp->tm_year + 1900)) {
    155  1.1  cgd 		yrdays = DAYSPERLYEAR;
    156  1.1  cgd 		cumdays = daytab[1];
    157  1.1  cgd 	} else {
    158  1.1  cgd 		yrdays = DAYSPERNYEAR;
    159  1.1  cgd 		cumdays = daytab[0];
    160  1.1  cgd 	}
    161  1.1  cgd 	/* Friday displays Monday's events */
    162  1.1  cgd 	offset = tp->tm_wday == 5 ? 3 : 1;
    163  1.1  cgd 	header[5].iov_base = dayname;
    164  1.1  cgd 	header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
    165  1.1  cgd }
    166  1.1  cgd 
    167  1.1  cgd /*
    168  1.1  cgd  * Possible date formats include any combination of:
    169  1.1  cgd  *	3-charmonth			(January, Jan, Jan)
    170  1.1  cgd  *	3-charweekday			(Friday, Monday, mon.)
    171  1.1  cgd  *	numeric month or day		(1, 2, 04)
    172  1.1  cgd  *
    173  1.1  cgd  * Any character may separate them, or they may not be separated.  Any line,
    174  1.1  cgd  * following a line that is matched, that starts with "whitespace", is shown
    175  1.1  cgd  * along with the matched line.
    176  1.1  cgd  */
    177  1.1  cgd isnow(endp)
    178  1.1  cgd 	char *endp;
    179  1.1  cgd {
    180  1.1  cgd 	int day, flags, month, v1, v2;
    181  1.1  cgd 
    182  1.1  cgd #define	F_ISMONTH	0x01
    183  1.1  cgd #define	F_ISDAY		0x02
    184  1.1  cgd 	flags = 0;
    185  1.1  cgd 	/* didn't recognize anything, skip it */
    186  1.1  cgd 	if (!(v1 = getfield(endp, &endp, &flags)))
    187  1.1  cgd 		return(0);
    188  1.1  cgd 	if (flags&F_ISDAY || v1 > 12) {
    189  1.1  cgd 		/* found a day */
    190  1.1  cgd 		day = v1;
    191  1.1  cgd 		/* if no recognizable month, assume just a day alone */
    192  1.1  cgd 		if (!(month = getfield(endp, &endp, &flags)))
    193  1.1  cgd 			month = tp->tm_mon;
    194  1.1  cgd 	} else if (flags&F_ISMONTH) {
    195  1.1  cgd 		month = v1;
    196  1.1  cgd 		/* if no recognizable day, assume the first */
    197  1.1  cgd 		if (!(day = getfield(endp, &endp, &flags)))
    198  1.1  cgd 			day = 1;
    199  1.1  cgd 	} else {
    200  1.1  cgd 		v2 = getfield(endp, &endp, &flags);
    201  1.1  cgd 		if (flags&F_ISMONTH) {
    202  1.1  cgd 			day = v1;
    203  1.1  cgd 			month = v2;
    204  1.1  cgd 		} else {
    205  1.1  cgd 			/* F_ISDAY set, v2 > 12, or no way to tell */
    206  1.1  cgd 			month = v1;
    207  1.1  cgd 			/* if no recognizable day, assume the first */
    208  1.1  cgd 			day = v2 ? v2 : 1;
    209  1.1  cgd 		}
    210  1.1  cgd 	}
    211  1.1  cgd 	day = cumdays[month] + day;
    212  1.1  cgd 
    213  1.1  cgd 	/* if today or today + offset days */
    214  1.1  cgd 	if (day >= tp->tm_yday && day <= tp->tm_yday + offset)
    215  1.1  cgd 		return(1);
    216  1.1  cgd 	/* if number of days left in this year + days to event in next year */
    217  1.1  cgd 	if (yrdays - tp->tm_yday + day <= offset)
    218  1.1  cgd 		return(1);
    219  1.1  cgd 	return(0);
    220  1.1  cgd }
    221  1.1  cgd 
    222  1.1  cgd getfield(p, endp, flags)
    223  1.1  cgd 	char *p, **endp;
    224  1.1  cgd 	int *flags;
    225  1.1  cgd {
    226  1.1  cgd 	int val;
    227  1.1  cgd 	char *start, savech;
    228  1.1  cgd 
    229  1.1  cgd 	if (*p == '*') {			/* `*' is current month */
    230  1.1  cgd 		*flags |= F_ISMONTH;
    231  1.1  cgd 		return(tp->tm_mon);
    232  1.1  cgd 	}
    233  1.1  cgd 	if (isdigit(*p)) {
    234  1.1  cgd 		val = strtol(p, &p, 10);	/* if 0, it's failure */
    235  1.1  cgd 		for (; !isdigit(*p) && !isalpha(*p); ++p);
    236  1.1  cgd 		*endp = p;
    237  1.1  cgd 		return(val);
    238  1.1  cgd 	}
    239  1.1  cgd 	for (start = p; isalpha(*++p););
    240  1.1  cgd 	savech = *p;
    241  1.1  cgd 	*p = '\0';
    242  1.1  cgd 	if (val = getmonth(start))
    243  1.1  cgd 		*flags |= F_ISMONTH;
    244  1.1  cgd 	else if (val = getday(start))
    245  1.1  cgd 		*flags |= F_ISDAY;
    246  1.1  cgd 	else
    247  1.1  cgd 		return(0);
    248  1.1  cgd 	for (*p = savech; !isdigit(*p) && !isalpha(*p); ++p);
    249  1.1  cgd 	*endp = p;
    250  1.1  cgd 	return(val);
    251  1.1  cgd }
    252  1.1  cgd 
    253  1.1  cgd char path[MAXPATHLEN + 1];
    254  1.1  cgd 
    255  1.1  cgd FILE *
    256  1.1  cgd opencal()
    257  1.1  cgd {
    258  1.1  cgd 	int fd, pdes[2];
    259  1.1  cgd 	char *mktemp();
    260  1.1  cgd 
    261  1.1  cgd 	/* open up calendar file as stdin */
    262  1.1  cgd 	if (!freopen("calendar", "r", stdin)) {
    263  1.1  cgd 		if (doall)
    264  1.1  cgd 			return((FILE *)NULL);
    265  1.1  cgd 		(void)fprintf(stderr, "calendar: no calendar file.\n");
    266  1.1  cgd 		exit(1);
    267  1.1  cgd 	}
    268  1.1  cgd 	if (pipe(pdes) < 0)
    269  1.1  cgd 		return(NULL);
    270  1.1  cgd 	switch (vfork()) {
    271  1.1  cgd 	case -1:			/* error */
    272  1.1  cgd 		(void)close(pdes[0]);
    273  1.1  cgd 		(void)close(pdes[1]);
    274  1.1  cgd 		return(NULL);
    275  1.1  cgd 	case 0:
    276  1.1  cgd 		/* child -- stdin already setup, set stdout to pipe input */
    277  1.1  cgd 		if (pdes[1] != STDOUT_FILENO) {
    278  1.1  cgd 			(void)dup2(pdes[1], STDOUT_FILENO);
    279  1.1  cgd 			(void)close(pdes[1]);
    280  1.1  cgd 		}
    281  1.1  cgd 		(void)close(pdes[0]);
    282  1.1  cgd 		execl(_PATH_CPP, "cpp", "-I.", _PATH_INCLUDE, NULL);
    283  1.1  cgd 		_exit(1);
    284  1.1  cgd 	}
    285  1.1  cgd 	/* parent -- set stdin to pipe output */
    286  1.1  cgd 	(void)dup2(pdes[0], STDIN_FILENO);
    287  1.1  cgd 	(void)close(pdes[0]);
    288  1.1  cgd 	(void)close(pdes[1]);
    289  1.1  cgd 
    290  1.1  cgd 	/* not reading all calendar files, just set output to stdout */
    291  1.1  cgd 	if (!doall)
    292  1.1  cgd 		return(stdout);
    293  1.1  cgd 
    294  1.1  cgd 	/* set output to a temporary file, so if no output don't send mail */
    295  1.1  cgd 	(void)sprintf(path, "%s/_calXXXXXX", _PATH_TMP);
    296  1.1  cgd 	if ((fd = mkstemp(path)) < 0)
    297  1.1  cgd 		return(NULL);
    298  1.1  cgd 	return(fdopen(fd, "w+"));
    299  1.1  cgd }
    300  1.1  cgd 
    301  1.1  cgd closecal(fp)
    302  1.1  cgd 	FILE *fp;
    303  1.1  cgd {
    304  1.1  cgd 	struct stat sbuf;
    305  1.1  cgd 	int nread, pdes[2], status;
    306  1.1  cgd 	char buf[1024], *mktemp();
    307  1.1  cgd 
    308  1.1  cgd 	if (!doall)
    309  1.1  cgd 		return;
    310  1.1  cgd 
    311  1.1  cgd 	(void)rewind(fp);
    312  1.1  cgd 	if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
    313  1.1  cgd 		goto done;
    314  1.1  cgd 	if (pipe(pdes) < 0)
    315  1.1  cgd 		goto done;
    316  1.1  cgd 	switch (vfork()) {
    317  1.1  cgd 	case -1:			/* error */
    318  1.1  cgd 		(void)close(pdes[0]);
    319  1.1  cgd 		(void)close(pdes[1]);
    320  1.1  cgd 		goto done;
    321  1.1  cgd 	case 0:
    322  1.1  cgd 		/* child -- set stdin to pipe output */
    323  1.1  cgd 		if (pdes[0] != STDIN_FILENO) {
    324  1.1  cgd 			(void)dup2(pdes[0], STDIN_FILENO);
    325  1.1  cgd 			(void)close(pdes[0]);
    326  1.1  cgd 		}
    327  1.1  cgd 		(void)close(pdes[1]);
    328  1.1  cgd 		execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
    329  1.1  cgd 		    "\"Reminder Service\"", "-f", "root", NULL);
    330  1.1  cgd 		(void)fprintf(stderr, "calendar: %s: %s.\n",
    331  1.1  cgd 		    _PATH_SENDMAIL, strerror(errno));
    332  1.1  cgd 		_exit(1);
    333  1.1  cgd 	}
    334  1.1  cgd 	/* parent -- write to pipe input */
    335  1.1  cgd 	(void)close(pdes[0]);
    336  1.1  cgd 
    337  1.1  cgd 	header[1].iov_base = header[3].iov_base = pw->pw_name;
    338  1.1  cgd 	header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
    339  1.1  cgd 	writev(pdes[1], header, 7);
    340  1.1  cgd 	while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
    341  1.1  cgd 		(void)write(pdes[1], buf, nread);
    342  1.1  cgd 	(void)close(pdes[1]);
    343  1.1  cgd done:	(void)fclose(fp);
    344  1.1  cgd 	(void)unlink(path);
    345  1.1  cgd 	while (wait(&status) >= 0);
    346  1.1  cgd }
    347  1.1  cgd 
    348  1.1  cgd static char *months[] = {
    349  1.1  cgd 	"jan", "feb", "mar", "apr", "may", "jun",
    350  1.1  cgd 	"jul", "aug", "sep", "oct", "nov", "dec", NULL,
    351  1.1  cgd };
    352  1.1  cgd getmonth(s)
    353  1.1  cgd 	register char *s;
    354  1.1  cgd {
    355  1.1  cgd 	register char **p;
    356  1.1  cgd 
    357  1.1  cgd 	for (p = months; *p; ++p)
    358  1.1  cgd 		if (!strncasecmp(s, *p, 3))
    359  1.1  cgd 			return((p - months) + 1);
    360  1.1  cgd 	return(0);
    361  1.1  cgd }
    362  1.1  cgd 
    363  1.1  cgd static char *days[] = {
    364  1.1  cgd 	"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
    365  1.1  cgd };
    366  1.1  cgd getday(s)
    367  1.1  cgd 	register char *s;
    368  1.1  cgd {
    369  1.1  cgd 	register char **p;
    370  1.1  cgd 
    371  1.1  cgd 	for (p = days; *p; ++p)
    372  1.1  cgd 		if (!strncasecmp(s, *p, 3))
    373  1.1  cgd 			return((p - days) + 1);
    374  1.1  cgd 	return(0);
    375  1.1  cgd }
    376  1.1  cgd 
    377  1.1  cgd usage()
    378  1.1  cgd {
    379  1.1  cgd 	(void)fprintf(stderr, "usage: calendar [-a]\n");
    380  1.1  cgd 	exit(1);
    381  1.1  cgd }
    382