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