=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/calendar/day.c,v retrieving revision 1.4 retrieving revision 1.14 diff -u -r1.4 -r1.14 --- src/usr.bin/calendar/day.c 1998/03/30 06:59:26 1.4 +++ src/usr.bin/calendar/day.c 2001/11/19 19:02:13 1.14 @@ -1,4 +1,4 @@ -/* $OpenBSD: day.c,v 1.4 1998/03/30 06:59:26 deraadt Exp $ */ +/* $OpenBSD: day.c,v 1.14 2001/11/19 19:02:13 mpech Exp $ */ /* * Copyright (c) 1989, 1993, 1994 @@ -43,7 +43,7 @@ #if 0 static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; #else -static char rcsid[] = "$OpenBSD: day.c,v 1.4 1998/03/30 06:59:26 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: day.c,v 1.14 2001/11/19 19:02:13 mpech Exp $"; #endif #endif /* not lint */ @@ -62,8 +62,12 @@ #include "pathnames.h" #include "calendar.h" +#define WEEKLY 1 +#define MONTHLY 2 +#define YEARLY 3 + struct tm *tp; -int *cumdays, offset, yrdays; +int *cumdays, offset; char dayname[10]; @@ -104,7 +108,7 @@ if (ndays[i].name != NULL) free(ndays[i].name); if ((ndays[i].name = strdup(buf)) == NULL) - errx(1, "cannot allocate memory"); + err(1, NULL); ndays[i].len = strlen(buf); l = strftime(buf, sizeof(buf), "%A", &tm); @@ -114,7 +118,7 @@ if (fndays[i].name != NULL) free(fndays[i].name); if ((fndays[i].name = strdup(buf)) == NULL) - errx(1, "cannot allocate memory"); + err(1, NULL); fndays[i].len = strlen(buf); } @@ -127,7 +131,7 @@ if (nmonths[i].name != NULL) free(nmonths[i].name); if ((nmonths[i].name = strdup(buf)) == NULL) - errx(1, "cannot allocate memory"); + err(1, NULL); nmonths[i].len = strlen(buf); l = strftime(buf, sizeof(buf), "%B", &tm); @@ -137,25 +141,42 @@ if (fnmonths[i].name != NULL) free(fnmonths[i].name); if ((fnmonths[i].name = strdup(buf)) == NULL) - errx(1, "cannot allocate memory"); + err(1, NULL); fnmonths[i].len = strlen(buf); } + /* Hardwired special events */ + spev[0].name = strdup(EASTER); + spev[0].nlen = EASTERNAMELEN; + spev[0].getev = easter; + spev[1].name = strdup(PASKHA); + spev[1].nlen = PASKHALEN; + spev[1].getev = paskha; + for (i = 0; i < NUMEV; i++) { + if (spev[i].name == NULL) + err(1, NULL); + spev[i].uname = NULL; + } } void settime(now) - time_t now; + time_t *now; { - tp = localtime(&now); - if (isleap(tp->tm_year + TM_YEAR_BASE)) { - yrdays = DAYSPERLYEAR; + tp = localtime(now); + tp->tm_sec = 0; + tp->tm_min = 0; + /* Avoid getting caught by a timezone shift; set time to noon */ + tp->tm_isdst = 0; + tp->tm_hour = 12; + *now = mktime(tp); + if (isleap(tp->tm_year + TM_YEAR_BASE)) cumdays = daytab[1]; - } else { - yrdays = DAYSPERNYEAR; + else cumdays = daytab[0]; - } /* Friday displays Monday's events */ offset = tp->tm_wday == 5 ? 3 : 1; + if (f_dayAfter) + offset = 0; /* Except not when range is set explicitly */ header[5].iov_base = dayname; (void) setlocale(LC_TIME, "C"); @@ -183,7 +204,9 @@ return((time_t)-1); tm.tm_sec = 0; tm.tm_min = 0; - tm.tm_hour = 0; + /* Avoid getting caught by a timezone shift; set time to noon */ + tm.tm_isdst = 0; + tm.tm_hour = 12; tm.tm_wday = 0; tm.tm_mday = tp->tm_mday; tm.tm_mon = tp->tm_mon; @@ -199,12 +222,12 @@ } /* Year */ - if (len >= 7) { - *(date + len - 4) = '\0'; - tm.tm_year = atoi(date); + if (len >= 6) { + *(date + len - 4) = '\0'; + tm.tm_year = atoi(date); /* tm_year up TM_YEAR_BASE ... */ - if (tm.tm_year < 70) + if (tm.tm_year < 69) /* Y2K */ tm.tm_year += 2000 - TM_YEAR_BASE; else if (tm.tm_year < 100) tm.tm_year += 1900 - TM_YEAR_BASE; @@ -225,18 +248,22 @@ * 3-charweekday (Friday, Monday, mon.) * numeric month or day (1, 2, 04) * - * Any character may separate them, or they may not be separated. Any line, - * following a line that is matched, that starts with "whitespace", is shown - * along with the matched line. + * Any character except \t or '*' may separate them, or they may not be + * separated. Any line following a line that is matched, that starts + * with \t, is shown along with the matched line. */ -int -isnow(endp, monthp, dayp, varp) +struct match * +isnow(endp, bodun) char *endp; - int *monthp; - int *dayp; - int *varp; + int bodun; { - int day, flags = 0, month = 0, v1, v2; + int day = 0, flags = 0, month = 0, v1, v2, i; + int monthp, dayp, varp = 0; + struct match *matches = NULL, *tmp, *tmp2; + int interval = YEARLY; /* how frequently the event repeats. */ + int vwd = 0; /* Variable weekday */ + time_t tdiff, ttmp; + struct tm tmtmp; /* * CONVENTION @@ -251,11 +278,15 @@ /* read first field */ /* didn't recognize anything, skip it */ if (!(v1 = getfield(endp, &endp, &flags))) - return (0); + return (NULL); + /* adjust bodun rate */ + if (bodun && !bodun_always) + bodun = !(arc4random() % 3); + /* Easter or Easter depending days */ - if (flags & F_EASTER) - day = v1 - 1; /* days since January 1 [0-365] */ + if (flags & F_SPECIAL) + vwd = v1; /* * 1. {Weekday,Day} XYZ ... @@ -264,24 +295,47 @@ */ else if (flags & F_ISDAY || v1 > 12) { - /* found a day; day: 1-31 or weekday: 1-7 */ + /* found a day; day: 13-31 or weekday: 1-7 */ day = v1; /* {Day,Weekday} {Month,Monthname} ... */ - /* if no recognizable month, assume just a day alone - * in other words, find month or use current month */ - if (!(month = getfield(endp, &endp, &flags))) + /* if no recognizable month, assume just a day alone -- this is + * very unlikely and can only happen after the first 12 days. + * --find month or use current month */ + if (!(month = getfield(endp, &endp, &flags))) { month = tp->tm_mon + 1; + /* F_ISDAY is set only if a weekday was spelled out */ + /* F_ISDAY must be set if 0 < day < 8 */ + if ((day <= 7) && (day >= 1)) + interval = WEEKLY; + else + interval = MONTHLY; + } else if ((day <= 7) && (day >= 1)) + day += 10; + /* it's a weekday; make it the first one of the month */ + if (month == -1) { + month = tp->tm_mon + 1; + interval = MONTHLY; + } + if ((month > 12) || (month < 1)) + return (NULL); } /* 2. {Monthname} XYZ ... */ else if (flags & F_ISMONTH) { month = v1; - + if (month == -1) { + month = tp->tm_mon + 1; + interval = MONTHLY; + } /* Monthname {day,weekday} */ /* if no recognizable day, assume the first day in month */ if (!(day = getfield(endp, &endp, &flags))) day = 1; + /* If a weekday was spelled out without an ordering, + * assume the first of that day in the month */ + if ((flags & F_ISDAY) && (day >= 1) && (day <=7)) + day += 10; } /* Hm ... */ @@ -295,7 +349,10 @@ if (flags & F_ISMONTH) { day = v1; month = v2; - *varp = 0; + if (month == -1) { + month = tp->tm_mon + 1; + interval = MONTHLY; + } } /* {Month} {Weekday,Day} ... */ @@ -304,103 +361,213 @@ month = v1; /* if no recognizable day, assume the first */ day = v2 ? v2 : 1; - *varp = 0; + if ((flags & F_ISDAY) && (day >= 1) && (day <= 7)) + day += 10; } } /* convert Weekday into *next* Day, * e.g.: 'Sunday' -> 22 - * 'SunayLast' -> ?? + * 'SundayLast' -> ?? */ if (flags & F_ISDAY) { #if DEBUG - fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month); + fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month); #endif - *varp = 1; - /* variable weekday, SundayLast, MondayFirst ... */ - if (day < 0 || day >= 10) { - - /* negative offset; last, -4 .. -1 */ - if (day < 0) { - v1 = day/10 - 1; /* offset -4 ... -1 */ - day = 10 + (day % 10); /* day 1 ... 7 */ - - /* day, eg '22th' */ - v2 = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); - - /* (month length - day) / 7 + 1 */ - if (((int)((cumdays[month+1] - - cumdays[month] - v2) / 7) + 1) == -v1) - /* bingo ! */ - day = v2; - - /* set to yesterday */ - else - day = tp->tm_mday - 1; + varp = 1; + /* variable weekday, SundayLast, MondayFirst ... */ + if (day < 0 || day >= 10) + vwd = day; + else { + day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); + interval = WEEKLY; } + } else + /* Check for silliness. Note we still catch Feb 29 */ + if (!(flags & F_SPECIAL) && + (day > (cumdays[month + 1] - cumdays[month]) || day < 1)) { + if (!((month == 2 && day == 29) || + (interval == MONTHLY && day <= 31))) + return (NULL); + } - /* first, second ... +1 ... +5 */ - else { - v1 = day/10; /* offset: +1 (first Sunday) ... */ - day = day % 10; + if (!(flags & F_SPECIAL)) { + monthp = month; + dayp = day; + day = cumdays[month] + day; +#if DEBUG + fprintf(stderr, "day2: day %d(%d) yday %d\n", dayp, day, tp->tm_yday); +#endif + /* Speed up processing for the most common situation: yearly events + * when the interval being checked is less than a month or so (this + * could be less than a year, but then we have to start worrying about + * leap years). Only one event can match, and it's easy to find. + * Note we can't check special events, because they can wander widely. + */ + if (((v1 = offset + f_dayAfter) < 50) && (interval == YEARLY)) { + memcpy(&tmtmp, tp, sizeof(struct tm)); + tmtmp.tm_mday = dayp; + tmtmp.tm_mon = monthp - 1; + if (vwd) { + /* We want the event next year if it's late now + * this year. The 50-day limit means we don't have to + * worry if next year is or isn't a leap year. + */ + if (tp->tm_yday > 300 && tmtmp.tm_mon <= 1) + variable_weekday(&vwd, tmtmp.tm_mon + 1, + tmtmp.tm_year + TM_YEAR_BASE + 1); + else + variable_weekday(&vwd, tmtmp.tm_mon + 1, + tmtmp.tm_year + TM_YEAR_BASE); + day = cumdays[tmtmp.tm_mon + 1] + vwd; + tmtmp.tm_mday = vwd; + } + v2 = day - tp->tm_yday; + if ((v2 > v1) || (v2 < 0)) { + if ((v2 += isleap(tp->tm_year + TM_YEAR_BASE) ? 366 : 365) + <= v1) + tmtmp.tm_year++; + else if(!bodun || (day - tp->tm_yday) != -1) + return(NULL); + } + if ((tmp = malloc(sizeof(struct match))) == NULL) + err(1, NULL); - /* day, eg '22th' */ - v2 = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); + if (bodun && (day - tp->tm_yday) == -1) { + tmp->when = f_time - 1 * SECSPERDAY; + tmtmp.tm_mday++; + tmp->bodun = 1; + } else { + tmp->when = f_time + v2 * SECSPERDAY; + tmp->bodun = 0; + } - /* Hurrah! matched */ - if ( ((v2 - 1 + 7) / 7) == v1 ) - day = v2; + (void)mktime(&tmtmp); + if (strftime(tmp->print_date, + sizeof(tmp->print_date), + /* "%a %b %d", &tm); Skip weekdays */ + "%b %d", &tmtmp) == 0) + tmp->print_date[sizeof(tmp->print_date) - 1] = '\0'; - /* set to yesterday */ - else - day = tp->tm_mday - 1; + tmp->var = varp; + tmp->next = NULL; + return(tmp); } - } - - /* wired */ - else { - day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); - *varp = 1; - } } - - if (!(flags & F_EASTER)) { - *monthp = month; - *dayp = day; - day = cumdays[month] + day; - } else { - for (v1 = 0; day > cumdays[v1]; v1++) - ; - *monthp = v1 - 1; - *dayp = day - cumdays[v1 - 1]; - *varp = 1; + varp = 1; + /* Set up v1 to the event number and ... */ + v1 = vwd % (NUMEV + 1) - 1; + vwd /= (NUMEV + 1); + if (v1 < 0) { + v1 += NUMEV + 1; + vwd--; + } + dayp = monthp = 1; /* Why not */ } -#if DEBUG - fprintf(stderr, "day2: day %d(%d) yday %d\n", *dayp, day, tp->tm_yday); -#endif - /* if today or today + offset days */ - if (day >= tp->tm_yday - f_dayBefore && - day <= tp->tm_yday + offset + f_dayAfter) - return (1); - - /* if number of days left in this year + days to event in next year */ - if (yrdays - tp->tm_yday + day <= offset + f_dayAfter || - /* a year backward, eg. 6 Jan and 10 days before -> 27. Dec */ - tp->tm_yday + day - f_dayBefore < 0 - ) - return (1); - return (0); + /* Compare to past and coming instances of the event. The i == 0 part + * of the loop corresponds to this specific instance. Note that we + * can leave things sort of higgledy-piggledy since a mktime() happens + * on this before anything gets printed. Also note that even though + * we've effectively gotten rid of f_dayBefore, we still have to check + * the one prior event for situations like "the 31st of every month" + * and "yearly" events which could happen twice in one year but not in + * the next */ + tmp2 = matches; + for (i = -1; i < 2; i++) { + memcpy(&tmtmp, tp, sizeof(struct tm)); + tmtmp.tm_mday = dayp; + tmtmp.tm_mon = month = monthp - 1; + do { + v2 = 0; + switch (interval) { + case WEEKLY: + tmtmp.tm_mday += 7 * i; + break; + case MONTHLY: + month += i; + tmtmp.tm_mon = month; + switch(tmtmp.tm_mon) { + case -1: + tmtmp.tm_mon = month = 11; + tmtmp.tm_year--; + break; + case 12: + tmtmp.tm_mon = month = 0; + tmtmp.tm_year++; + break; + } + if (vwd) { + v1 = vwd; + variable_weekday(&v1, tmtmp.tm_mon + 1, + tmtmp.tm_year + TM_YEAR_BASE); + tmtmp.tm_mday = v1; + } else + tmtmp.tm_mday = dayp; + break; + case YEARLY: + default: + tmtmp.tm_year += i; + if (flags & F_SPECIAL) { + tmtmp.tm_mon = 0; /* Gee, mktime() is nice */ + tmtmp.tm_mday = spev[v1].getev(tmtmp.tm_year + + TM_YEAR_BASE) + vwd; + } else if (vwd) { + v1 = vwd; + variable_weekday(&v1, tmtmp.tm_mon + 1, + tmtmp.tm_year + TM_YEAR_BASE); + tmtmp.tm_mday = v1; + } else { + /* Need the following to keep Feb 29 from + * becoming Mar 1 */ + tmtmp.tm_mday = dayp; + tmtmp.tm_mon = monthp - 1; + } + break; + } + /* How many days apart are we */ + if ((ttmp = mktime(&tmtmp)) == -1) + warnx("time out of range: %s", endp); + else { + tdiff = difftime(ttmp, f_time)/ SECSPERDAY; + if (tdiff <= offset + f_dayAfter || + (bodun && tdiff == -1)) { + if (tdiff >= 0 || + (bodun && tdiff == -1)) { + if ((tmp = malloc(sizeof(struct match))) == NULL) + err(1, NULL); + tmp->when = ttmp; + if (strftime(tmp->print_date, + sizeof(tmp->print_date), + /* "%a %b %d", &tm); Skip weekdays */ + "%b %d", &tmtmp) == 0) + tmp->print_date[sizeof(tmp->print_date) - 1] = '\0'; + tmp->bodun = bodun && tdiff == -1; + tmp->var = varp; + tmp->next = NULL; + if (tmp2) + tmp2->next = tmp; + else + matches = tmp; + tmp2 = tmp; + v2 = (i == 1) ? 1 : 0; + } + } else + i = 2; /* No point checking in the future */ + } + } while (v2 != 0); + } + return (matches); } int getmonth(s) - register char *s; + char *s; { - register char **p; + char **p; struct fixs *n; for (n = fnmonths; n->name; ++n) @@ -418,9 +585,9 @@ int getday(s) - register char *s; + char *s; { - register char **p; + char **p; struct fixs *n; for (n = fndays; n->name; ++n) @@ -442,28 +609,24 @@ */ int getdayvar(s) - register char *s; + char *s; { - register int offset; + int offset; offset = strlen(s); - /* Sun+1 or Wednesday-2 * ^ ^ */ /* printf ("x: %s %s %d\n", s, s + offset - 2, offset); */ switch(*(s + offset - 2)) { case '-': - return(-(atoi(s + offset - 1))); - break; case '+': - return(atoi(s + offset - 1)); + return(atoi(s + offset - 2)); break; } - /* * some aliases: last, first, second, third, fourth */ @@ -480,7 +643,63 @@ else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth")) return(+4); - /* no offset detected */ return(0); +} + + +int +foy(year) + int year; +{ + /* 0-6; what weekday Jan 1 is */ + year--; + return ((1 - year/100 + year/400 + (int)(365.25 * year)) % 7); +} + + + +void +variable_weekday(day, month, year) + int *day, month, year; +{ + int v1, v2; + int *cumdays; + int day1; + + if (isleap(year)) + cumdays = daytab[1]; + else + cumdays = daytab[0]; + day1 = foy(year); + /* negative offset; last, -4 .. -1 */ + if (*day < 0) { + v1 = *day/10 - 1; /* offset -4 ... -1 */ + *day = 10 + (*day % 10); /* day 1 ... 7 */ + + /* which weekday the end of the month is (1-7) */ + v2 = (cumdays[month + 1] + day1) % 7 + 1; + + /* and subtract enough days */ + *day = cumdays[month + 1] - cumdays[month] + + (v1 + 1) * 7 - (v2 - *day + 7) % 7; +#if DEBUG + fprintf(stderr, "\nMonth %d ends on weekday %d\n", month, v2); +#endif + } + + /* first, second ... +1 ... +5 */ + else { + v1 = *day/10; /* offset */ + *day = *day % 10; + + /* which weekday the first of the month is (1-7) */ + v2 = (cumdays[month] + 1 + day1) % 7 + 1; + + /* and add enough days */ + *day = 1 + (v1 - 1) * 7 + (*day - v2 + 7) % 7; +#if DEBUG + fprintf(stderr, "\nMonth %d starts on weekday %d\n", month, v2); +#endif + } }