1/*-
2 * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <err.h>
34#include <time.h>
35
36#include "calendar.h"
37
38struct cal_year {
39	int year;	/* 19xx, 20xx, 21xx */
40	int easter;	/* Julian day */
41	int paskha;	/* Julian day */
42	int cny;	/* Julian day */
43	int firstdayofweek; /* 0 .. 6 */
44	struct cal_month *months;
45	struct cal_year	*nextyear;
46} cal_year;
47
48struct cal_month {
49	int month;			/* 01 .. 12 */
50	int firstdayjulian;		/* 000 .. 366 */
51	int firstdayofweek;		/* 0 .. 6 */
52	struct cal_year *year;		/* points back */
53	struct cal_day *days;
54	struct cal_month *nextmonth;
55} cal_month;
56
57struct cal_day {
58	int dayofmonth;			/* 01 .. 31 */
59	int julianday;			/* 000 .. 366 */
60	int dayofweek;			/* 0 .. 6 */
61	struct cal_day *nextday;
62	struct cal_month *month;	/* points back */
63	struct cal_year	*year;		/* points back */
64	struct event *events;
65} cal_day;
66
67int debug_remember = 0;
68struct cal_year	*hyear = NULL;
69
70/* 1-based month, 0-based days, cumulative */
71int *cumdays;
72int	cumdaytab[][14] = {
73	{0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
74	{0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
75};
76/* 1-based month, individual */
77int *mondays;
78int	mondaytab[][14] = {
79	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
80	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
81};
82
83static struct cal_day *	find_day(int yy, int mm, int dd);
84
85static void
86createdate(int y, int m, int d)
87{
88	struct cal_year *py, *pyp;
89	struct cal_month *pm, *pmp;
90	struct cal_day *pd, *pdp;
91	int *cumday;
92
93	pyp = NULL;
94	py = hyear;
95	while (py != NULL) {
96		if (py->year == y + 1900)
97			break;
98		pyp = py;
99		py = py->nextyear;
100	}
101
102	if (py == NULL) {
103		struct tm td;
104		time_t t;
105		py = (struct cal_year *)calloc(1, sizeof(struct cal_year));
106		py->year = y + 1900;
107		py->easter = easter(y);
108		py->paskha = paskha(y);
109
110		td = tm0;
111		td.tm_year = y;
112		td.tm_mday = 1;
113		t = mktime(&td);
114		localtime_r(&t, &td);
115		py->firstdayofweek = td.tm_wday;
116
117		if (pyp != NULL)
118			pyp->nextyear = py;
119	}
120	if (pyp == NULL) {
121		/* The very very very first one */
122		hyear = py;
123	}
124
125	pmp = NULL;
126	pm = py->months;
127	while (pm != NULL) {
128		if (pm->month == m)
129			break;
130		pmp = pm;
131		pm = pm->nextmonth;
132	}
133
134	if (pm == NULL) {
135		pm = (struct cal_month *)calloc(1, sizeof(struct cal_month));
136		pm->year = py;
137		pm->month = m;
138		cumday = cumdaytab[isleap(y)];
139		pm->firstdayjulian = cumday[m] + 2;
140		pm->firstdayofweek =
141		    (py->firstdayofweek + pm->firstdayjulian -1) % 7;
142		if (pmp != NULL)
143			pmp->nextmonth = pm;
144	}
145	if (pmp == NULL)
146		py->months = pm;
147
148	pdp = NULL;
149	pd = pm->days;
150	while (pd != NULL) {
151		pdp = pd;
152		pd = pd->nextday;
153	}
154
155	if (pd == NULL) {	/* Always true */
156		pd = (struct cal_day *)calloc(1, sizeof(struct cal_day));
157		pd->month = pm;
158		pd->year = py;
159		pd->dayofmonth = d;
160		pd->julianday = pm->firstdayjulian + d - 1;
161		pd->dayofweek = (pm->firstdayofweek + d - 1) % 7;
162		if (pdp != NULL)
163			pdp->nextday = pd;
164	}
165	if (pdp == NULL)
166		pm->days = pd;
167}
168
169void
170generatedates(struct tm *tp1, struct tm *tp2)
171{
172	int y1, m1, d1;
173	int y2, m2, d2;
174	int y, m, d;
175
176	y1 = tp1->tm_year;
177	m1 = tp1->tm_mon + 1;
178	d1 = tp1->tm_mday;
179	y2 = tp2->tm_year;
180	m2 = tp2->tm_mon + 1;
181	d2 = tp2->tm_mday;
182
183	if (y1 == y2) {
184		if (m1 == m2) {
185			/* Same year, same month. Easy! */
186			for (d = d1; d <= d2; d++)
187				createdate(y1, m1, d);
188			return;
189		}
190		/*
191		 * Same year, different month.
192		 * - Take the leftover days from m1
193		 * - Take all days from <m1 .. m2>
194		 * - Take the first days from m2
195		 */
196		mondays = mondaytab[isleap(y1)];
197		for (d = d1; d <= mondays[m1]; d++)
198			createdate(y1, m1, d);
199		for (m = m1 + 1; m < m2; m++)
200			for (d = 1; d <= mondays[m]; d++)
201				createdate(y1, m, d);
202		for (d = 1; d <= d2; d++)
203			createdate(y1, m2, d);
204		return;
205	}
206	/*
207	 * Different year, different month.
208	 * - Take the leftover days from y1-m1
209	 * - Take all days from y1-<m1 .. 12]
210	 * - Take all days from <y1 .. y2>
211	 * - Take all days from y2-[1 .. m2>
212	 * - Take the first days of y2-m2
213	 */
214	mondays = mondaytab[isleap(y1)];
215	for (d = d1; d <= mondays[m1]; d++)
216		createdate(y1, m1, d);
217	for (m = m1 + 1; m <= 12; m++)
218		for (d = 1; d <= mondays[m]; d++)
219			createdate(y1, m, d);
220	for (y = y1 + 1; y < y2; y++) {
221		mondays = mondaytab[isleap(y)];
222		for (m = 1; m <= 12; m++)
223			for (d = 1; d <= mondays[m]; d++)
224				createdate(y, m, d);
225	}
226	mondays = mondaytab[isleap(y2)];
227	for (m = 1; m < m2; m++)
228		for (d = 1; d <= mondays[m]; d++)
229			createdate(y2, m, d);
230	for (d = 1; d <= d2; d++)
231		createdate(y2, m2, d);
232}
233
234void
235dumpdates(void)
236{
237	struct cal_year *y;
238	struct cal_month *m;
239	struct cal_day *d;
240
241	y = hyear;
242	while (y != NULL) {
243		printf("%-5d (wday:%d)\n", y->year, y->firstdayofweek);
244		m = y->months;
245		while (m != NULL) {
246			printf("-- %-5d (julian:%d, dow:%d)\n", m->month,
247			    m->firstdayjulian, m->firstdayofweek);
248			d = m->days;
249			while (d != NULL) {
250				printf("  -- %-5d (julian:%d, dow:%d)\n",
251				    d->dayofmonth, d->julianday, d->dayofweek);
252				d = d->nextday;
253			}
254			m = m->nextmonth;
255		}
256		y = y->nextyear;
257	}
258}
259
260int
261remember_ymd(int yy, int mm, int dd)
262{
263	struct cal_year *y;
264	struct cal_month *m;
265	struct cal_day *d;
266
267	if (debug_remember)
268		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
269
270	y = hyear;
271	while (y != NULL) {
272		if (y->year != yy) {
273			y = y->nextyear;
274			continue;
275		}
276		m = y->months;
277		while (m != NULL) {
278			if (m->month != mm) {
279				m = m->nextmonth;
280				continue;
281			}
282			d = m->days;
283			while (d != NULL) {
284				if (d->dayofmonth == dd)
285					return (1);
286				d = d->nextday;
287				continue;
288			}
289			return (0);
290		}
291		return (0);
292	}
293	return (0);
294}
295
296int
297remember_yd(int yy, int dd, int *rm, int *rd)
298{
299	struct cal_year *y;
300	struct cal_month *m;
301	struct cal_day *d;
302
303	if (debug_remember)
304		printf("remember_yd: %d - %d\n", yy, dd);
305
306	y = hyear;
307	while (y != NULL) {
308		if (y->year != yy) {
309			y = y->nextyear;
310			continue;
311		}
312		m = y->months;
313		while (m != NULL) {
314			d = m->days;
315			while (d != NULL) {
316				if (d->julianday == dd) {
317					*rm = m->month;
318					*rd = d->dayofmonth;
319					return (1);
320				}
321				d = d->nextday;
322			}
323			m = m->nextmonth;
324		}
325		return (0);
326	}
327	return (0);
328}
329
330int
331first_dayofweek_of_year(int yy)
332{
333	struct cal_year *y;
334
335	y = hyear;
336	while (y != NULL) {
337		if (y->year == yy)
338			return (y->firstdayofweek);
339		y = y->nextyear;
340	}
341
342	/* Should not happen */
343	return (-1);
344}
345
346int
347first_dayofweek_of_month(int yy, int mm)
348{
349	struct cal_year *y;
350	struct cal_month *m;
351
352	y = hyear;
353	while (y != NULL) {
354		if (y->year != yy) {
355			y = y->nextyear;
356			continue;
357		}
358		m = y->months;
359		while (m != NULL) {
360			if (m->month == mm)
361				return (m->firstdayofweek);
362			m = m->nextmonth;
363		}
364		/* Should not happen */
365		return (-1);
366	}
367
368	/* Should not happen */
369	return (-1);
370}
371
372int
373walkthrough_dates(struct event **e)
374{
375	static struct cal_year *y = NULL;
376	static struct cal_month *m = NULL;
377	static struct cal_day *d = NULL;
378
379	if (y == NULL) {
380		y = hyear;
381		m = y->months;
382		d = m->days;
383		*e = d->events;
384		return (1);
385	};
386	if (d->nextday != NULL) {
387		d = d->nextday;
388		*e = d->events;
389		return (1);
390	}
391	if (m->nextmonth != NULL) {
392		m = m->nextmonth;
393		d = m->days;
394		*e = d->events;
395		return (1);
396	}
397	if (y->nextyear != NULL) {
398		y = y->nextyear;
399		m = y->months;
400		d = m->days;
401		*e = d->events;
402		return (1);
403	}
404
405	return (0);
406}
407
408static struct cal_day *
409find_day(int yy, int mm, int dd)
410{
411	struct cal_year *y;
412	struct cal_month *m;
413	struct cal_day *d;
414
415	if (debug_remember)
416		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
417
418	y = hyear;
419	while (y != NULL) {
420		if (y->year != yy) {
421			y = y->nextyear;
422			continue;
423		}
424		m = y->months;
425		while (m != NULL) {
426			if (m->month != mm) {
427				m = m->nextmonth;
428				continue;
429			}
430			d = m->days;
431			while (d != NULL) {
432				if (d->dayofmonth == dd)
433					return (d);
434				d = d->nextday;
435				continue;
436			}
437			return (NULL);
438		}
439		return (NULL);
440	}
441	return (NULL);
442}
443
444void
445addtodate(struct event *e, int year, int month, int day)
446{
447	struct cal_day *d;
448
449	d = find_day(year, month, day);
450	e->next = d->events;
451	d->events = e;
452}
453