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