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