xref: /original-bsd/usr.bin/calendar/calendar.c (revision a95f03a8)
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.12 (Berkeley) 05/11/92";
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 == 5 ? 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 + 1;
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 	/* ASSUME THIS SHIT WORKS... %^&%&^%^& */
186 	day = cumdays[month] + day;
187 
188 	/* if today or today + offset days */
189 	if (day >= tp->tm_yday && day <= tp->tm_yday + offset)
190 		return(1);
191 	/* if number of days left in this year + days to event in next year */
192 	if (yrdays - tp->tm_yday + day <= offset)
193 		return(1);
194 	return(0);
195 }
196 
197 getfield(p, endp, flags)
198 	char *p, **endp;
199 	int *flags;
200 {
201 	int val;
202 	char *start, savech;
203 
204 	for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p)
205 		;
206 	if (*p == '*') {			/* `*' is current month */
207 		*flags |= F_ISMONTH;
208 		*endp = p+1;
209 		return(tp->tm_mon + 1);
210 	}
211 	if (isdigit(*p)) {
212 		val = strtol(p, &p, 10);	/* if 0, it's failure */
213 		for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
214 		*endp = p;
215 		return(val);
216 	}
217 	for (start = p; isalpha(*++p););
218 	savech = *p;
219 	*p = '\0';
220 	if (val = getmonth(start))
221 		*flags |= F_ISMONTH;
222 	else if (val = getday(start))
223 		*flags |= F_ISDAY;
224 	else
225 		return(0);
226 	for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
227 	*endp = p;
228 	return(val);
229 }
230 
231 char path[MAXPATHLEN + 1];
232 
233 FILE *
234 opencal()
235 {
236 	int fd, pdes[2];
237 	char *mktemp();
238 
239 	/* open up calendar file as stdin */
240 	if (!freopen("calendar", "r", stdin)) {
241 		if (doall)
242 			return((FILE *)NULL);
243 		(void)fprintf(stderr, "calendar: no calendar file.\n");
244 		exit(1);
245 	}
246 	if (pipe(pdes) < 0)
247 		return(NULL);
248 	switch (vfork()) {
249 	case -1:			/* error */
250 		(void)close(pdes[0]);
251 		(void)close(pdes[1]);
252 		return(NULL);
253 	case 0:
254 		/* child -- stdin already setup, set stdout to pipe input */
255 		if (pdes[1] != STDOUT_FILENO) {
256 			(void)dup2(pdes[1], STDOUT_FILENO);
257 			(void)close(pdes[1]);
258 		}
259 		(void)close(pdes[0]);
260 		execl(_PATH_CPP, "cpp", "-I.", _PATH_INCLUDE, NULL);
261 		_exit(1);
262 	}
263 	/* parent -- set stdin to pipe output */
264 	(void)dup2(pdes[0], STDIN_FILENO);
265 	(void)close(pdes[0]);
266 	(void)close(pdes[1]);
267 
268 	/* not reading all calendar files, just set output to stdout */
269 	if (!doall)
270 		return(stdout);
271 
272 	/* set output to a temporary file, so if no output don't send mail */
273 	(void)sprintf(path, "%s/_calXXXXXX", _PATH_TMP);
274 	if ((fd = mkstemp(path)) < 0)
275 		return(NULL);
276 	return(fdopen(fd, "w+"));
277 }
278 
279 closecal(fp)
280 	FILE *fp;
281 {
282 	struct stat sbuf;
283 	int nread, pdes[2], status;
284 	char buf[1024], *mktemp();
285 
286 	if (!doall)
287 		return;
288 
289 	(void)rewind(fp);
290 	if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
291 		goto done;
292 	if (pipe(pdes) < 0)
293 		goto done;
294 	switch (vfork()) {
295 	case -1:			/* error */
296 		(void)close(pdes[0]);
297 		(void)close(pdes[1]);
298 		goto done;
299 	case 0:
300 		/* child -- set stdin to pipe output */
301 		if (pdes[0] != STDIN_FILENO) {
302 			(void)dup2(pdes[0], STDIN_FILENO);
303 			(void)close(pdes[0]);
304 		}
305 		(void)close(pdes[1]);
306 		execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
307 		    "\"Reminder Service\"", "-f", "root", NULL);
308 		(void)fprintf(stderr, "calendar: %s: %s.\n",
309 		    _PATH_SENDMAIL, strerror(errno));
310 		_exit(1);
311 	}
312 	/* parent -- write to pipe input */
313 	(void)close(pdes[0]);
314 
315 	header[1].iov_base = header[3].iov_base = pw->pw_name;
316 	header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
317 	writev(pdes[1], header, 7);
318 	while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
319 		(void)write(pdes[1], buf, nread);
320 	(void)close(pdes[1]);
321 done:	(void)fclose(fp);
322 	(void)unlink(path);
323 	while (wait(&status) >= 0);
324 }
325 
326 static char *months[] = {
327 	"jan", "feb", "mar", "apr", "may", "jun",
328 	"jul", "aug", "sep", "oct", "nov", "dec", NULL,
329 };
330 getmonth(s)
331 	register char *s;
332 {
333 	register char **p;
334 
335 	for (p = months; *p; ++p)
336 		if (!strncasecmp(s, *p, 3))
337 			return((p - months) + 1);
338 	return(0);
339 }
340 
341 static char *days[] = {
342 	"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
343 };
344 getday(s)
345 	register char *s;
346 {
347 	register char **p;
348 
349 	for (p = days; *p; ++p)
350 		if (!strncasecmp(s, *p, 3))
351 			return((p - days) + 1);
352 	return(0);
353 }
354 
355 usage()
356 {
357 	(void)fprintf(stderr, "usage: calendar [-a]\n");
358 	exit(1);
359 }
360