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