1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1992-2009 Edwin Groothuis <[email protected]>.
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
40 struct 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
50 struct 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
59 struct 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
70 int debug_remember = 0;
71 static struct cal_year *hyear = NULL;
72
73 /* 1-based month, 0-based days, cumulative */
74 int 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 */
79 static int *monthdays;
80 int 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
85 static struct cal_day * find_day(int yy, int mm, int dd);
86
87 static void
createdate(int y,int m,int d)88 createdate(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
171 void
generatedates(struct tm * tp1,struct tm * tp2)172 generatedates(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
236 void
dumpdates(void)237 dumpdates(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
262 int
remember_ymd(int yy,int mm,int dd)263 remember_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
298 int
remember_yd(int yy,int dd,int * rm,int * rd)299 remember_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
332 int
first_dayofweek_of_year(int yy)333 first_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
348 int
first_dayofweek_of_month(int yy,int mm)349 first_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
374 int
walkthrough_dates(struct event ** e)375 walkthrough_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
410 static struct cal_day *
find_day(int yy,int mm,int dd)411 find_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
446 void
addtodate(struct event * e)447 addtodate(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