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