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 };
68
69 int debug_remember = 0;
70 static struct cal_year *hyear = NULL;
71
72 /* 1-based month, 0-based days, cumulative */
73 int 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 */
78 static int *monthdays;
79 int 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
84 static struct cal_day * find_day(int yy, int mm, int dd);
85
86 static void
createdate(int y,int m,int d)87 createdate(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
170 void
generatedates(struct tm * tp1,struct tm * tp2)171 generatedates(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
235 void
dumpdates(void)236 dumpdates(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
261 int
remember_ymd(int yy,int mm,int dd)262 remember_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
297 int
remember_yd(int yy,int dd,int * rm,int * rd)298 remember_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
331 int
first_dayofweek_of_year(int yy)332 first_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
347 int
first_dayofweek_of_month(int yy,int mm)348 first_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
373 int
walkthrough_dates(struct event ** e)374 walkthrough_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
409 static struct cal_day *
find_day(int yy,int mm,int dd)410 find_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
445 void
addtodate(struct event * e,int year,int month,int day)446 addtodate(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