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