1205821Sedwin/*-
2205872Sedwin * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>.
3205872Sedwin * All rights reserved.
4205821Sedwin *
5205821Sedwin * Redistribution and use in source and binary forms, with or without
6205821Sedwin * modification, are permitted provided that the following conditions
7205821Sedwin * are met:
8205821Sedwin * 1. Redistributions of source code must retain the above copyright
9205821Sedwin *    notice, this list of conditions and the following disclaimer.
10205821Sedwin * 2. Redistributions in binary form must reproduce the above copyright
11205821Sedwin *    notice, this list of conditions and the following disclaimer in the
12205821Sedwin *    documentation and/or other materials provided with the distribution.
13251647Sgrog *
14205821Sedwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15205821Sedwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16205821Sedwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17205821Sedwin * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18205821Sedwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19205821Sedwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20205821Sedwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21205821Sedwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22205821Sedwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23205821Sedwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24205821Sedwin * SUCH DAMAGE.
25251647Sgrog *
26205821Sedwin */
27205821Sedwin
28205821Sedwin#include <sys/cdefs.h>
29205821Sedwin__FBSDID("$FreeBSD$");
30205821Sedwin
31205821Sedwin#include <stdio.h>
32205821Sedwin#include <stdlib.h>
33205821Sedwin#include <err.h>
34205821Sedwin#include <time.h>
35205821Sedwin
36205821Sedwin#include "calendar.h"
37205821Sedwin
38205821Sedwinstruct cal_year {
39205821Sedwin	int year;	/* 19xx, 20xx, 21xx */
40205821Sedwin	int easter;	/* Julian day */
41205821Sedwin	int paskha;	/* Julian day */
42205821Sedwin	int cny;	/* Julian day */
43205821Sedwin	int firstdayofweek; /* 0 .. 6 */
44205821Sedwin	struct cal_month *months;
45205821Sedwin	struct cal_year	*nextyear;
46241737Sed};
47205821Sedwin
48205821Sedwinstruct cal_month {
49205821Sedwin	int month;			/* 01 .. 12 */
50205821Sedwin	int firstdayjulian;		/* 000 .. 366 */
51205821Sedwin	int firstdayofweek;		/* 0 .. 6 */
52205821Sedwin	struct cal_year *year;		/* points back */
53205821Sedwin	struct cal_day *days;
54205821Sedwin	struct cal_month *nextmonth;
55241737Sed};
56205821Sedwin
57205821Sedwinstruct cal_day {
58205821Sedwin	int dayofmonth;			/* 01 .. 31 */
59205821Sedwin	int julianday;			/* 000 .. 366 */
60205821Sedwin	int dayofweek;			/* 0 .. 6 */
61205821Sedwin	struct cal_day *nextday;
62205821Sedwin	struct cal_month *month;	/* points back */
63205821Sedwin	struct cal_year	*year;		/* points back */
64205821Sedwin	struct event *events;
65241737Sed};
66205821Sedwin
67205821Sedwinint debug_remember = 0;
68241737Sedstatic struct cal_year *hyear = NULL;
69205821Sedwin
70205821Sedwin/* 1-based month, 0-based days, cumulative */
71205821Sedwinint	cumdaytab[][14] = {
72205821Sedwin	{0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
73205821Sedwin	{0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
74205821Sedwin};
75205821Sedwin/* 1-based month, individual */
76251647Sgrogstatic int *monthdays;
77251647Sgrogint	monthdaytab[][14] = {
78205821Sedwin	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
79205821Sedwin	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
80205821Sedwin};
81205821Sedwin
82205821Sedwinstatic struct cal_day *	find_day(int yy, int mm, int dd);
83205821Sedwin
84205821Sedwinstatic void
85205821Sedwincreatedate(int y, int m, int d)
86205821Sedwin{
87205821Sedwin	struct cal_year *py, *pyp;
88205821Sedwin	struct cal_month *pm, *pmp;
89205821Sedwin	struct cal_day *pd, *pdp;
90205821Sedwin	int *cumday;
91205821Sedwin
92205821Sedwin	pyp = NULL;
93205821Sedwin	py = hyear;
94205821Sedwin	while (py != NULL) {
95205821Sedwin		if (py->year == y + 1900)
96205821Sedwin			break;
97205821Sedwin		pyp = py;
98205821Sedwin		py = py->nextyear;
99205821Sedwin	}
100205821Sedwin
101205821Sedwin	if (py == NULL) {
102205821Sedwin		struct tm td;
103205821Sedwin		time_t t;
104205821Sedwin		py = (struct cal_year *)calloc(1, sizeof(struct cal_year));
105205821Sedwin		py->year = y + 1900;
106205821Sedwin		py->easter = easter(y);
107205821Sedwin		py->paskha = paskha(y);
108205821Sedwin
109205821Sedwin		td = tm0;
110205821Sedwin		td.tm_year = y;
111205821Sedwin		td.tm_mday = 1;
112205821Sedwin		t = mktime(&td);
113205821Sedwin		localtime_r(&t, &td);
114205821Sedwin		py->firstdayofweek = td.tm_wday;
115205821Sedwin
116205821Sedwin		if (pyp != NULL)
117205821Sedwin			pyp->nextyear = py;
118205821Sedwin	}
119205821Sedwin	if (pyp == NULL) {
120205821Sedwin		/* The very very very first one */
121205821Sedwin		hyear = py;
122205821Sedwin	}
123205821Sedwin
124205821Sedwin	pmp = NULL;
125205821Sedwin	pm = py->months;
126205821Sedwin	while (pm != NULL) {
127205821Sedwin		if (pm->month == m)
128205821Sedwin			break;
129205821Sedwin		pmp = pm;
130205821Sedwin		pm = pm->nextmonth;
131205821Sedwin	}
132205821Sedwin
133205821Sedwin	if (pm == NULL) {
134205821Sedwin		pm = (struct cal_month *)calloc(1, sizeof(struct cal_month));
135205821Sedwin		pm->year = py;
136205821Sedwin		pm->month = m;
137205821Sedwin		cumday = cumdaytab[isleap(y)];
138205821Sedwin		pm->firstdayjulian = cumday[m] + 2;
139205821Sedwin		pm->firstdayofweek =
140205821Sedwin		    (py->firstdayofweek + pm->firstdayjulian -1) % 7;
141205821Sedwin		if (pmp != NULL)
142205821Sedwin			pmp->nextmonth = pm;
143205821Sedwin	}
144205821Sedwin	if (pmp == NULL)
145205821Sedwin		py->months = pm;
146205821Sedwin
147205821Sedwin	pdp = NULL;
148205821Sedwin	pd = pm->days;
149205821Sedwin	while (pd != NULL) {
150205821Sedwin		pdp = pd;
151205821Sedwin		pd = pd->nextday;
152205821Sedwin	}
153205821Sedwin
154205821Sedwin	if (pd == NULL) {	/* Always true */
155205821Sedwin		pd = (struct cal_day *)calloc(1, sizeof(struct cal_day));
156205821Sedwin		pd->month = pm;
157205821Sedwin		pd->year = py;
158205821Sedwin		pd->dayofmonth = d;
159205821Sedwin		pd->julianday = pm->firstdayjulian + d - 1;
160205821Sedwin		pd->dayofweek = (pm->firstdayofweek + d - 1) % 7;
161205821Sedwin		if (pdp != NULL)
162205821Sedwin			pdp->nextday = pd;
163205821Sedwin	}
164205821Sedwin	if (pdp == NULL)
165205821Sedwin		pm->days = pd;
166205821Sedwin}
167205821Sedwin
168205821Sedwinvoid
169205821Sedwingeneratedates(struct tm *tp1, struct tm *tp2)
170205821Sedwin{
171205821Sedwin	int y1, m1, d1;
172205821Sedwin	int y2, m2, d2;
173205821Sedwin	int y, m, d;
174205821Sedwin
175205821Sedwin	y1 = tp1->tm_year;
176205821Sedwin	m1 = tp1->tm_mon + 1;
177205821Sedwin	d1 = tp1->tm_mday;
178205821Sedwin	y2 = tp2->tm_year;
179205821Sedwin	m2 = tp2->tm_mon + 1;
180205821Sedwin	d2 = tp2->tm_mday;
181205821Sedwin
182205821Sedwin	if (y1 == y2) {
183205821Sedwin		if (m1 == m2) {
184205821Sedwin			/* Same year, same month. Easy! */
185205821Sedwin			for (d = d1; d <= d2; d++)
186205821Sedwin				createdate(y1, m1, d);
187205821Sedwin			return;
188205821Sedwin		}
189205821Sedwin		/*
190205821Sedwin		 * Same year, different month.
191205821Sedwin		 * - Take the leftover days from m1
192205821Sedwin		 * - Take all days from <m1 .. m2>
193205821Sedwin		 * - Take the first days from m2
194205821Sedwin		 */
195251647Sgrog		monthdays = monthdaytab[isleap(y1)];
196251647Sgrog		for (d = d1; d <= monthdays[m1]; d++)
197205821Sedwin			createdate(y1, m1, d);
198205821Sedwin		for (m = m1 + 1; m < m2; m++)
199251647Sgrog			for (d = 1; d <= monthdays[m]; d++)
200205821Sedwin				createdate(y1, m, d);
201205821Sedwin		for (d = 1; d <= d2; d++)
202205821Sedwin			createdate(y1, m2, d);
203205821Sedwin		return;
204205821Sedwin	}
205205821Sedwin	/*
206205821Sedwin	 * Different year, different month.
207205821Sedwin	 * - Take the leftover days from y1-m1
208205821Sedwin	 * - Take all days from y1-<m1 .. 12]
209205821Sedwin	 * - Take all days from <y1 .. y2>
210205821Sedwin	 * - Take all days from y2-[1 .. m2>
211205821Sedwin	 * - Take the first days of y2-m2
212205821Sedwin	 */
213251647Sgrog	monthdays = monthdaytab[isleap(y1)];
214251647Sgrog	for (d = d1; d <= monthdays[m1]; d++)
215205821Sedwin		createdate(y1, m1, d);
216205821Sedwin	for (m = m1 + 1; m <= 12; m++)
217251647Sgrog		for (d = 1; d <= monthdays[m]; d++)
218205821Sedwin			createdate(y1, m, d);
219205821Sedwin	for (y = y1 + 1; y < y2; y++) {
220251647Sgrog		monthdays = monthdaytab[isleap(y)];
221205821Sedwin		for (m = 1; m <= 12; m++)
222251647Sgrog			for (d = 1; d <= monthdays[m]; d++)
223205821Sedwin				createdate(y, m, d);
224205821Sedwin	}
225251647Sgrog	monthdays = monthdaytab[isleap(y2)];
226205821Sedwin	for (m = 1; m < m2; m++)
227251647Sgrog		for (d = 1; d <= monthdays[m]; d++)
228205821Sedwin			createdate(y2, m, d);
229205821Sedwin	for (d = 1; d <= d2; d++)
230205821Sedwin		createdate(y2, m2, d);
231205821Sedwin}
232205821Sedwin
233205821Sedwinvoid
234205821Sedwindumpdates(void)
235205821Sedwin{
236205821Sedwin	struct cal_year *y;
237205821Sedwin	struct cal_month *m;
238205821Sedwin	struct cal_day *d;
239205821Sedwin
240205821Sedwin	y = hyear;
241205821Sedwin	while (y != NULL) {
242205821Sedwin		printf("%-5d (wday:%d)\n", y->year, y->firstdayofweek);
243205821Sedwin		m = y->months;
244205821Sedwin		while (m != NULL) {
245205821Sedwin			printf("-- %-5d (julian:%d, dow:%d)\n", m->month,
246205821Sedwin			    m->firstdayjulian, m->firstdayofweek);
247205821Sedwin			d = m->days;
248205821Sedwin			while (d != NULL) {
249205821Sedwin				printf("  -- %-5d (julian:%d, dow:%d)\n",
250205821Sedwin				    d->dayofmonth, d->julianday, d->dayofweek);
251205821Sedwin				d = d->nextday;
252205821Sedwin			}
253205821Sedwin			m = m->nextmonth;
254205821Sedwin		}
255205821Sedwin		y = y->nextyear;
256205821Sedwin	}
257205821Sedwin}
258205821Sedwin
259205821Sedwinint
260205821Sedwinremember_ymd(int yy, int mm, int dd)
261205821Sedwin{
262205821Sedwin	struct cal_year *y;
263205821Sedwin	struct cal_month *m;
264205821Sedwin	struct cal_day *d;
265205821Sedwin
266205821Sedwin	if (debug_remember)
267205821Sedwin		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
268205821Sedwin
269205821Sedwin	y = hyear;
270205821Sedwin	while (y != NULL) {
271205821Sedwin		if (y->year != yy) {
272205821Sedwin			y = y->nextyear;
273205821Sedwin			continue;
274205821Sedwin		}
275205821Sedwin		m = y->months;
276205821Sedwin		while (m != NULL) {
277205821Sedwin			if (m->month != mm) {
278205821Sedwin				m = m->nextmonth;
279205821Sedwin				continue;
280205821Sedwin			}
281205821Sedwin			d = m->days;
282205821Sedwin			while (d != NULL) {
283205821Sedwin				if (d->dayofmonth == dd)
284205821Sedwin					return (1);
285205821Sedwin				d = d->nextday;
286205821Sedwin				continue;
287205821Sedwin			}
288205821Sedwin			return (0);
289205821Sedwin		}
290205821Sedwin		return (0);
291205821Sedwin	}
292205821Sedwin	return (0);
293205821Sedwin}
294205821Sedwin
295205821Sedwinint
296205821Sedwinremember_yd(int yy, int dd, int *rm, int *rd)
297205821Sedwin{
298205821Sedwin	struct cal_year *y;
299205821Sedwin	struct cal_month *m;
300205821Sedwin	struct cal_day *d;
301205821Sedwin
302205821Sedwin	if (debug_remember)
303205821Sedwin		printf("remember_yd: %d - %d\n", yy, dd);
304205821Sedwin
305205821Sedwin	y = hyear;
306205821Sedwin	while (y != NULL) {
307205821Sedwin		if (y->year != yy) {
308205821Sedwin			y = y->nextyear;
309205821Sedwin			continue;
310205821Sedwin		}
311205821Sedwin		m = y->months;
312205821Sedwin		while (m != NULL) {
313205821Sedwin			d = m->days;
314205821Sedwin			while (d != NULL) {
315205821Sedwin				if (d->julianday == dd) {
316205821Sedwin					*rm = m->month;
317205821Sedwin					*rd = d->dayofmonth;
318205821Sedwin					return (1);
319205821Sedwin				}
320205821Sedwin				d = d->nextday;
321205821Sedwin			}
322205821Sedwin			m = m->nextmonth;
323205821Sedwin		}
324205821Sedwin		return (0);
325205821Sedwin	}
326205821Sedwin	return (0);
327205821Sedwin}
328205821Sedwin
329205821Sedwinint
330205821Sedwinfirst_dayofweek_of_year(int yy)
331205821Sedwin{
332205821Sedwin	struct cal_year *y;
333205821Sedwin
334205821Sedwin	y = hyear;
335205821Sedwin	while (y != NULL) {
336205821Sedwin		if (y->year == yy)
337205821Sedwin			return (y->firstdayofweek);
338205821Sedwin		y = y->nextyear;
339205821Sedwin	}
340205821Sedwin
341205821Sedwin	/* Should not happen */
342205821Sedwin	return (-1);
343205821Sedwin}
344205821Sedwin
345205821Sedwinint
346205821Sedwinfirst_dayofweek_of_month(int yy, int mm)
347205821Sedwin{
348205821Sedwin	struct cal_year *y;
349205821Sedwin	struct cal_month *m;
350205821Sedwin
351205821Sedwin	y = hyear;
352205821Sedwin	while (y != NULL) {
353205821Sedwin		if (y->year != yy) {
354205821Sedwin			y = y->nextyear;
355205821Sedwin			continue;
356205821Sedwin		}
357205821Sedwin		m = y->months;
358205821Sedwin		while (m != NULL) {
359205821Sedwin			if (m->month == mm)
360205821Sedwin				return (m->firstdayofweek);
361205821Sedwin			m = m->nextmonth;
362205821Sedwin		}
363251647Sgrog		/* No data for this month */
364205821Sedwin		return (-1);
365205821Sedwin	}
366205821Sedwin
367251647Sgrog	/* No data for this year.  Error? */
368251647Sgrog        return (-1);
369205821Sedwin}
370205821Sedwin
371205821Sedwinint
372205821Sedwinwalkthrough_dates(struct event **e)
373205821Sedwin{
374205821Sedwin	static struct cal_year *y = NULL;
375205821Sedwin	static struct cal_month *m = NULL;
376205821Sedwin	static struct cal_day *d = NULL;
377205821Sedwin
378205821Sedwin	if (y == NULL) {
379205821Sedwin		y = hyear;
380205821Sedwin		m = y->months;
381205821Sedwin		d = m->days;
382205821Sedwin		*e = d->events;
383205821Sedwin		return (1);
384205821Sedwin	};
385205821Sedwin	if (d->nextday != NULL) {
386205821Sedwin		d = d->nextday;
387205821Sedwin		*e = d->events;
388205821Sedwin		return (1);
389205821Sedwin	}
390205821Sedwin	if (m->nextmonth != NULL) {
391205821Sedwin		m = m->nextmonth;
392205821Sedwin		d = m->days;
393205821Sedwin		*e = d->events;
394205821Sedwin		return (1);
395205821Sedwin	}
396205821Sedwin	if (y->nextyear != NULL) {
397205821Sedwin		y = y->nextyear;
398205821Sedwin		m = y->months;
399205821Sedwin		d = m->days;
400205821Sedwin		*e = d->events;
401205821Sedwin		return (1);
402205821Sedwin	}
403205821Sedwin
404205821Sedwin	return (0);
405205821Sedwin}
406205821Sedwin
407205821Sedwinstatic struct cal_day *
408205821Sedwinfind_day(int yy, int mm, int dd)
409205821Sedwin{
410205821Sedwin	struct cal_year *y;
411205821Sedwin	struct cal_month *m;
412205821Sedwin	struct cal_day *d;
413205821Sedwin
414205821Sedwin	if (debug_remember)
415205821Sedwin		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
416205821Sedwin
417205821Sedwin	y = hyear;
418205821Sedwin	while (y != NULL) {
419205821Sedwin		if (y->year != yy) {
420205821Sedwin			y = y->nextyear;
421205821Sedwin			continue;
422205821Sedwin		}
423205821Sedwin		m = y->months;
424205821Sedwin		while (m != NULL) {
425205821Sedwin			if (m->month != mm) {
426205821Sedwin				m = m->nextmonth;
427205821Sedwin				continue;
428205821Sedwin			}
429205821Sedwin			d = m->days;
430205821Sedwin			while (d != NULL) {
431205821Sedwin				if (d->dayofmonth == dd)
432205821Sedwin					return (d);
433205821Sedwin				d = d->nextday;
434205821Sedwin				continue;
435205821Sedwin			}
436205821Sedwin			return (NULL);
437205821Sedwin		}
438205821Sedwin		return (NULL);
439205821Sedwin	}
440205821Sedwin	return (NULL);
441205821Sedwin}
442205821Sedwin
443205821Sedwinvoid
444205821Sedwinaddtodate(struct event *e, int year, int month, int day)
445205821Sedwin{
446205821Sedwin	struct cal_day *d;
447205821Sedwin
448205821Sedwin	d = find_day(year, month, day);
449205821Sedwin	e->next = d->events;
450205821Sedwin	d->events = e;
451205821Sedwin}
452