1*883b05efSmillert /* $OpenBSD: day.c,v 1.37 2019/08/12 20:03:28 millert Exp $ */
2b1fc8d4cSmillert
3b1fc8d4cSmillert /*
4b1fc8d4cSmillert * Copyright (c) 1989, 1993, 1994
5b1fc8d4cSmillert * The Regents of the University of California. All rights reserved.
6b1fc8d4cSmillert *
7b1fc8d4cSmillert * Redistribution and use in source and binary forms, with or without
8b1fc8d4cSmillert * modification, are permitted provided that the following conditions
9b1fc8d4cSmillert * are met:
10b1fc8d4cSmillert * 1. Redistributions of source code must retain the above copyright
11b1fc8d4cSmillert * notice, this list of conditions and the following disclaimer.
12b1fc8d4cSmillert * 2. Redistributions in binary form must reproduce the above copyright
13b1fc8d4cSmillert * notice, this list of conditions and the following disclaimer in the
14b1fc8d4cSmillert * documentation and/or other materials provided with the distribution.
15f75387cbSmillert * 3. Neither the name of the University nor the names of its contributors
16b1fc8d4cSmillert * may be used to endorse or promote products derived from this software
17b1fc8d4cSmillert * without specific prior written permission.
18b1fc8d4cSmillert *
19b1fc8d4cSmillert * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20b1fc8d4cSmillert * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21b1fc8d4cSmillert * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22b1fc8d4cSmillert * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23b1fc8d4cSmillert * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24b1fc8d4cSmillert * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25b1fc8d4cSmillert * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26b1fc8d4cSmillert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27b1fc8d4cSmillert * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28b1fc8d4cSmillert * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29b1fc8d4cSmillert * SUCH DAMAGE.
30b1fc8d4cSmillert */
31b1fc8d4cSmillert
32b1fc8d4cSmillert #include <sys/types.h>
33b1fc8d4cSmillert #include <sys/uio.h>
34b1fc8d4cSmillert
35b1fc8d4cSmillert #include <ctype.h>
36b1fc8d4cSmillert #include <err.h>
37b1fc8d4cSmillert #include <locale.h>
38b1fc8d4cSmillert #include <stdio.h>
39b1fc8d4cSmillert #include <stdlib.h>
40b1fc8d4cSmillert #include <string.h>
41b1fc8d4cSmillert #include <time.h>
42b1fc8d4cSmillert
43b1fc8d4cSmillert #include "pathnames.h"
44b1fc8d4cSmillert #include "calendar.h"
45b1fc8d4cSmillert
46b0589fbaSjsg extern struct iovec header[];
47b0589fbaSjsg
488a44d90fSpjanzen #define WEEKLY 1
498a44d90fSpjanzen #define MONTHLY 2
508a44d90fSpjanzen #define YEARLY 3
518a44d90fSpjanzen
52b1fc8d4cSmillert struct tm *tp;
538a44d90fSpjanzen int *cumdays, offset;
54b1fc8d4cSmillert char dayname[10];
55907f0a9fSmickey enum calendars calendar;
56907f0a9fSmickey u_long julian;
57b1fc8d4cSmillert
58b1fc8d4cSmillert
59b1fc8d4cSmillert /* 1-based month, 0-based days, cumulative */
60b1fc8d4cSmillert int daytab[][14] = {
61b1fc8d4cSmillert { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 },
62b1fc8d4cSmillert { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
63b1fc8d4cSmillert };
64b1fc8d4cSmillert
65b1fc8d4cSmillert static char *days[] = {
66b1fc8d4cSmillert "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
67b1fc8d4cSmillert };
68b1fc8d4cSmillert
69b1fc8d4cSmillert static char *months[] = {
70b1fc8d4cSmillert "jan", "feb", "mar", "apr", "may", "jun",
71b1fc8d4cSmillert "jul", "aug", "sep", "oct", "nov", "dec", NULL,
72b1fc8d4cSmillert };
73b1fc8d4cSmillert
74b1fc8d4cSmillert static struct fixs fndays[8]; /* full national days names */
75b1fc8d4cSmillert static struct fixs ndays[8]; /* short national days names */
76b1fc8d4cSmillert
77b1fc8d4cSmillert static struct fixs fnmonths[13]; /* full national months names */
78b1fc8d4cSmillert static struct fixs nmonths[13]; /* short national month names */
79b1fc8d4cSmillert
80638175c6Sderaadt void
fill_print_date(struct match * m,struct tm * tm)81ca2f2f78Sespie fill_print_date(struct match *m, struct tm *tm)
82ca2f2f78Sespie {
83ca2f2f78Sespie if (strftime(m->print_date, sizeof(m->print_date),
84ca2f2f78Sespie daynames ? "%a %b %d" : "%b %d", tm) == 0)
85ca2f2f78Sespie m->print_date[sizeof(m->print_date) - 1] = '\0';
86ca2f2f78Sespie }
87ca2f2f78Sespie
88ca2f2f78Sespie void
setnnames(void)89638175c6Sderaadt setnnames(void)
90b1fc8d4cSmillert {
91b1fc8d4cSmillert char buf[80];
92b1fc8d4cSmillert int i, l;
93b1fc8d4cSmillert struct tm tm;
94b1fc8d4cSmillert
95b1fc8d4cSmillert for (i = 0; i < 7; i++) {
96b1fc8d4cSmillert tm.tm_wday = i;
9744c6eb09Smillert l = strftime(buf, sizeof(buf), "%a", &tm);
986cd4fad2Sderaadt for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
99b1fc8d4cSmillert ;
100b1fc8d4cSmillert buf[l] = '\0';
101b1fc8d4cSmillert free(ndays[i].name);
102b1fc8d4cSmillert if ((ndays[i].name = strdup(buf)) == NULL)
103e454ee63Spjanzen err(1, NULL);
104b1fc8d4cSmillert ndays[i].len = strlen(buf);
105b1fc8d4cSmillert
10644c6eb09Smillert l = strftime(buf, sizeof(buf), "%A", &tm);
1076cd4fad2Sderaadt for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
108b1fc8d4cSmillert ;
109b1fc8d4cSmillert buf[l] = '\0';
110b1fc8d4cSmillert free(fndays[i].name);
111b1fc8d4cSmillert if ((fndays[i].name = strdup(buf)) == NULL)
112e454ee63Spjanzen err(1, NULL);
113b1fc8d4cSmillert fndays[i].len = strlen(buf);
114b1fc8d4cSmillert }
115b1fc8d4cSmillert
116b1fc8d4cSmillert for (i = 0; i < 12; i++) {
117b1fc8d4cSmillert tm.tm_mon = i;
11844c6eb09Smillert l = strftime(buf, sizeof(buf), "%b", &tm);
1196cd4fad2Sderaadt for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
120b1fc8d4cSmillert ;
121b1fc8d4cSmillert buf[l] = '\0';
122b1fc8d4cSmillert free(nmonths[i].name);
123b1fc8d4cSmillert if ((nmonths[i].name = strdup(buf)) == NULL)
124e454ee63Spjanzen err(1, NULL);
125b1fc8d4cSmillert nmonths[i].len = strlen(buf);
126b1fc8d4cSmillert
12744c6eb09Smillert l = strftime(buf, sizeof(buf), "%B", &tm);
1286cd4fad2Sderaadt for (; l > 0 && isspace((unsigned char)buf[l - 1]); l--)
129b1fc8d4cSmillert ;
130b1fc8d4cSmillert buf[l] = '\0';
131b1fc8d4cSmillert free(fnmonths[i].name);
132b1fc8d4cSmillert if ((fnmonths[i].name = strdup(buf)) == NULL)
133e454ee63Spjanzen err(1, NULL);
134b1fc8d4cSmillert fnmonths[i].len = strlen(buf);
135b1fc8d4cSmillert }
1368a44d90fSpjanzen /* Hardwired special events */
137eaf2975cSmickey spev[0].name = strdup(PESACH);
138eaf2975cSmickey spev[0].nlen = PESACHLEN;
139eaf2975cSmickey spev[0].getev = pesach;
140eaf2975cSmickey spev[1].name = strdup(EASTER);
141eaf2975cSmickey spev[1].nlen = EASTERNAMELEN;
142eaf2975cSmickey spev[1].getev = easter;
143eaf2975cSmickey spev[2].name = strdup(PASKHA);
144eaf2975cSmickey spev[2].nlen = PASKHALEN;
145eaf2975cSmickey spev[2].getev = paskha;
1468a44d90fSpjanzen for (i = 0; i < NUMEV; i++) {
1478a44d90fSpjanzen if (spev[i].name == NULL)
148e454ee63Spjanzen err(1, NULL);
1498a44d90fSpjanzen spev[i].uname = NULL;
1508a44d90fSpjanzen }
151b1fc8d4cSmillert }
152b1fc8d4cSmillert
153b1fc8d4cSmillert void
settime(time_t * now)154638175c6Sderaadt settime(time_t *now)
155b1fc8d4cSmillert {
1568a44d90fSpjanzen tp = localtime(now);
1578a44d90fSpjanzen tp->tm_sec = 0;
1588a44d90fSpjanzen tp->tm_min = 0;
1598a44d90fSpjanzen /* Avoid getting caught by a timezone shift; set time to noon */
1608a44d90fSpjanzen tp->tm_isdst = 0;
1618a44d90fSpjanzen tp->tm_hour = 12;
1628a44d90fSpjanzen *now = mktime(tp);
16378badebcSmillert if (isleap(tp->tm_year + 1900))
164b1fc8d4cSmillert cumdays = daytab[1];
1658a44d90fSpjanzen else
166b1fc8d4cSmillert cumdays = daytab[0];
167b1fc8d4cSmillert /* Friday displays Monday's events */
168b1fc8d4cSmillert offset = tp->tm_wday == 5 ? 3 : 1;
169db809114Smillert if (f_Setday)
1708a44d90fSpjanzen offset = 0; /* Except not when range is set explicitly */
171b1fc8d4cSmillert header[5].iov_base = dayname;
172b1fc8d4cSmillert
173b1fc8d4cSmillert (void) setlocale(LC_TIME, "C");
174b1fc8d4cSmillert header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
175b1fc8d4cSmillert (void) setlocale(LC_TIME, "");
176b1fc8d4cSmillert
177b1fc8d4cSmillert setnnames();
178b1fc8d4cSmillert }
179b1fc8d4cSmillert
180c60f2790Smillert /* convert [Year][Month]Day into unix time (since 1970)
181c60f2790Smillert * Year: two or four digits, Month: two digits, Day: two digits
182b1fc8d4cSmillert */
183638175c6Sderaadt time_t
Mktime(char * date)184638175c6Sderaadt Mktime(char *date)
185b1fc8d4cSmillert {
186b1fc8d4cSmillert time_t t;
187b1fc8d4cSmillert int len;
188b1fc8d4cSmillert struct tm tm;
189b1fc8d4cSmillert
190b1fc8d4cSmillert (void)time(&t);
191b1fc8d4cSmillert tp = localtime(&t);
192b1fc8d4cSmillert
193b1fc8d4cSmillert len = strlen(date);
194c60f2790Smillert if (len < 2)
195c60f2790Smillert return((time_t)-1);
1964486f476Sderaadt bzero(&tm, sizeof tm);
197b1fc8d4cSmillert tm.tm_sec = 0;
198b1fc8d4cSmillert tm.tm_min = 0;
1990b02000dSpjanzen /* Avoid getting caught by a timezone shift; set time to noon */
2000b02000dSpjanzen tm.tm_isdst = 0;
2010b02000dSpjanzen tm.tm_hour = 12;
202b1fc8d4cSmillert tm.tm_wday = 0;
203b1fc8d4cSmillert tm.tm_mday = tp->tm_mday;
204b1fc8d4cSmillert tm.tm_mon = tp->tm_mon;
205b1fc8d4cSmillert tm.tm_year = tp->tm_year;
206b1fc8d4cSmillert
207c60f2790Smillert /* Day */
208c60f2790Smillert tm.tm_mday = atoi(date + len - 2);
209b1fc8d4cSmillert
210c60f2790Smillert /* Month */
211b1fc8d4cSmillert if (len >= 4) {
212c60f2790Smillert *(date + len - 2) = '\0';
213c60f2790Smillert tm.tm_mon = atoi(date + len - 4) - 1;
214b1fc8d4cSmillert }
215b1fc8d4cSmillert
216b1fc8d4cSmillert /* Year */
2170b02000dSpjanzen if (len >= 6) {
218c60f2790Smillert *(date + len - 4) = '\0';
219c60f2790Smillert tm.tm_year = atoi(date);
220b1fc8d4cSmillert
2210b02000dSpjanzen if (tm.tm_year < 69) /* Y2K */
22278badebcSmillert tm.tm_year += 100;
22378badebcSmillert else if (tm.tm_year > 1900)
22478badebcSmillert tm.tm_year -= 1900;
225b1fc8d4cSmillert }
226b1fc8d4cSmillert
227b1fc8d4cSmillert #if DEBUG
228fab9a59cSderaadt printf("Mktime: %d %lld %d %s\n", (int)mktime(&tm), (long long)t, len,
229b1fc8d4cSmillert asctime(&tm));
230b1fc8d4cSmillert #endif
231b1fc8d4cSmillert return(mktime(&tm));
232b1fc8d4cSmillert }
233b1fc8d4cSmillert
234babb7faeSderaadt static void
adjust_calendar(int * day,int * month)235907f0a9fSmickey adjust_calendar(int *day, int *month)
236907f0a9fSmickey {
237907f0a9fSmickey switch (calendar) {
238907f0a9fSmickey case GREGORIAN:
239907f0a9fSmickey break;
240907f0a9fSmickey
241907f0a9fSmickey case JULIAN:
242907f0a9fSmickey *day += julian;
243907f0a9fSmickey if (*day > (cumdays[*month + 1] - cumdays[*month])) {
244907f0a9fSmickey *day -= (cumdays[*month + 1] - cumdays[*month]);
245907f0a9fSmickey if (++*month > 12)
246907f0a9fSmickey *month = 1;
247907f0a9fSmickey }
248907f0a9fSmickey break;
249907f0a9fSmickey case LUNAR:
250907f0a9fSmickey break;
251907f0a9fSmickey }
252907f0a9fSmickey }
253907f0a9fSmickey
254b1fc8d4cSmillert /*
255b1fc8d4cSmillert * Possible date formats include any combination of:
256b1fc8d4cSmillert * 3-charmonth (January, Jan, Jan)
257b1fc8d4cSmillert * 3-charweekday (Friday, Monday, mon.)
258b1fc8d4cSmillert * numeric month or day (1, 2, 04)
259b1fc8d4cSmillert *
2601daff509Spjanzen * Any character except \t or '*' may separate them, or they may not be
2611daff509Spjanzen * separated. Any line following a line that is matched, that starts
2621daff509Spjanzen * with \t, is shown along with the matched line.
263b1fc8d4cSmillert */
2644185b97eSpjanzen struct match *
isnow(char * endp,int bodun)265638175c6Sderaadt isnow(char *endp, int bodun)
266b1fc8d4cSmillert {
2678a44d90fSpjanzen int day = 0, flags = 0, month = 0, v1, v2, i;
2688a44d90fSpjanzen int monthp, dayp, varp = 0;
2698a44d90fSpjanzen struct match *matches = NULL, *tmp, *tmp2;
2708a44d90fSpjanzen int interval = YEARLY; /* how frequently the event repeats. */
2718a44d90fSpjanzen int vwd = 0; /* Variable weekday */
2728a44d90fSpjanzen time_t tdiff, ttmp;
2738a44d90fSpjanzen struct tm tmtmp;
274b1fc8d4cSmillert
275b1fc8d4cSmillert /*
276b1fc8d4cSmillert * CONVENTION
277b1fc8d4cSmillert *
278b1fc8d4cSmillert * Month: 1-12
279b1fc8d4cSmillert * Monthname: Jan .. Dec
280b1fc8d4cSmillert * Day: 1-31
281b1fc8d4cSmillert * Weekday: Mon-Sun
282b1fc8d4cSmillert *
283b1fc8d4cSmillert */
284b1fc8d4cSmillert
285b1fc8d4cSmillert /* read first field */
286b1fc8d4cSmillert /* didn't recognize anything, skip it */
287b1fc8d4cSmillert if (!(v1 = getfield(endp, &endp, &flags)))
2884185b97eSpjanzen return (NULL);
289b1fc8d4cSmillert
2900eb5bb76Smickey /* adjust bodun rate */
2910eb5bb76Smickey if (bodun && !bodun_always)
29266ad965fSdjm bodun = !arc4random_uniform(3);
2930eb5bb76Smickey
294b1fc8d4cSmillert /* Easter or Easter depending days */
2958a44d90fSpjanzen if (flags & F_SPECIAL)
2968a44d90fSpjanzen vwd = v1;
297b1fc8d4cSmillert
298b1fc8d4cSmillert /*
299b1fc8d4cSmillert * 1. {Weekday,Day} XYZ ...
300b1fc8d4cSmillert *
301b1fc8d4cSmillert * where Day is > 12
302b1fc8d4cSmillert */
303b1fc8d4cSmillert else if (flags & F_ISDAY || v1 > 12) {
304b1fc8d4cSmillert
3058a44d90fSpjanzen /* found a day; day: 13-31 or weekday: 1-7 */
306b1fc8d4cSmillert day = v1;
307b1fc8d4cSmillert
308b1fc8d4cSmillert /* {Day,Weekday} {Month,Monthname} ... */
3098a44d90fSpjanzen /* if no recognizable month, assume just a day alone -- this is
3108a44d90fSpjanzen * very unlikely and can only happen after the first 12 days.
3118a44d90fSpjanzen * --find month or use current month */
3128a44d90fSpjanzen if (!(month = getfield(endp, &endp, &flags))) {
313b1fc8d4cSmillert month = tp->tm_mon + 1;
3148a44d90fSpjanzen /* F_ISDAY is set only if a weekday was spelled out */
3158a44d90fSpjanzen /* F_ISDAY must be set if 0 < day < 8 */
3168a44d90fSpjanzen if ((day <= 7) && (day >= 1))
3178a44d90fSpjanzen interval = WEEKLY;
3188a44d90fSpjanzen else
3198a44d90fSpjanzen interval = MONTHLY;
3208a44d90fSpjanzen } else if ((day <= 7) && (day >= 1))
3218a44d90fSpjanzen day += 10;
3228a44d90fSpjanzen /* it's a weekday; make it the first one of the month */
3238a44d90fSpjanzen if (month == -1) {
3248a44d90fSpjanzen month = tp->tm_mon + 1;
3258a44d90fSpjanzen interval = MONTHLY;
326*883b05efSmillert } else {
3278a44d90fSpjanzen if ((month > 12) || (month < 1))
3288a44d90fSpjanzen return (NULL);
329*883b05efSmillert if (calendar)
330*883b05efSmillert adjust_calendar(&day, &month);
331*883b05efSmillert }
332b1fc8d4cSmillert }
333b1fc8d4cSmillert
334b1fc8d4cSmillert /* 2. {Monthname} XYZ ... */
335b1fc8d4cSmillert else if (flags & F_ISMONTH) {
336b1fc8d4cSmillert month = v1;
3378a44d90fSpjanzen if (month == -1) {
3388a44d90fSpjanzen month = tp->tm_mon + 1;
3398a44d90fSpjanzen interval = MONTHLY;
3408a44d90fSpjanzen }
341b1fc8d4cSmillert /* Monthname {day,weekday} */
342b1fc8d4cSmillert /* if no recognizable day, assume the first day in month */
343b1fc8d4cSmillert if (!(day = getfield(endp, &endp, &flags)))
344b1fc8d4cSmillert day = 1;
3458a44d90fSpjanzen /* If a weekday was spelled out without an ordering,
3468a44d90fSpjanzen * assume the first of that day in the month */
347907f0a9fSmickey if ((flags & F_ISDAY)) {
348907f0a9fSmickey if ((day >= 1) && (day <=7))
3498a44d90fSpjanzen day += 10;
350907f0a9fSmickey } else if (calendar)
351907f0a9fSmickey adjust_calendar(&day, &month);
352b1fc8d4cSmillert }
353b1fc8d4cSmillert
354b1fc8d4cSmillert /* Hm ... */
355b1fc8d4cSmillert else {
356b1fc8d4cSmillert v2 = getfield(endp, &endp, &flags);
357b1fc8d4cSmillert
358b1fc8d4cSmillert /*
359b1fc8d4cSmillert * {Day} {Monthname} ...
360b1fc8d4cSmillert * where Day <= 12
361b1fc8d4cSmillert */
362b1fc8d4cSmillert if (flags & F_ISMONTH) {
363b1fc8d4cSmillert day = v1;
364b1fc8d4cSmillert month = v2;
3658a44d90fSpjanzen if (month == -1) {
3668a44d90fSpjanzen month = tp->tm_mon + 1;
3678a44d90fSpjanzen interval = MONTHLY;
368907f0a9fSmickey } else if (calendar)
369907f0a9fSmickey adjust_calendar(&day, &month);
370b1fc8d4cSmillert }
371b1fc8d4cSmillert
372b1fc8d4cSmillert /* {Month} {Weekday,Day} ... */
373b1fc8d4cSmillert else {
374b1fc8d4cSmillert /* F_ISDAY set, v2 > 12, or no way to tell */
375b1fc8d4cSmillert month = v1;
376*883b05efSmillert if ((month > 12) || (month < 1))
377*883b05efSmillert return (NULL);
378b1fc8d4cSmillert /* if no recognizable day, assume the first */
379b1fc8d4cSmillert day = v2 ? v2 : 1;
380907f0a9fSmickey if ((flags & F_ISDAY)) {
381907f0a9fSmickey if ((day >= 1) && (day <= 7))
3828a44d90fSpjanzen day += 10;
383907f0a9fSmickey } else
384907f0a9fSmickey adjust_calendar(&day, &month);
385b1fc8d4cSmillert }
386b1fc8d4cSmillert }
387b1fc8d4cSmillert
388b1fc8d4cSmillert /* convert Weekday into *next* Day,
389b1fc8d4cSmillert * e.g.: 'Sunday' -> 22
3900b02000dSpjanzen * 'SundayLast' -> ??
391b1fc8d4cSmillert */
392b1fc8d4cSmillert if (flags & F_ISDAY) {
393b1fc8d4cSmillert #if DEBUG
394b1fc8d4cSmillert fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month);
395b1fc8d4cSmillert #endif
396b1fc8d4cSmillert
3974185b97eSpjanzen varp = 1;
398b1fc8d4cSmillert /* variable weekday, SundayLast, MondayFirst ... */
3998a44d90fSpjanzen if (day < 0 || day >= 10)
4008a44d90fSpjanzen vwd = day;
401b1fc8d4cSmillert else {
4024185b97eSpjanzen day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
4038a44d90fSpjanzen interval = WEEKLY;
4048a44d90fSpjanzen }
4058a44d90fSpjanzen } else
4068a44d90fSpjanzen /* Check for silliness. Note we still catch Feb 29 */
4078a44d90fSpjanzen if (!(flags & F_SPECIAL) &&
4088a44d90fSpjanzen (day > (cumdays[month + 1] - cumdays[month]) || day < 1)) {
4098a44d90fSpjanzen if (!((month == 2 && day == 29) ||
4108a44d90fSpjanzen (interval == MONTHLY && day <= 31)))
4118a44d90fSpjanzen return (NULL);
412b1fc8d4cSmillert }
413b1fc8d4cSmillert
4148a44d90fSpjanzen if (!(flags & F_SPECIAL)) {
4154185b97eSpjanzen monthp = month;
4164185b97eSpjanzen dayp = day;
417b1fc8d4cSmillert day = cumdays[month] + day;
418b1fc8d4cSmillert #if DEBUG
4194185b97eSpjanzen fprintf(stderr, "day2: day %d(%d) yday %d\n", dayp, day, tp->tm_yday);
420b1fc8d4cSmillert #endif
4218a44d90fSpjanzen /* Speed up processing for the most common situation: yearly events
4228a44d90fSpjanzen * when the interval being checked is less than a month or so (this
4238a44d90fSpjanzen * could be less than a year, but then we have to start worrying about
4248a44d90fSpjanzen * leap years). Only one event can match, and it's easy to find.
4258a44d90fSpjanzen * Note we can't check special events, because they can wander widely.
4268a44d90fSpjanzen */
4278a44d90fSpjanzen if (((v1 = offset + f_dayAfter) < 50) && (interval == YEARLY)) {
4288a44d90fSpjanzen memcpy(&tmtmp, tp, sizeof(struct tm));
4298a44d90fSpjanzen tmtmp.tm_mday = dayp;
4308a44d90fSpjanzen tmtmp.tm_mon = monthp - 1;
4318a9bb311Spjanzen if (vwd) {
4328a9bb311Spjanzen /* We want the event next year if it's late now
4338a9bb311Spjanzen * this year. The 50-day limit means we don't have to
4348a9bb311Spjanzen * worry if next year is or isn't a leap year.
4358a9bb311Spjanzen */
4368a9bb311Spjanzen if (tp->tm_yday > 300 && tmtmp.tm_mon <= 1)
4378a9bb311Spjanzen variable_weekday(&vwd, tmtmp.tm_mon + 1,
43878badebcSmillert tmtmp.tm_year + 1900 + 1);
4398a9bb311Spjanzen else
4408a9bb311Spjanzen variable_weekday(&vwd, tmtmp.tm_mon + 1,
44178badebcSmillert tmtmp.tm_year + 1900);
4428a9bb311Spjanzen day = cumdays[tmtmp.tm_mon + 1] + vwd;
4438a9bb311Spjanzen tmtmp.tm_mday = vwd;
4448a9bb311Spjanzen }
4458a44d90fSpjanzen v2 = day - tp->tm_yday;
4468a44d90fSpjanzen if ((v2 > v1) || (v2 < 0)) {
44778badebcSmillert if ((v2 += isleap(tp->tm_year + 1900) ? 366 : 365)
4488a44d90fSpjanzen <= v1)
4498a44d90fSpjanzen tmtmp.tm_year++;
4500eb5bb76Smickey else if(!bodun || (day - tp->tm_yday) != -1)
4514185b97eSpjanzen return(NULL);
452b1fc8d4cSmillert }
4538a44d90fSpjanzen if ((tmp = malloc(sizeof(struct match))) == NULL)
454e454ee63Spjanzen err(1, NULL);
455a807d8f6Smickey
456a807d8f6Smickey if (bodun && (day - tp->tm_yday) == -1) {
457a807d8f6Smickey tmp->when = f_time - 1 * SECSPERDAY;
458a807d8f6Smickey tmtmp.tm_mday++;
459a807d8f6Smickey tmp->bodun = 1;
460a807d8f6Smickey } else {
4618a44d90fSpjanzen tmp->when = f_time + v2 * SECSPERDAY;
462a807d8f6Smickey tmp->bodun = 0;
463a807d8f6Smickey }
464a807d8f6Smickey
4658a44d90fSpjanzen (void)mktime(&tmtmp);
466ca2f2f78Sespie fill_print_date(tmp, &tmtmp);
4678a44d90fSpjanzen tmp->var = varp;
4688a44d90fSpjanzen tmp->next = NULL;
4698a44d90fSpjanzen return(tmp);
4708a44d90fSpjanzen }
471907f0a9fSmickey } else {
4728a44d90fSpjanzen varp = 1;
4738a44d90fSpjanzen /* Set up v1 to the event number and ... */
4748a44d90fSpjanzen v1 = vwd % (NUMEV + 1) - 1;
4758a44d90fSpjanzen vwd /= (NUMEV + 1);
4768a44d90fSpjanzen if (v1 < 0) {
4778a44d90fSpjanzen v1 += NUMEV + 1;
4788a44d90fSpjanzen vwd--;
4798a44d90fSpjanzen }
4808a44d90fSpjanzen dayp = monthp = 1; /* Why not */
4818a44d90fSpjanzen }
4828a44d90fSpjanzen
4838a44d90fSpjanzen /* Compare to past and coming instances of the event. The i == 0 part
4848a44d90fSpjanzen * of the loop corresponds to this specific instance. Note that we
4858a44d90fSpjanzen * can leave things sort of higgledy-piggledy since a mktime() happens
4868a44d90fSpjanzen * on this before anything gets printed. Also note that even though
4878a44d90fSpjanzen * we've effectively gotten rid of f_dayBefore, we still have to check
4888a44d90fSpjanzen * the one prior event for situations like "the 31st of every month"
4898a44d90fSpjanzen * and "yearly" events which could happen twice in one year but not in
4908a44d90fSpjanzen * the next */
4918a44d90fSpjanzen tmp2 = matches;
4928a44d90fSpjanzen for (i = -1; i < 2; i++) {
4938a44d90fSpjanzen memcpy(&tmtmp, tp, sizeof(struct tm));
4948a44d90fSpjanzen tmtmp.tm_mday = dayp;
4958a44d90fSpjanzen tmtmp.tm_mon = month = monthp - 1;
4968a44d90fSpjanzen do {
4978a44d90fSpjanzen v2 = 0;
4988a44d90fSpjanzen switch (interval) {
4998a44d90fSpjanzen case WEEKLY:
5008a44d90fSpjanzen tmtmp.tm_mday += 7 * i;
5018a44d90fSpjanzen break;
5028a44d90fSpjanzen case MONTHLY:
5038a44d90fSpjanzen month += i;
5048a44d90fSpjanzen tmtmp.tm_mon = month;
5058a44d90fSpjanzen switch(tmtmp.tm_mon) {
5068a44d90fSpjanzen case -1:
5078a44d90fSpjanzen tmtmp.tm_mon = month = 11;
5088a44d90fSpjanzen tmtmp.tm_year--;
5098a44d90fSpjanzen break;
5108a44d90fSpjanzen case 12:
5118a44d90fSpjanzen tmtmp.tm_mon = month = 0;
5128a44d90fSpjanzen tmtmp.tm_year++;
5138a44d90fSpjanzen break;
5148a44d90fSpjanzen }
5158a44d90fSpjanzen if (vwd) {
5168a44d90fSpjanzen v1 = vwd;
5178a44d90fSpjanzen variable_weekday(&v1, tmtmp.tm_mon + 1,
51878badebcSmillert tmtmp.tm_year + 1900);
5198a44d90fSpjanzen tmtmp.tm_mday = v1;
5208a44d90fSpjanzen } else
5218a44d90fSpjanzen tmtmp.tm_mday = dayp;
5228a44d90fSpjanzen break;
5238a44d90fSpjanzen case YEARLY:
5248a44d90fSpjanzen default:
5258a44d90fSpjanzen tmtmp.tm_year += i;
5268a44d90fSpjanzen if (flags & F_SPECIAL) {
5278a44d90fSpjanzen tmtmp.tm_mon = 0; /* Gee, mktime() is nice */
5288a44d90fSpjanzen tmtmp.tm_mday = spev[v1].getev(tmtmp.tm_year +
52978badebcSmillert 1900) + vwd;
5308a44d90fSpjanzen } else if (vwd) {
5318a44d90fSpjanzen v1 = vwd;
5328a44d90fSpjanzen variable_weekday(&v1, tmtmp.tm_mon + 1,
53378badebcSmillert tmtmp.tm_year + 1900);
5348a44d90fSpjanzen tmtmp.tm_mday = v1;
5358a44d90fSpjanzen } else {
5368a44d90fSpjanzen /* Need the following to keep Feb 29 from
5378a44d90fSpjanzen * becoming Mar 1 */
5388a44d90fSpjanzen tmtmp.tm_mday = dayp;
5398a44d90fSpjanzen tmtmp.tm_mon = monthp - 1;
5408a44d90fSpjanzen }
5418a44d90fSpjanzen break;
5428a44d90fSpjanzen }
5438a44d90fSpjanzen /* How many days apart are we */
5448a44d90fSpjanzen if ((ttmp = mktime(&tmtmp)) == -1)
5458a44d90fSpjanzen warnx("time out of range: %s", endp);
5468a44d90fSpjanzen else {
5478a44d90fSpjanzen tdiff = difftime(ttmp, f_time)/ SECSPERDAY;
5480eb5bb76Smickey if (tdiff <= offset + f_dayAfter ||
5490eb5bb76Smickey (bodun && tdiff == -1)) {
55052592e37Smillert if (((tmtmp.tm_mon == month) ||
55152592e37Smillert (flags & F_SPECIAL) ||
55252592e37Smillert (interval == WEEKLY)) &&
55345cf8591Smillert (tdiff >= 0 ||
55445cf8591Smillert (bodun && tdiff == -1))) {
5558a44d90fSpjanzen if ((tmp = malloc(sizeof(struct match))) == NULL)
556e454ee63Spjanzen err(1, NULL);
5578a44d90fSpjanzen tmp->when = ttmp;
558ca2f2f78Sespie fill_print_date(tmp, &tmtmp);
559a807d8f6Smickey tmp->bodun = bodun && tdiff == -1;
5608a44d90fSpjanzen tmp->var = varp;
5618a44d90fSpjanzen tmp->next = NULL;
5628a44d90fSpjanzen if (tmp2)
5638a44d90fSpjanzen tmp2->next = tmp;
5648a44d90fSpjanzen else
5658a44d90fSpjanzen matches = tmp;
5668a44d90fSpjanzen tmp2 = tmp;
5678a44d90fSpjanzen v2 = (i == 1) ? 1 : 0;
5688a44d90fSpjanzen }
5698a44d90fSpjanzen } else
5708a44d90fSpjanzen i = 2; /* No point checking in the future */
5718a44d90fSpjanzen }
5728a44d90fSpjanzen } while (v2 != 0);
5738a44d90fSpjanzen }
5748a44d90fSpjanzen return (matches);
5758a44d90fSpjanzen }
576b1fc8d4cSmillert
577b1fc8d4cSmillert
578b1fc8d4cSmillert int
getmonth(char * s)579638175c6Sderaadt getmonth(char *s)
580b1fc8d4cSmillert {
581c0932ef1Smpech char **p;
582b1fc8d4cSmillert struct fixs *n;
583b1fc8d4cSmillert
584b1fc8d4cSmillert for (n = fnmonths; n->name; ++n)
585b1fc8d4cSmillert if (!strncasecmp(s, n->name, n->len))
586b1fc8d4cSmillert return ((n - fnmonths) + 1);
587b1fc8d4cSmillert for (n = nmonths; n->name; ++n)
588b1fc8d4cSmillert if (!strncasecmp(s, n->name, n->len))
589b1fc8d4cSmillert return ((n - nmonths) + 1);
590b1fc8d4cSmillert for (p = months; *p; ++p)
591b1fc8d4cSmillert if (!strncasecmp(s, *p, 3))
592b1fc8d4cSmillert return ((p - months) + 1);
593b1fc8d4cSmillert return (0);
594b1fc8d4cSmillert }
595b1fc8d4cSmillert
596b1fc8d4cSmillert
597b1fc8d4cSmillert int
getday(char * s)598638175c6Sderaadt getday(char *s)
599b1fc8d4cSmillert {
600c0932ef1Smpech char **p;
601b1fc8d4cSmillert struct fixs *n;
602b1fc8d4cSmillert
603b1fc8d4cSmillert for (n = fndays; n->name; ++n)
604b1fc8d4cSmillert if (!strncasecmp(s, n->name, n->len))
605b1fc8d4cSmillert return ((n - fndays) + 1);
606b1fc8d4cSmillert for (n = ndays; n->name; ++n)
607b1fc8d4cSmillert if (!strncasecmp(s, n->name, n->len))
608b1fc8d4cSmillert return ((n - ndays) + 1);
609b1fc8d4cSmillert for (p = days; *p; ++p)
610b1fc8d4cSmillert if (!strncasecmp(s, *p, 3))
611b1fc8d4cSmillert return ((p - days) + 1);
612b1fc8d4cSmillert return (0);
613b1fc8d4cSmillert }
614b1fc8d4cSmillert
615b1fc8d4cSmillert /* return offset for variable weekdays
616b1fc8d4cSmillert * -1 -> last weekday in month
617b1fc8d4cSmillert * +1 -> first weekday in month
618b1fc8d4cSmillert * ... etc ...
619b1fc8d4cSmillert */
620b1fc8d4cSmillert int
getdayvar(char * s)621638175c6Sderaadt getdayvar(char *s)
622b1fc8d4cSmillert {
623c0932ef1Smpech int offset;
624b1fc8d4cSmillert
625b1fc8d4cSmillert
626b1fc8d4cSmillert offset = strlen(s);
627b1fc8d4cSmillert
628b1fc8d4cSmillert /* Sun+1 or Wednesday-2
629b1fc8d4cSmillert * ^ ^ */
630b1fc8d4cSmillert
631b1fc8d4cSmillert /* printf ("x: %s %s %d\n", s, s + offset - 2, offset); */
632b1fc8d4cSmillert switch(*(s + offset - 2)) {
633b1fc8d4cSmillert case '-':
634b1fc8d4cSmillert case '+':
6358a44d90fSpjanzen return(atoi(s + offset - 2));
636b1fc8d4cSmillert break;
637b1fc8d4cSmillert }
638b1fc8d4cSmillert
639b1fc8d4cSmillert /*
640b1fc8d4cSmillert * some aliases: last, first, second, third, fourth
641b1fc8d4cSmillert */
642b1fc8d4cSmillert
643b1fc8d4cSmillert /* last */
644b1fc8d4cSmillert if (offset > 4 && !strcasecmp(s + offset - 4, "last"))
645b1fc8d4cSmillert return(-1);
646b1fc8d4cSmillert else if (offset > 5 && !strcasecmp(s + offset - 5, "first"))
647b1fc8d4cSmillert return(+1);
648b1fc8d4cSmillert else if (offset > 6 && !strcasecmp(s + offset - 6, "second"))
649b1fc8d4cSmillert return(+2);
650b1fc8d4cSmillert else if (offset > 5 && !strcasecmp(s + offset - 5, "third"))
651b1fc8d4cSmillert return(+3);
652b1fc8d4cSmillert else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth"))
653b1fc8d4cSmillert return(+4);
654b1fc8d4cSmillert
655b1fc8d4cSmillert /* no offset detected */
656b1fc8d4cSmillert return(0);
657b1fc8d4cSmillert }
6588a44d90fSpjanzen
6598a44d90fSpjanzen
6608a44d90fSpjanzen int
foy(int year)661638175c6Sderaadt foy(int year)
6628a44d90fSpjanzen {
6638a44d90fSpjanzen /* 0-6; what weekday Jan 1 is */
6648a44d90fSpjanzen year--;
6658a44d90fSpjanzen return ((1 - year/100 + year/400 + (int)(365.25 * year)) % 7);
6668a44d90fSpjanzen }
6678a44d90fSpjanzen
6688a44d90fSpjanzen
6698a44d90fSpjanzen
6708a44d90fSpjanzen void
variable_weekday(int * day,int month,int year)671638175c6Sderaadt variable_weekday(int *day, int month, int year)
6728a44d90fSpjanzen {
6738a44d90fSpjanzen int v1, v2;
6748a44d90fSpjanzen int *cumdays;
6758a44d90fSpjanzen int day1;
6768a44d90fSpjanzen
6778a44d90fSpjanzen if (isleap(year))
6788a44d90fSpjanzen cumdays = daytab[1];
6798a44d90fSpjanzen else
6808a44d90fSpjanzen cumdays = daytab[0];
6818a44d90fSpjanzen day1 = foy(year);
6828a44d90fSpjanzen /* negative offset; last, -4 .. -1 */
6838a44d90fSpjanzen if (*day < 0) {
6848a44d90fSpjanzen v1 = *day/10 - 1; /* offset -4 ... -1 */
6858a44d90fSpjanzen *day = 10 + (*day % 10); /* day 1 ... 7 */
6868a44d90fSpjanzen
6878a44d90fSpjanzen /* which weekday the end of the month is (1-7) */
6888a44d90fSpjanzen v2 = (cumdays[month + 1] + day1) % 7 + 1;
6898a44d90fSpjanzen
6908a44d90fSpjanzen /* and subtract enough days */
6918a44d90fSpjanzen *day = cumdays[month + 1] - cumdays[month] +
6928a44d90fSpjanzen (v1 + 1) * 7 - (v2 - *day + 7) % 7;
6938a44d90fSpjanzen #if DEBUG
6948a44d90fSpjanzen fprintf(stderr, "\nMonth %d ends on weekday %d\n", month, v2);
6958a44d90fSpjanzen #endif
6968a44d90fSpjanzen }
6978a44d90fSpjanzen
6988a44d90fSpjanzen /* first, second ... +1 ... +5 */
6998a44d90fSpjanzen else {
7008a44d90fSpjanzen v1 = *day/10; /* offset */
7018a44d90fSpjanzen *day = *day % 10;
7028a44d90fSpjanzen
7038a44d90fSpjanzen /* which weekday the first of the month is (1-7) */
7048a44d90fSpjanzen v2 = (cumdays[month] + 1 + day1) % 7 + 1;
7058a44d90fSpjanzen
7068a44d90fSpjanzen /* and add enough days */
7078a44d90fSpjanzen *day = 1 + (v1 - 1) * 7 + (*day - v2 + 7) % 7;
7088a44d90fSpjanzen #if DEBUG
7098a44d90fSpjanzen fprintf(stderr, "\nMonth %d starts on weekday %d\n", month, v2);
7108a44d90fSpjanzen #endif
7118a44d90fSpjanzen }
7128a44d90fSpjanzen }
713