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