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