1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 #include <stdio.h>
32 #include <stdlib.h>
33 #include <err.h>
34 #include <time.h>
35
36 #include "calendar.h"
37
38 struct 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
48 struct 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
57 struct 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 struct event *lastevent;
66 };
67
68 int debug_remember = 0;
69 static struct cal_year *hyear = NULL;
70
71 /* 1-based month, 0-based days, cumulative */
72 int 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 */
77 static int *monthdays;
78 int monthdaytab[][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
83 static struct cal_day * find_day(int yy, int mm, int dd);
84
85 static void
createdate(int y,int m,int d)86 createdate(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
169 void
generatedates(struct tm * tp1,struct tm * tp2)170 generatedates(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 monthdays = monthdaytab[isleap(y1)];
197 for (d = d1; d <= monthdays[m1]; d++)
198 createdate(y1, m1, d);
199 for (m = m1 + 1; m < m2; m++)
200 for (d = 1; d <= monthdays[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 monthdays = monthdaytab[isleap(y1)];
215 for (d = d1; d <= monthdays[m1]; d++)
216 createdate(y1, m1, d);
217 for (m = m1 + 1; m <= 12; m++)
218 for (d = 1; d <= monthdays[m]; d++)
219 createdate(y1, m, d);
220 for (y = y1 + 1; y < y2; y++) {
221 monthdays = monthdaytab[isleap(y)];
222 for (m = 1; m <= 12; m++)
223 for (d = 1; d <= monthdays[m]; d++)
224 createdate(y, m, d);
225 }
226 monthdays = monthdaytab[isleap(y2)];
227 for (m = 1; m < m2; m++)
228 for (d = 1; d <= monthdays[m]; d++)
229 createdate(y2, m, d);
230 for (d = 1; d <= d2; d++)
231 createdate(y2, m2, d);
232 }
233
234 void
dumpdates(void)235 dumpdates(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
260 int
remember_ymd(int yy,int mm,int dd)261 remember_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
296 int
remember_yd(int yy,int dd,int * rm,int * rd)297 remember_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
330 int
first_dayofweek_of_year(int yy)331 first_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
346 int
first_dayofweek_of_month(int yy,int mm)347 first_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 /* No data for this month */
365 return (-1);
366 }
367
368 /* No data for this year. Error? */
369 return (-1);
370 }
371
372 int
walkthrough_dates(struct event ** e)373 walkthrough_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
408 static struct cal_day *
find_day(int yy,int mm,int dd)409 find_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
444 void
addtodate(struct event * e)445 addtodate(struct event *e)
446 {
447 struct cal_day *d;
448 struct event *ee;
449
450 d = find_day(e->year, e->month, e->day);
451 ee = d->lastevent;
452 if (ee != NULL)
453 ee->next = e;
454 else
455 d->events = e;
456 d->lastevent = e;
457 }
458