This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / calendar / calendar.c
CommitLineData
15637ed4
RG
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
35char 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
41static 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
58extern int errno;
59struct passwd *pw;
60int doall;
61
62main(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
104cal()
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
129struct 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 */
140int 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};
144struct tm *tp;
145int *cumdays, offset, yrdays;
146char dayname[10];
147
148settime()
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 */
177isnow(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
222getfield(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
253char path[MAXPATHLEN + 1];
254
255FILE *
256opencal()
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
301closecal(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]);
343done: (void)fclose(fp);
344 (void)unlink(path);
345 while (wait(&status) >= 0);
346}
347
348static char *months[] = {
349 "jan", "feb", "mar", "apr", "may", "jun",
350 "jul", "aug", "sep", "oct", "nov", "dec", NULL,
351};
352getmonth(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
363static char *days[] = {
364 "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
365};
366getday(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
377usage()
378{
379 (void)fprintf(stderr, "usage: calendar [-a]\n");
380 exit(1);
381}