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