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