Annotation of src/usr.bin/calendar/io.c, Revision 1.33
1.33 ! deraadt 1: /* $OpenBSD: io.c,v 1.32 2005/11/24 19:36:10 moritz Exp $ */
1.1 millert 2:
3: /*
4: * Copyright (c) 1989, 1993, 1994
5: * The Regents of the University of California. All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
1.24 millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 millert 16: * may be used to endorse or promote products derived from this software
17: * without specific prior written permission.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29: * SUCH DAMAGE.
30: */
31:
32: #ifndef lint
33: static const char copyright[] =
34: "@(#) Copyright (c) 1989, 1993\n\
35: The Regents of the University of California. All rights reserved.\n";
36: #endif /* not lint */
37:
38: #ifndef lint
39: #if 0
40: static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94";
41: #else
1.33 ! deraadt 42: static const char rcsid[] = "$OpenBSD: io.c,v 1.32 2005/11/24 19:36:10 moritz Exp $";
1.1 millert 43: #endif
44: #endif /* not lint */
45:
46: #include <sys/param.h>
47: #include <sys/stat.h>
48: #include <sys/time.h>
49: #include <sys/types.h>
50: #include <sys/uio.h>
51: #include <sys/wait.h>
52:
53: #include <ctype.h>
54: #include <err.h>
55: #include <errno.h>
1.14 pjanzen 56: #include <fcntl.h>
1.1 millert 57: #include <locale.h>
58: #include <pwd.h>
59: #include <stdio.h>
60: #include <stdlib.h>
61: #include <string.h>
1.4 deraadt 62: #include <tzfile.h>
1.1 millert 63: #include <unistd.h>
64:
65: #include "pathnames.h"
66: #include "calendar.h"
67:
68:
69: struct iovec header[] = {
1.31 deraadt 70: { "From: ", 6 },
71: { NULL, 0 },
72: { " (Reminder Service)\nTo: ", 24 },
73: { NULL, 0 },
74: { "\nSubject: ", 10 },
75: { NULL, 0 },
1.33 ! deraadt 76: { "'s Calendar\nPrecedence: bulk\n", 29 },
! 77: { "Auto-Submitted: auto-generated\n\n", 32 },
1.1 millert 78: };
79:
80:
81: void
1.20 vincent 82: cal(void)
1.1 millert 83: {
1.31 deraadt 84: int ch, l, i, bodun = 0, bodun_maybe = 0, var, printing;
1.6 pjanzen 85: struct event *events, *cur_evt, *ev1, *tmp;
1.31 deraadt 86: char buf[2048 + 1], *prefix = NULL, *p;
1.5 pjanzen 87: struct match *m;
1.23 grange 88: size_t nlen;
1.31 deraadt 89: FILE *fp;
1.1 millert 90:
1.5 pjanzen 91: events = NULL;
92: cur_evt = NULL;
1.1 millert 93: if ((fp = opencal()) == NULL)
94: return;
95: for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
96: if ((p = strchr(buf, '\n')) != NULL)
97: *p = '\0';
98: else
99: while ((ch = getchar()) != '\n' && ch != EOF);
1.10 deraadt 100: for (l = strlen(buf); l > 0 && isspace(buf[l - 1]); l--)
1.1 millert 101: ;
102: buf[l] = '\0';
103: if (buf[0] == '\0')
104: continue;
105: if (strncmp(buf, "LANG=", 5) == 0) {
106: (void) setlocale(LC_ALL, buf + 5);
107: setnnames();
1.15 mickey 108: if (!strcmp(buf + 5, "ru_RU.KOI8-R") ||
109: !strcmp(buf + 5, "uk_UA.KOI8-U") ||
110: !strcmp(buf + 5, "by_BY.KOI8-B")) {
111: bodun_maybe++;
1.17 mickey 112: bodun = 0;
113: if (prefix)
114: free(prefix);
115: prefix = NULL;
1.15 mickey 116: } else
117: bodun_maybe = 0;
1.1 millert 118: continue;
1.27 mickey 119: } else if (strncmp(buf, "CALENDAR=", 9) == 0) {
120: char *ep;
121:
122: if (buf[9] == '\0')
123: calendar = 0;
124: else if (!strcasecmp(buf + 9, "julian")) {
125: calendar = JULIAN;
126: errno = 0;
127: julian = strtoul(buf + 14, &ep, 10);
128: if (buf[0] == '\0' || *ep != '\0')
129: julian = 13;
130: if ((errno == ERANGE && julian == ULONG_MAX) ||
131: julian > 14)
132: errx(1, "Julian calendar offset is too large");
133: } else if (!strcasecmp(buf + 9, "gregorian"))
134: calendar = GREGORIAN;
135: else if (!strcasecmp(buf + 9, "lunar"))
136: calendar = LUNAR;
137: } else if (bodun_maybe && strncmp(buf, "BODUN=", 6) == 0) {
1.15 mickey 138: bodun++;
1.17 mickey 139: if (prefix)
140: free(prefix);
141: if ((prefix = strdup(buf + 6)) == NULL)
142: err(1, NULL);
1.27 mickey 143: continue;
1.17 mickey 144: }
1.6 pjanzen 145: /* User defined names for special events */
146: if ((p = strchr(buf, '='))) {
147: for (i = 0; i < NUMEV; i++) {
1.31 deraadt 148: if (strncasecmp(buf, spev[i].name,
149: spev[i].nlen) == 0 &&
150: (p - buf == spev[i].nlen) &&
151: buf[spev[i].nlen + 1]) {
152: p++;
153: if (spev[i].uname != NULL)
154: free(spev[i].uname);
155: if ((spev[i].uname = strdup(p)) == NULL)
156: err(1, NULL);
157: spev[i].ulen = strlen(p);
158: i = NUMEV + 1;
159: }
1.6 pjanzen 160: }
1.31 deraadt 161: if (i > NUMEV)
162: continue;
1.1 millert 163: }
164: if (buf[0] != '\t') {
1.15 mickey 165: printing = (m = isnow(buf, bodun)) ? 1 : 0;
1.6 pjanzen 166: if ((p = strchr(buf, '\t')) == NULL) {
167: printing = 0;
1.1 millert 168: continue;
1.6 pjanzen 169: }
1.5 pjanzen 170: /* Need the following to catch hardwired "variable"
171: * dates */
1.1 millert 172: if (p > buf && p[-1] == '*')
173: var = 1;
1.5 pjanzen 174: else
175: var = 0;
1.1 millert 176: if (printing) {
1.5 pjanzen 177: struct match *foo;
178:
1.6 pjanzen 179: ev1 = NULL;
1.5 pjanzen 180: while (m) {
1.31 deraadt 181: cur_evt = malloc(sizeof(struct event));
182: if (cur_evt == NULL)
183: err(1, NULL);
184:
185: cur_evt->when = m->when;
186: snprintf(cur_evt->print_date,
187: sizeof(cur_evt->print_date), "%s%c",
188: m->print_date, (var + m->var) ? '*' : ' ');
189: if (ev1) {
190: cur_evt->desc = ev1->desc;
191: cur_evt->ldesc = NULL;
192: } else {
193: if (m->bodun && prefix) {
194: if (asprintf(&cur_evt->ldesc,
195: "\t%s %s", prefix,
196: p + 1) == -1)
197: err(1, NULL);
198: } else if ((cur_evt->ldesc =
199: strdup(p)) == NULL)
1.26 tom 200: err(1, NULL);
1.31 deraadt 201: cur_evt->desc = &(cur_evt->ldesc);
202: ev1 = cur_evt;
203: }
204: insert(&events, cur_evt);
205: foo = m;
206: m = m->next;
207: free(foo);
1.5 pjanzen 208: }
1.1 millert 209: }
1.30 deraadt 210: } else if (printing) {
1.32 moritz 211: if (asprintf(&p, "%s\n%s", ev1->ldesc,
1.30 deraadt 212: buf) == -1)
1.11 pjanzen 213: err(1, NULL);
1.32 moritz 214: free(ev1->ldesc);
215: ev1->ldesc = p;
1.5 pjanzen 216: }
217: }
218: tmp = events;
219: while (tmp) {
1.6 pjanzen 220: (void)fprintf(fp, "%s%s\n", tmp->print_date, *(tmp->desc));
221: tmp = tmp->next;
222: }
223: tmp = events;
224: while (tmp) {
1.5 pjanzen 225: events = tmp;
1.6 pjanzen 226: if (tmp->ldesc)
227: free(tmp->ldesc);
1.5 pjanzen 228: tmp = tmp->next;
229: free(events);
1.1 millert 230: }
231: closecal(fp);
232: }
233:
234: int
1.31 deraadt 235: getfield(char *p, char **endp, int *flags)
1.1 millert 236: {
1.6 pjanzen 237: int val, var, i;
1.1 millert 238: char *start, savech;
239:
1.8 pjanzen 240: for (; !isdigit(*p) && !isalpha(*p) && *p != '*' && *p != '\t'; ++p)
1.1 millert 241: ;
1.6 pjanzen 242: if (*p == '*') { /* `*' is every month */
1.1 millert 243: *flags |= F_ISMONTH;
244: *endp = p+1;
1.6 pjanzen 245: return (-1); /* means 'every month' */
1.1 millert 246: }
247: if (isdigit(*p)) {
248: val = strtol(p, &p, 10); /* if 0, it's failure */
249: for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p)
250: ;
251: *endp = p;
252: return (val);
253: }
254: for (start = p; isalpha(*++p);)
255: ;
256:
257: /* Sunday-1 */
258: if (*p == '+' || *p == '-')
1.31 deraadt 259: for(; isdigit(*++p); )
260: ;
1.1 millert 261:
262: savech = *p;
263: *p = '\0';
264:
265: /* Month */
266: if ((val = getmonth(start)) != 0)
267: *flags |= F_ISMONTH;
268:
269: /* Day */
270: else if ((val = getday(start)) != 0) {
1.31 deraadt 271: *flags |= F_ISDAY;
1.1 millert 272:
1.31 deraadt 273: /* variable weekday */
274: if ((var = getdayvar(start)) != 0) {
275: if (var <= 5 && var >= -4)
276: val += var * 10;
1.1 millert 277: #ifdef DEBUG
1.31 deraadt 278: printf("var: %d\n", var);
1.1 millert 279: #endif
1.31 deraadt 280: }
1.1 millert 281: }
282:
1.6 pjanzen 283: /* Try specials (Easter, Paskha, ...) */
1.1 millert 284: else {
1.6 pjanzen 285: for (i = 0; i < NUMEV; i++) {
286: if (strncasecmp(start, spev[i].name, spev[i].nlen) == 0) {
287: start += spev[i].nlen;
288: val = i + 1;
289: i = NUMEV + 1;
290: } else if (spev[i].uname != NULL &&
291: strncasecmp(start, spev[i].uname, spev[i].ulen) == 0) {
292: start += spev[i].ulen;
293: val = i + 1;
294: i = NUMEV + 1;
295: }
296: }
297: if (i > NUMEV) {
298: switch(*start) {
299: case '-':
300: case '+':
1.31 deraadt 301: var = atoi(start);
302: if (var > 365 || var < -365)
303: return (0); /* Someone is just being silly */
304: val += (NUMEV + 1) * var;
305: /* We add one to the matching event and multiply by
306: * (NUMEV + 1) so as not to return 0 if there's a match.
307: * val will overflow if there is an obscenely large
308: * number of special events. */
309: break;
1.6 pjanzen 310: }
1.31 deraadt 311: *flags |= F_SPECIAL;
1.6 pjanzen 312: }
313: if (!(*flags & F_SPECIAL)) {
1.31 deraadt 314: /* undefined rest */
1.6 pjanzen 315: *p = savech;
316: return (0);
317: }
1.1 millert 318: }
1.8 pjanzen 319: for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*' && *p != '\t'; ++p)
1.1 millert 320: ;
321: *endp = p;
322: return (val);
323: }
324:
1.10 deraadt 325:
1.1 millert 326: FILE *
1.20 vincent 327: opencal(void)
1.1 millert 328: {
1.20 vincent 329: int pdes[2], fdin;
330: struct stat st;
1.1 millert 331:
332: /* open up calendar file as stdin */
1.20 vincent 333: if ((fdin = open(calendarFile, O_RDONLY)) == -1 ||
334: fstat(fdin, &st) == -1 || !S_ISREG(st.st_mode)) {
1.13 pjanzen 335: if (!doall) {
1.9 millert 336: char *home = getenv("HOME");
337: if (home == NULL || *home == '\0')
338: errx(1, "cannot get home directory");
1.10 deraadt 339: if (!(chdir(home) == 0 &&
340: chdir(calendarHome) == 0 &&
1.14 pjanzen 341: (fdin = open(calendarFile, O_RDONLY)) != -1))
1.18 deraadt 342: errx(1, "no calendar file: ``%s'' or ``~/%s/%s''",
1.2 millert 343: calendarFile, calendarHome, calendarFile);
1.1 millert 344: }
345: }
1.20 vincent 346:
1.1 millert 347: if (pipe(pdes) < 0)
348: return (NULL);
349: switch (vfork()) {
350: case -1: /* error */
351: (void)close(pdes[0]);
352: (void)close(pdes[1]);
353: return (NULL);
354: case 0:
1.10 deraadt 355: dup2(fdin, STDIN_FILENO);
356: /* child -- set stdout to pipe input */
1.1 millert 357: if (pdes[1] != STDOUT_FILENO) {
358: (void)dup2(pdes[1], STDOUT_FILENO);
359: (void)close(pdes[1]);
360: }
361: (void)close(pdes[0]);
1.20 vincent 362: /*
363: * Set stderr to /dev/null. Necessary so that cron does not
1.13 pjanzen 364: * wait for cpp to finish if it's running calendar -a.
365: */
366: if (doall) {
367: int fderr;
368: fderr = open(_PATH_DEVNULL, O_WRONLY, 0);
369: if (fderr == -1)
370: _exit(0);
371: (void)dup2(fderr, STDERR_FILENO);
372: (void)close(fderr);
373: }
1.29 otto 374: execl(_PATH_CPP, "cpp", "-traditional", "-undef", "-U__GNUC__",
375: "-P", "-I.", _PATH_INCLUDE, (char *)NULL);
1.1 millert 376: warn(_PATH_CPP);
377: _exit(1);
378: }
379: /* parent -- set stdin to pipe output */
380: (void)dup2(pdes[0], STDIN_FILENO);
381: (void)close(pdes[0]);
382: (void)close(pdes[1]);
383:
384: /* not reading all calendar files, just set output to stdout */
385: if (!doall)
386: return (stdout);
387:
388: /* set output to a temporary file, so if no output don't send mail */
1.13 pjanzen 389: return(tmpfile());
1.1 millert 390: }
391:
392: void
1.31 deraadt 393: closecal(FILE *fp)
1.1 millert 394: {
395: struct stat sbuf;
396: int nread, pdes[2], status;
397: char buf[1024];
398:
399: if (!doall)
400: return;
401:
402: (void)rewind(fp);
403: if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
404: goto done;
405: if (pipe(pdes) < 0)
406: goto done;
407: switch (vfork()) {
408: case -1: /* error */
409: (void)close(pdes[0]);
410: (void)close(pdes[1]);
411: goto done;
412: case 0:
413: /* child -- set stdin to pipe output */
414: if (pdes[0] != STDIN_FILENO) {
415: (void)dup2(pdes[0], STDIN_FILENO);
416: (void)close(pdes[0]);
417: }
418: (void)close(pdes[1]);
419: execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
1.12 deraadt 420: "\"Reminder Service\"", (char *)NULL);
1.1 millert 421: warn(_PATH_SENDMAIL);
422: _exit(1);
423: }
424: /* parent -- write to pipe input */
425: (void)close(pdes[0]);
426:
427: header[1].iov_base = header[3].iov_base = pw->pw_name;
428: header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
1.33 ! deraadt 429: writev(pdes[1], header, 8);
1.1 millert 430: while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
431: (void)write(pdes[1], buf, nread);
432: (void)close(pdes[1]);
433: done: (void)fclose(fp);
1.10 deraadt 434: while (wait(&status) >= 0)
435: ;
1.5 pjanzen 436: }
437:
438:
439: void
1.31 deraadt 440: insert(struct event **head, struct event *cur_evt)
1.5 pjanzen 441: {
442: struct event *tmp, *tmp2;
443:
444: if (*head) {
445: /* Insert this one in order */
446: tmp = *head;
447: tmp2 = NULL;
448: while (tmp->next &&
449: tmp->when <= cur_evt->when) {
450: tmp2 = tmp;
451: tmp = tmp->next;
452: }
453: if (tmp->when > cur_evt->when) {
454: cur_evt->next = tmp;
455: if (tmp2)
456: tmp2->next = cur_evt;
457: else
458: *head = cur_evt;
459: } else {
460: cur_evt->next = tmp->next;
461: tmp->next = cur_evt;
462: }
463: } else {
464: *head = cur_evt;
465: cur_evt->next = NULL;
466: }
1.1 millert 467: }