Annotation of src/usr.bin/calendar/io.c, Revision 1.31
1.31 ! deraadt 1: /* $OpenBSD: io.c,v 1.30 2005/11/14 15:56:35 deraadt 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.31 ! deraadt 42: static const char rcsid[] = "$OpenBSD: io.c,v 1.30 2005/11/14 15:56:35 deraadt 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 },
! 76: { "'s Calendar\nPrecedence: bulk\n\n", 30 },
1.1 millert 77: };
78:
79:
80: void
1.20 vincent 81: cal(void)
1.1 millert 82: {
1.31 ! deraadt 83: int ch, l, i, bodun = 0, bodun_maybe = 0, var, printing;
1.6 pjanzen 84: struct event *events, *cur_evt, *ev1, *tmp;
1.31 ! deraadt 85: char buf[2048 + 1], *prefix = NULL, *p;
1.5 pjanzen 86: struct match *m;
1.23 grange 87: size_t nlen;
1.31 ! deraadt 88: FILE *fp;
1.1 millert 89:
1.5 pjanzen 90: events = NULL;
91: cur_evt = NULL;
1.1 millert 92: if ((fp = opencal()) == NULL)
93: return;
94: for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
95: if ((p = strchr(buf, '\n')) != NULL)
96: *p = '\0';
97: else
98: while ((ch = getchar()) != '\n' && ch != EOF);
1.10 deraadt 99: for (l = strlen(buf); l > 0 && isspace(buf[l - 1]); l--)
1.1 millert 100: ;
101: buf[l] = '\0';
102: if (buf[0] == '\0')
103: continue;
104: if (strncmp(buf, "LANG=", 5) == 0) {
105: (void) setlocale(LC_ALL, buf + 5);
106: setnnames();
1.15 mickey 107: if (!strcmp(buf + 5, "ru_RU.KOI8-R") ||
108: !strcmp(buf + 5, "uk_UA.KOI8-U") ||
109: !strcmp(buf + 5, "by_BY.KOI8-B")) {
110: bodun_maybe++;
1.17 mickey 111: bodun = 0;
112: if (prefix)
113: free(prefix);
114: prefix = NULL;
1.15 mickey 115: } else
116: bodun_maybe = 0;
1.1 millert 117: continue;
1.27 mickey 118: } else if (strncmp(buf, "CALENDAR=", 9) == 0) {
119: char *ep;
120:
121: if (buf[9] == '\0')
122: calendar = 0;
123: else if (!strcasecmp(buf + 9, "julian")) {
124: calendar = JULIAN;
125: errno = 0;
126: julian = strtoul(buf + 14, &ep, 10);
127: if (buf[0] == '\0' || *ep != '\0')
128: julian = 13;
129: if ((errno == ERANGE && julian == ULONG_MAX) ||
130: julian > 14)
131: errx(1, "Julian calendar offset is too large");
132: } else if (!strcasecmp(buf + 9, "gregorian"))
133: calendar = GREGORIAN;
134: else if (!strcasecmp(buf + 9, "lunar"))
135: calendar = LUNAR;
136: } else if (bodun_maybe && strncmp(buf, "BODUN=", 6) == 0) {
1.15 mickey 137: bodun++;
1.17 mickey 138: if (prefix)
139: free(prefix);
140: if ((prefix = strdup(buf + 6)) == NULL)
141: err(1, NULL);
1.27 mickey 142: continue;
1.17 mickey 143: }
1.6 pjanzen 144: /* User defined names for special events */
145: if ((p = strchr(buf, '='))) {
146: for (i = 0; i < NUMEV; i++) {
1.31 ! deraadt 147: if (strncasecmp(buf, spev[i].name,
! 148: spev[i].nlen) == 0 &&
! 149: (p - buf == spev[i].nlen) &&
! 150: buf[spev[i].nlen + 1]) {
! 151: p++;
! 152: if (spev[i].uname != NULL)
! 153: free(spev[i].uname);
! 154: if ((spev[i].uname = strdup(p)) == NULL)
! 155: err(1, NULL);
! 156: spev[i].ulen = strlen(p);
! 157: i = NUMEV + 1;
! 158: }
1.6 pjanzen 159: }
1.31 ! deraadt 160: if (i > NUMEV)
! 161: continue;
1.1 millert 162: }
163: if (buf[0] != '\t') {
1.15 mickey 164: printing = (m = isnow(buf, bodun)) ? 1 : 0;
1.6 pjanzen 165: if ((p = strchr(buf, '\t')) == NULL) {
166: printing = 0;
1.1 millert 167: continue;
1.6 pjanzen 168: }
1.5 pjanzen 169: /* Need the following to catch hardwired "variable"
170: * dates */
1.1 millert 171: if (p > buf && p[-1] == '*')
172: var = 1;
1.5 pjanzen 173: else
174: var = 0;
1.1 millert 175: if (printing) {
1.5 pjanzen 176: struct match *foo;
177:
1.6 pjanzen 178: ev1 = NULL;
1.5 pjanzen 179: while (m) {
1.31 ! deraadt 180: cur_evt = malloc(sizeof(struct event));
! 181: if (cur_evt == NULL)
! 182: err(1, NULL);
! 183:
! 184: cur_evt->when = m->when;
! 185: snprintf(cur_evt->print_date,
! 186: sizeof(cur_evt->print_date), "%s%c",
! 187: m->print_date, (var + m->var) ? '*' : ' ');
! 188: if (ev1) {
! 189: cur_evt->desc = ev1->desc;
! 190: cur_evt->ldesc = NULL;
! 191: } else {
! 192: if (m->bodun && prefix) {
! 193: if (asprintf(&cur_evt->ldesc,
! 194: "\t%s %s", prefix,
! 195: p + 1) == -1)
! 196: err(1, NULL);
! 197: } else if ((cur_evt->ldesc =
! 198: strdup(p)) == NULL)
1.26 tom 199: err(1, NULL);
1.31 ! deraadt 200: cur_evt->desc = &(cur_evt->ldesc);
! 201: ev1 = cur_evt;
! 202: }
! 203: insert(&events, cur_evt);
! 204: foo = m;
! 205: m = m->next;
! 206: free(foo);
1.5 pjanzen 207: }
1.1 millert 208: }
1.30 deraadt 209: } else if (printing) {
210: free(ev1->ldesc);
211: if (asprintf(&ev1->ldesc, "%s\n%s", ev1->ldesc,
212: buf) == -1)
1.11 pjanzen 213: err(1, NULL);
1.30 deraadt 214:
1.5 pjanzen 215: }
216: }
217: tmp = events;
218: while (tmp) {
1.6 pjanzen 219: (void)fprintf(fp, "%s%s\n", tmp->print_date, *(tmp->desc));
220: tmp = tmp->next;
221: }
222: tmp = events;
223: while (tmp) {
1.5 pjanzen 224: events = tmp;
1.6 pjanzen 225: if (tmp->ldesc)
226: free(tmp->ldesc);
1.5 pjanzen 227: tmp = tmp->next;
228: free(events);
1.1 millert 229: }
230: closecal(fp);
231: }
232:
233: int
1.31 ! deraadt 234: getfield(char *p, char **endp, int *flags)
1.1 millert 235: {
1.6 pjanzen 236: int val, var, i;
1.1 millert 237: char *start, savech;
238:
1.8 pjanzen 239: for (; !isdigit(*p) && !isalpha(*p) && *p != '*' && *p != '\t'; ++p)
1.1 millert 240: ;
1.6 pjanzen 241: if (*p == '*') { /* `*' is every month */
1.1 millert 242: *flags |= F_ISMONTH;
243: *endp = p+1;
1.6 pjanzen 244: return (-1); /* means 'every month' */
1.1 millert 245: }
246: if (isdigit(*p)) {
247: val = strtol(p, &p, 10); /* if 0, it's failure */
248: for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p)
249: ;
250: *endp = p;
251: return (val);
252: }
253: for (start = p; isalpha(*++p);)
254: ;
255:
256: /* Sunday-1 */
257: if (*p == '+' || *p == '-')
1.31 ! deraadt 258: for(; isdigit(*++p); )
! 259: ;
1.1 millert 260:
261: savech = *p;
262: *p = '\0';
263:
264: /* Month */
265: if ((val = getmonth(start)) != 0)
266: *flags |= F_ISMONTH;
267:
268: /* Day */
269: else if ((val = getday(start)) != 0) {
1.31 ! deraadt 270: *flags |= F_ISDAY;
1.1 millert 271:
1.31 ! deraadt 272: /* variable weekday */
! 273: if ((var = getdayvar(start)) != 0) {
! 274: if (var <= 5 && var >= -4)
! 275: val += var * 10;
1.1 millert 276: #ifdef DEBUG
1.31 ! deraadt 277: printf("var: %d\n", var);
1.1 millert 278: #endif
1.31 ! deraadt 279: }
1.1 millert 280: }
281:
1.6 pjanzen 282: /* Try specials (Easter, Paskha, ...) */
1.1 millert 283: else {
1.6 pjanzen 284: for (i = 0; i < NUMEV; i++) {
285: if (strncasecmp(start, spev[i].name, spev[i].nlen) == 0) {
286: start += spev[i].nlen;
287: val = i + 1;
288: i = NUMEV + 1;
289: } else if (spev[i].uname != NULL &&
290: strncasecmp(start, spev[i].uname, spev[i].ulen) == 0) {
291: start += spev[i].ulen;
292: val = i + 1;
293: i = NUMEV + 1;
294: }
295: }
296: if (i > NUMEV) {
297: switch(*start) {
298: case '-':
299: case '+':
1.31 ! deraadt 300: var = atoi(start);
! 301: if (var > 365 || var < -365)
! 302: return (0); /* Someone is just being silly */
! 303: val += (NUMEV + 1) * var;
! 304: /* We add one to the matching event and multiply by
! 305: * (NUMEV + 1) so as not to return 0 if there's a match.
! 306: * val will overflow if there is an obscenely large
! 307: * number of special events. */
! 308: break;
1.6 pjanzen 309: }
1.31 ! deraadt 310: *flags |= F_SPECIAL;
1.6 pjanzen 311: }
312: if (!(*flags & F_SPECIAL)) {
1.31 ! deraadt 313: /* undefined rest */
1.6 pjanzen 314: *p = savech;
315: return (0);
316: }
1.1 millert 317: }
1.8 pjanzen 318: for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*' && *p != '\t'; ++p)
1.1 millert 319: ;
320: *endp = p;
321: return (val);
322: }
323:
1.10 deraadt 324:
1.1 millert 325: FILE *
1.20 vincent 326: opencal(void)
1.1 millert 327: {
1.20 vincent 328: int pdes[2], fdin;
329: struct stat st;
1.1 millert 330:
331: /* open up calendar file as stdin */
1.20 vincent 332: if ((fdin = open(calendarFile, O_RDONLY)) == -1 ||
333: fstat(fdin, &st) == -1 || !S_ISREG(st.st_mode)) {
1.13 pjanzen 334: if (!doall) {
1.9 millert 335: char *home = getenv("HOME");
336: if (home == NULL || *home == '\0')
337: errx(1, "cannot get home directory");
1.10 deraadt 338: if (!(chdir(home) == 0 &&
339: chdir(calendarHome) == 0 &&
1.14 pjanzen 340: (fdin = open(calendarFile, O_RDONLY)) != -1))
1.18 deraadt 341: errx(1, "no calendar file: ``%s'' or ``~/%s/%s''",
1.2 millert 342: calendarFile, calendarHome, calendarFile);
1.1 millert 343: }
344: }
1.20 vincent 345:
1.1 millert 346: if (pipe(pdes) < 0)
347: return (NULL);
348: switch (vfork()) {
349: case -1: /* error */
350: (void)close(pdes[0]);
351: (void)close(pdes[1]);
352: return (NULL);
353: case 0:
1.10 deraadt 354: dup2(fdin, STDIN_FILENO);
355: /* child -- set stdout to pipe input */
1.1 millert 356: if (pdes[1] != STDOUT_FILENO) {
357: (void)dup2(pdes[1], STDOUT_FILENO);
358: (void)close(pdes[1]);
359: }
360: (void)close(pdes[0]);
1.20 vincent 361: /*
362: * Set stderr to /dev/null. Necessary so that cron does not
1.13 pjanzen 363: * wait for cpp to finish if it's running calendar -a.
364: */
365: if (doall) {
366: int fderr;
367: fderr = open(_PATH_DEVNULL, O_WRONLY, 0);
368: if (fderr == -1)
369: _exit(0);
370: (void)dup2(fderr, STDERR_FILENO);
371: (void)close(fderr);
372: }
1.29 otto 373: execl(_PATH_CPP, "cpp", "-traditional", "-undef", "-U__GNUC__",
374: "-P", "-I.", _PATH_INCLUDE, (char *)NULL);
1.1 millert 375: warn(_PATH_CPP);
376: _exit(1);
377: }
378: /* parent -- set stdin to pipe output */
379: (void)dup2(pdes[0], STDIN_FILENO);
380: (void)close(pdes[0]);
381: (void)close(pdes[1]);
382:
383: /* not reading all calendar files, just set output to stdout */
384: if (!doall)
385: return (stdout);
386:
387: /* set output to a temporary file, so if no output don't send mail */
1.13 pjanzen 388: return(tmpfile());
1.1 millert 389: }
390:
391: void
1.31 ! deraadt 392: closecal(FILE *fp)
1.1 millert 393: {
394: struct stat sbuf;
395: int nread, pdes[2], status;
396: char buf[1024];
397:
398: if (!doall)
399: return;
400:
401: (void)rewind(fp);
402: if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
403: goto done;
404: if (pipe(pdes) < 0)
405: goto done;
406: switch (vfork()) {
407: case -1: /* error */
408: (void)close(pdes[0]);
409: (void)close(pdes[1]);
410: goto done;
411: case 0:
412: /* child -- set stdin to pipe output */
413: if (pdes[0] != STDIN_FILENO) {
414: (void)dup2(pdes[0], STDIN_FILENO);
415: (void)close(pdes[0]);
416: }
417: (void)close(pdes[1]);
418: execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
1.12 deraadt 419: "\"Reminder Service\"", (char *)NULL);
1.1 millert 420: warn(_PATH_SENDMAIL);
421: _exit(1);
422: }
423: /* parent -- write to pipe input */
424: (void)close(pdes[0]);
425:
426: header[1].iov_base = header[3].iov_base = pw->pw_name;
427: header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
428: writev(pdes[1], header, 7);
429: while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
430: (void)write(pdes[1], buf, nread);
431: (void)close(pdes[1]);
432: done: (void)fclose(fp);
1.10 deraadt 433: while (wait(&status) >= 0)
434: ;
1.5 pjanzen 435: }
436:
437:
438: void
1.31 ! deraadt 439: insert(struct event **head, struct event *cur_evt)
1.5 pjanzen 440: {
441: struct event *tmp, *tmp2;
442:
443: if (*head) {
444: /* Insert this one in order */
445: tmp = *head;
446: tmp2 = NULL;
447: while (tmp->next &&
448: tmp->when <= cur_evt->when) {
449: tmp2 = tmp;
450: tmp = tmp->next;
451: }
452: if (tmp->when > cur_evt->when) {
453: cur_evt->next = tmp;
454: if (tmp2)
455: tmp2->next = cur_evt;
456: else
457: *head = cur_evt;
458: } else {
459: cur_evt->next = tmp->next;
460: tmp->next = cur_evt;
461: }
462: } else {
463: *head = cur_evt;
464: cur_evt->next = NULL;
465: }
1.1 millert 466: }