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};
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};
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};
66
67int debug_remember = 0;
68static struct cal_year *hyear = NULL;
69
70/* 1-based month, 0-based days, cumulative */
71int	cumdaytab[][14] = {
72	{0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
73	{0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
74};
75/* 1-based month, individual */
76static int *monthdays;
77int	monthdaytab[][14] = {
78	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
79	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
80};
81
82static struct cal_day *	find_day(int yy, int mm, int dd);
83
84static void
85createdate(int y, int m, int d)
86{
87	struct cal_year *py, *pyp;
88	struct cal_month *pm, *pmp;
89	struct cal_day *pd, *pdp;
90	int *cumday;
91
92	pyp = NULL;
93	py = hyear;
94	while (py != NULL) {
95		if (py->year == y + 1900)
96			break;
97		pyp = py;
98		py = py->nextyear;
99	}
100
101	if (py == NULL) {
102		struct tm td;
103		time_t t;
104		py = (struct cal_year *)calloc(1, sizeof(struct cal_year));
105		py->year = y + 1900;
106		py->easter = easter(y);
107		py->paskha = paskha(y);
108
109		td = tm0;
110		td.tm_year = y;
111		td.tm_mday = 1;
112		t = mktime(&td);
113		localtime_r(&t, &td);
114		py->firstdayofweek = td.tm_wday;
115
116		if (pyp != NULL)
117			pyp->nextyear = py;
118	}
119	if (pyp == NULL) {
120		/* The very very very first one */
121		hyear = py;
122	}
123
124	pmp = NULL;
125	pm = py->months;
126	while (pm != NULL) {
127		if (pm->month == m)
128			break;
129		pmp = pm;
130		pm = pm->nextmonth;
131	}
132
133	if (pm == NULL) {
134		pm = (struct cal_month *)calloc(1, sizeof(struct cal_month));
135		pm->year = py;
136		pm->month = m;
137		cumday = cumdaytab[isleap(y)];
138		pm->firstdayjulian = cumday[m] + 2;
139		pm->firstdayofweek =
140		    (py->firstdayofweek + pm->firstdayjulian -1) % 7;
141		if (pmp != NULL)
142			pmp->nextmonth = pm;
143	}
144	if (pmp == NULL)
145		py->months = pm;
146
147	pdp = NULL;
148	pd = pm->days;
149	while (pd != NULL) {
150		pdp = pd;
151		pd = pd->nextday;
152	}
153
154	if (pd == NULL) {	/* Always true */
155		pd = (struct cal_day *)calloc(1, sizeof(struct cal_day));
156		pd->month = pm;
157		pd->year = py;
158		pd->dayofmonth = d;
159		pd->julianday = pm->firstdayjulian + d - 1;
160		pd->dayofweek = (pm->firstdayofweek + d - 1) % 7;
161		if (pdp != NULL)
162			pdp->nextday = pd;
163	}
164	if (pdp == NULL)
165		pm->days = pd;
166}
167
168void
169generatedates(struct tm *tp1, struct tm *tp2)
170{
171	int y1, m1, d1;
172	int y2, m2, d2;
173	int y, m, d;
174
175	y1 = tp1->tm_year;
176	m1 = tp1->tm_mon + 1;
177	d1 = tp1->tm_mday;
178	y2 = tp2->tm_year;
179	m2 = tp2->tm_mon + 1;
180	d2 = tp2->tm_mday;
181
182	if (y1 == y2) {
183		if (m1 == m2) {
184			/* Same year, same month. Easy! */
185			for (d = d1; d <= d2; d++)
186				createdate(y1, m1, d);
187			return;
188		}
189		/*
190		 * Same year, different month.
191		 * - Take the leftover days from m1
192		 * - Take all days from <m1 .. m2>
193		 * - Take the first days from m2
194		 */
195		monthdays = monthdaytab[isleap(y1)];
196		for (d = d1; d <= monthdays[m1]; d++)
197			createdate(y1, m1, d);
198		for (m = m1 + 1; m < m2; m++)
199			for (d = 1; d <= monthdays[m]; d++)
200				createdate(y1, m, d);
201		for (d = 1; d <= d2; d++)
202			createdate(y1, m2, d);
203		return;
204	}
205	/*
206	 * Different year, different month.
207	 * - Take the leftover days from y1-m1
208	 * - Take all days from y1-<m1 .. 12]
209	 * - Take all days from <y1 .. y2>
210	 * - Take all days from y2-[1 .. m2>
211	 * - Take the first days of y2-m2
212	 */
213	monthdays = monthdaytab[isleap(y1)];
214	for (d = d1; d <= monthdays[m1]; d++)
215		createdate(y1, m1, d);
216	for (m = m1 + 1; m <= 12; m++)
217		for (d = 1; d <= monthdays[m]; d++)
218			createdate(y1, m, d);
219	for (y = y1 + 1; y < y2; y++) {
220		monthdays = monthdaytab[isleap(y)];
221		for (m = 1; m <= 12; m++)
222			for (d = 1; d <= monthdays[m]; d++)
223				createdate(y, m, d);
224	}
225	monthdays = monthdaytab[isleap(y2)];
226	for (m = 1; m < m2; m++)
227		for (d = 1; d <= monthdays[m]; d++)
228			createdate(y2, m, d);
229	for (d = 1; d <= d2; d++)
230		createdate(y2, m2, d);
231}
232
233void
234dumpdates(void)
235{
236	struct cal_year *y;
237	struct cal_month *m;
238	struct cal_day *d;
239
240	y = hyear;
241	while (y != NULL) {
242		printf("%-5d (wday:%d)\n", y->year, y->firstdayofweek);
243		m = y->months;
244		while (m != NULL) {
245			printf("-- %-5d (julian:%d, dow:%d)\n", m->month,
246			    m->firstdayjulian, m->firstdayofweek);
247			d = m->days;
248			while (d != NULL) {
249				printf("  -- %-5d (julian:%d, dow:%d)\n",
250				    d->dayofmonth, d->julianday, d->dayofweek);
251				d = d->nextday;
252			}
253			m = m->nextmonth;
254		}
255		y = y->nextyear;
256	}
257}
258
259int
260remember_ymd(int yy, int mm, int dd)
261{
262	struct cal_year *y;
263	struct cal_month *m;
264	struct cal_day *d;
265
266	if (debug_remember)
267		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
268
269	y = hyear;
270	while (y != NULL) {
271		if (y->year != yy) {
272			y = y->nextyear;
273			continue;
274		}
275		m = y->months;
276		while (m != NULL) {
277			if (m->month != mm) {
278				m = m->nextmonth;
279				continue;
280			}
281			d = m->days;
282			while (d != NULL) {
283				if (d->dayofmonth == dd)
284					return (1);
285				d = d->nextday;
286				continue;
287			}
288			return (0);
289		}
290		return (0);
291	}
292	return (0);
293}
294
295int
296remember_yd(int yy, int dd, int *rm, int *rd)
297{
298	struct cal_year *y;
299	struct cal_month *m;
300	struct cal_day *d;
301
302	if (debug_remember)
303		printf("remember_yd: %d - %d\n", yy, dd);
304
305	y = hyear;
306	while (y != NULL) {
307		if (y->year != yy) {
308			y = y->nextyear;
309			continue;
310		}
311		m = y->months;
312		while (m != NULL) {
313			d = m->days;
314			while (d != NULL) {
315				if (d->julianday == dd) {
316					*rm = m->month;
317					*rd = d->dayofmonth;
318					return (1);
319				}
320				d = d->nextday;
321			}
322			m = m->nextmonth;
323		}
324		return (0);
325	}
326	return (0);
327}
328
329int
330first_dayofweek_of_year(int yy)
331{
332	struct cal_year *y;
333
334	y = hyear;
335	while (y != NULL) {
336		if (y->year == yy)
337			return (y->firstdayofweek);
338		y = y->nextyear;
339	}
340
341	/* Should not happen */
342	return (-1);
343}
344
345int
346first_dayofweek_of_month(int yy, int mm)
347{
348	struct cal_year *y;
349	struct cal_month *m;
350
351	y = hyear;
352	while (y != NULL) {
353		if (y->year != yy) {
354			y = y->nextyear;
355			continue;
356		}
357		m = y->months;
358		while (m != NULL) {
359			if (m->month == mm)
360				return (m->firstdayofweek);
361			m = m->nextmonth;
362		}
363		/* No data for this month */
364		return (-1);
365	}
366
367	/* No data for this year.  Error? */
368        return (-1);
369}
370
371int
372walkthrough_dates(struct event **e)
373{
374	static struct cal_year *y = NULL;
375	static struct cal_month *m = NULL;
376	static struct cal_day *d = NULL;
377
378	if (y == NULL) {
379		y = hyear;
380		m = y->months;
381		d = m->days;
382		*e = d->events;
383		return (1);
384	};
385	if (d->nextday != NULL) {
386		d = d->nextday;
387		*e = d->events;
388		return (1);
389	}
390	if (m->nextmonth != NULL) {
391		m = m->nextmonth;
392		d = m->days;
393		*e = d->events;
394		return (1);
395	}
396	if (y->nextyear != NULL) {
397		y = y->nextyear;
398		m = y->months;
399		d = m->days;
400		*e = d->events;
401		return (1);
402	}
403
404	return (0);
405}
406
407static struct cal_day *
408find_day(int yy, int mm, int dd)
409{
410	struct cal_year *y;
411	struct cal_month *m;
412	struct cal_day *d;
413
414	if (debug_remember)
415		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
416
417	y = hyear;
418	while (y != NULL) {
419		if (y->year != yy) {
420			y = y->nextyear;
421			continue;
422		}
423		m = y->months;
424		while (m != NULL) {
425			if (m->month != mm) {
426				m = m->nextmonth;
427				continue;
428			}
429			d = m->days;
430			while (d != NULL) {
431				if (d->dayofmonth == dd)
432					return (d);
433				d = d->nextday;
434				continue;
435			}
436			return (NULL);
437		}
438		return (NULL);
439	}
440	return (NULL);
441}
442
443void
444addtodate(struct event *e, int year, int month, int day)
445{
446	struct cal_day *d;
447
448	d = find_day(year, month, day);
449	e->next = d->events;
450	d->events = e;
451}
452