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 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 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 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 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 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 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 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 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 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 * 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 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