=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/calendar/io.c,v retrieving revision 1.2 retrieving revision 1.50 diff -u -r1.2 -r1.50 --- src/usr.bin/calendar/io.c 1997/09/12 04:12:48 1.2 +++ src/usr.bin/calendar/io.c 2021/10/24 21:24:16 1.50 @@ -1,4 +1,4 @@ -/* $OpenBSD: io.c,v 1.2 1997/09/12 04:12:48 millert Exp $ */ +/* $OpenBSD: io.c,v 1.50 2021/10/24 21:24:16 deraadt Exp $ */ /* * Copyright (c) 1989, 1993, 1994 @@ -12,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -33,21 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; -#else -static char rcsid[] = "$OpenBSD: io.c,v 1.2 1997/09/12 04:12:48 millert Exp $"; -#endif -#endif /* not lint */ - -#include #include #include #include @@ -57,46 +38,42 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include "pathnames.h" #include "calendar.h" -char *calendarFile = "calendar"; /* default calendar file */ -char *calendarHome = ".calendar"; /* HOME */ -char *calendarNoMail = "nomail"; /* don't sent mail if this file exist */ - -struct fixs neaster, npaskha; - struct iovec header[] = { - {"From: ", 6}, - {NULL, 0}, - {" (Reminder Service)\nTo: ", 24}, - {NULL, 0}, - {"\nSubject: ", 10}, - {NULL, 0}, - {"'s Calendar\nPrecedence: bulk\n\n", 30}, + { "From: ", 6 }, + { NULL, 0 }, + { " (Reminder Service)\nTo: ", 24 }, + { NULL, 0 }, + { "\nSubject: ", 10 }, + { NULL, 0 }, + { "'s Calendar\nPrecedence: bulk\n", 29 }, + { "Auto-Submitted: auto-generated\n\n", 32 }, }; void -cal() +cal(void) { - register int printing; - register char *p; + int ch, l, i, bodun = 0, bodun_maybe = 0, var, printing; + struct event *events, *cur_evt, *ev1, *tmp; + char buf[2048 + 1], *prefix = NULL, *p; + struct match *m; FILE *fp; - int ch, l; - int month; - int day; - int var; - char buf[2048 + 1]; + events = NULL; + cur_evt = NULL; if ((fp = opencal()) == NULL) return; for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) { @@ -104,9 +81,7 @@ *p = '\0'; else while ((ch = getchar()) != '\n' && ch != EOF); - for (l = strlen(buf); - l > 0 && isspace(buf[l - 1]); - l--) + for (l = strlen(buf); l > 0 && isspace(buf[l - 1]); l--) ; buf[l] = '\0'; if (buf[0] == '\0') @@ -114,83 +89,157 @@ if (strncmp(buf, "LANG=", 5) == 0) { (void) setlocale(LC_ALL, buf + 5); setnnames(); + if (!strcmp(buf + 5, "ru_RU.UTF-8") || + !strcmp(buf + 5, "uk_UA.UTF-8") || + !strcmp(buf + 5, "by_BY.UTF-8")) { + bodun_maybe++; + bodun = 0; + free(prefix); + prefix = NULL; + } else + bodun_maybe = 0; continue; - } - if (strncasecmp(buf, "Easter=", 7) == 0 && buf[7]) { - if (neaster.name != NULL) - free(neaster.name); - if ((neaster.name = strdup(buf + 7)) == NULL) - errx(1, "cannot allocate memory"); - neaster.len = strlen(buf + 7); + } else if (strncmp(buf, "CALENDAR=", 9) == 0) { + char *ep; + + if (buf[9] == '\0') + calendar = 0; + else if (!strcasecmp(buf + 9, "julian")) { + calendar = JULIAN; + errno = 0; + julian = strtoul(buf + 14, &ep, 10); + if (buf[0] == '\0' || *ep != '\0') + julian = 13; + if ((errno == ERANGE && julian == ULONG_MAX) || + julian > 14) + errx(1, "Julian calendar offset is too large"); + } else if (!strcasecmp(buf + 9, "gregorian")) + calendar = GREGORIAN; + else if (!strcasecmp(buf + 9, "lunar")) + calendar = LUNAR; + } else if (bodun_maybe && strncmp(buf, "BODUN=", 6) == 0) { + bodun++; + free(prefix); + if ((prefix = strdup(buf + 6)) == NULL) + err(1, NULL); continue; } - if (strncasecmp(buf, "Paskha=", 7) == 0 && buf[7]) { - if (npaskha.name != NULL) - free(npaskha.name); - if ((npaskha.name = strdup(buf + 7)) == NULL) - errx(1, "cannot allocate memory"); - npaskha.len = strlen(buf + 7); - continue; + /* User defined names for special events */ + if ((p = strchr(buf, '='))) { + for (i = 0; i < NUMEV; i++) { + if (strncasecmp(buf, spev[i].name, + spev[i].nlen) == 0 && + (p - buf == spev[i].nlen) && + buf[spev[i].nlen + 1]) { + p++; + free(spev[i].uname); + if ((spev[i].uname = strdup(p)) == NULL) + err(1, NULL); + spev[i].ulen = strlen(p); + i = NUMEV + 1; + } + } + if (i > NUMEV) + continue; } if (buf[0] != '\t') { - printing = isnow(buf, &month, &day, &var) ? 1 : 0; - if ((p = strchr(buf, '\t')) == NULL) + printing = (m = isnow(buf, bodun)) ? 1 : 0; + if ((p = strchr(buf, '\t')) == NULL) { + printing = 0; continue; + } + /* Need the following to catch hardwired "variable" + * dates */ if (p > buf && p[-1] == '*') var = 1; + else + var = 0; if (printing) { - struct tm tm; - char dbuf[30]; + struct match *foo; - tm.tm_sec = 0; /* unused */ - tm.tm_min = 0; /* unused */ - tm.tm_hour = 0; /* unused */ - tm.tm_wday = 0; /* unused */ - tm.tm_mon = month - 1; - tm.tm_mday = day; - tm.tm_year = tp->tm_year; /* unused */ - (void)strftime(dbuf, sizeof(dbuf), "%c", &tm); - dbuf[10] = '\0'; - (void)fprintf(fp, "%s%c%s\n", - dbuf + 4/* skip weekdays */, - var ? '*' : ' ', p); + ev1 = NULL; + while (m) { + cur_evt = malloc(sizeof(struct event)); + if (cur_evt == NULL) + err(1, NULL); + + cur_evt->when = m->when; + snprintf(cur_evt->print_date, + sizeof(cur_evt->print_date), "%s%c", + m->print_date, (var + m->var) ? '*' : ' '); + if (ev1) { + cur_evt->desc = ev1->desc; + cur_evt->ldesc = NULL; + } else { + if (m->bodun && prefix) { + if (asprintf(&cur_evt->ldesc, + "\t%s %s", prefix, + p + 1) == -1) + err(1, NULL); + } else if ((cur_evt->ldesc = + strdup(p)) == NULL) + err(1, NULL); + cur_evt->desc = &(cur_evt->ldesc); + ev1 = cur_evt; + } + insert(&events, cur_evt); + foo = m; + m = m->next; + free(foo); + } } + } else if (printing) { + if (asprintf(&p, "%s\n%s", ev1->ldesc, + buf) == -1) + err(1, NULL); + free(ev1->ldesc); + ev1->ldesc = p; } - else if (printing) - fprintf(fp, "%s\n", buf); } + tmp = events; + while (tmp) { + (void)fprintf(fp, "%s%s\n", tmp->print_date, *(tmp->desc)); + tmp = tmp->next; + } + tmp = events; + while (tmp) { + events = tmp; + free(tmp->ldesc); + tmp = tmp->next; + free(events); + } closecal(fp); } int -getfield(p, endp, flags) - char *p, **endp; - int *flags; +getfield(char *p, char **endp, int *flags) { - int val, var; + int val, var, i; char *start, savech; - for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p) + for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && + *p != '*' && *p != '\t'; ++p) ; - if (*p == '*') { /* `*' is current month */ + if (*p == '*') { /* `*' is every month */ *flags |= F_ISMONTH; *endp = p+1; - return (tp->tm_mon + 1); + return (-1); /* means 'every month' */ } - if (isdigit(*p)) { + if (isdigit((unsigned char)*p)) { val = strtol(p, &p, 10); /* if 0, it's failure */ - for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p) + for (; !isdigit((unsigned char)*p) && + !isalpha((unsigned char)*p) && *p != '*'; ++p) ; *endp = p; return (val); } - for (start = p; isalpha(*++p);) + for (start = p; isalpha((unsigned char)*++p);) ; /* Sunday-1 */ if (*p == '+' || *p == '-') - for(; isdigit(*++p);) - ; + for(; isdigit((unsigned char)*++p); ) + ; savech = *p; *p = '\0'; @@ -201,79 +250,119 @@ /* Day */ else if ((val = getday(start)) != 0) { - *flags |= F_ISDAY; + *flags |= F_ISDAY; - /* variable weekday */ - if ((var = getdayvar(start)) != 0) { - if (var <=5 && var >= -4) - val += var * 10; + /* variable weekday */ + if ((var = getdayvar(start)) != 0) { + if (var <= 5 && var >= -4) + val += var * 10; #ifdef DEBUG - printf("var: %d\n", var); + printf("var: %d\n", var); #endif - } + } } - /* Easter */ - else if ((val = geteaster(start, tp->tm_year + 1900)) != 0) - *flags |= F_EASTER; - - /* Paskha */ - else if ((val = getpaskha(start, tp->tm_year + 1900)) != 0) - *flags |= F_EASTER; - - /* undefined rest */ + /* Try specials (Easter, Paskha, ...) */ else { - *p = savech; - return (0); + for (i = 0; i < NUMEV; i++) { + if (strncasecmp(start, spev[i].name, spev[i].nlen) == 0) { + start += spev[i].nlen; + val = i + 1; + i = NUMEV + 1; + } else if (spev[i].uname != NULL && + strncasecmp(start, spev[i].uname, spev[i].ulen) == 0) { + start += spev[i].ulen; + val = i + 1; + i = NUMEV + 1; + } + } + if (i > NUMEV) { + const char *errstr; + + switch (*start) { + case '-': + case '+': + var = strtonum(start + 1, 0, 365, &errstr); + if (errstr) + return (0); /* Someone is just being silly */ + if (*start == '-') + var = -var; + val += (NUMEV + 1) * var; + /* We add one to the matching event and multiply by + * (NUMEV + 1) so as not to return 0 if there's a match. + * val will overflow if there is an obscenely large + * number of special events. */ + break; + } + *flags |= F_SPECIAL; + } + if (!(*flags & F_SPECIAL)) { + /* undefined rest */ + *p = savech; + return (0); + } } - for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p) + for (*p = savech; !isdigit((unsigned char)*p) && + !isalpha((unsigned char)*p) && *p != '*' && *p != '\t'; ++p) ; *endp = p; return (val); } -char path[MAXPATHLEN]; FILE * -opencal() +opencal(void) { - int fd, pdes[2]; - struct stat sbuf; + int pdes[2], fdin; + struct stat st; /* open up calendar file as stdin */ - if (!freopen(calendarFile, "r", stdin)) { - if (doall) { - if (chdir(calendarHome) != 0) - return (NULL); - if (stat(calendarNoMail, &sbuf) == 0) - return (NULL); - if (!freopen(calendarFile, "r", stdin)) - return (NULL); - } else { - chdir(getenv("HOME")); - if (!(chdir(calendarHome) == 0 && - freopen(calendarFile, "r", stdin))) - errx(1, "no calendar file: ``%s'' or ``~/%s/%s", + if ((fdin = open(calendarFile, O_RDONLY)) == -1 || + fstat(fdin, &st) == -1 || !S_ISREG(st.st_mode)) { + if (!doall) { + char *home = getenv("HOME"); + if (home == NULL || *home == '\0') + errx(1, "cannot get home directory"); + if (!(chdir(home) == 0 && + chdir(calendarHome) == 0 && + (fdin = open(calendarFile, O_RDONLY)) != -1)) + errx(1, "no calendar file: \"%s\" or \"~/%s/%s\"", calendarFile, calendarHome, calendarFile); } } - if (pipe(pdes) < 0) + + if (pipe(pdes) == -1) { + close(fdin); return (NULL); + } switch (vfork()) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); + close(fdin); return (NULL); case 0: - /* child -- stdin already setup, set stdout to pipe input */ + dup2(fdin, STDIN_FILENO); + /* child -- set stdout to pipe input */ if (pdes[1] != STDOUT_FILENO) { (void)dup2(pdes[1], STDOUT_FILENO); (void)close(pdes[1]); } (void)close(pdes[0]); - (void)setuid(geteuid()); - (void)setgid(getegid()); - execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL); + /* + * Set stderr to /dev/null. Necessary so that cron does not + * wait for cpp to finish if it's running calendar -a. + */ + if (doall) { + int fderr; + fderr = open(_PATH_DEVNULL, O_WRONLY); + if (fderr == -1) + _exit(0); + (void)dup2(fderr, STDERR_FILENO); + (void)close(fderr); + } + execl(_PATH_CPP, "cpp", "-traditional", "-undef", "-U__GNUC__", + "-P", "-I.", _PATH_INCLUDE, (char *)NULL); warn(_PATH_CPP); _exit(1); } @@ -287,19 +376,16 @@ return (stdout); /* set output to a temporary file, so if no output don't send mail */ - (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); - if ((fd = mkstemp(path)) < 0) - return (NULL); - return (fdopen(fd, "w+")); + return(tmpfile()); } void -closecal(fp) - FILE *fp; +closecal(FILE *fp) { struct stat sbuf; int nread, pdes[2], status; char buf[1024]; + pid_t pid = -1; if (!doall) return; @@ -307,9 +393,9 @@ (void)rewind(fp); if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) goto done; - if (pipe(pdes) < 0) + if (pipe(pdes) == -1) goto done; - switch (vfork()) { + switch ((pid = vfork())) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); @@ -321,10 +407,8 @@ (void)close(pdes[0]); } (void)close(pdes[1]); - (void)setuid(geteuid()); - (void)setgid(getegid()); execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", - "\"Reminder Service\"", "-f", "root", NULL); + "\"Reminder Service\"", (char *)NULL); warn(_PATH_SENDMAIL); _exit(1); } @@ -333,11 +417,46 @@ header[1].iov_base = header[3].iov_base = pw->pw_name; header[1].iov_len = header[3].iov_len = strlen(pw->pw_name); - writev(pdes[1], header, 7); + writev(pdes[1], header, 8); while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) (void)write(pdes[1], buf, nread); (void)close(pdes[1]); done: (void)fclose(fp); - (void)unlink(path); - while (wait(&status) >= 0); + if (pid != -1) { + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) + break; + } + } +} + + +void +insert(struct event **head, struct event *cur_evt) +{ + struct event *tmp, *tmp2; + + if (*head) { + /* Insert this one in order */ + tmp = *head; + tmp2 = NULL; + while (tmp->next && + tmp->when <= cur_evt->when) { + tmp2 = tmp; + tmp = tmp->next; + } + if (tmp->when > cur_evt->when) { + cur_evt->next = tmp; + if (tmp2) + tmp2->next = cur_evt; + else + *head = cur_evt; + } else { + cur_evt->next = tmp->next; + tmp->next = cur_evt; + } + } else { + *head = cur_evt; + cur_evt->next = NULL; + } }