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 * $FreeBSD: head/usr.bin/calendar/dates.c 326276 2017-11-27 15:37:16Z pfg $ 29 */ 30 31 #include <stdbool.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <err.h> 35 #include <time.h> 36 37 #include "calendar.h" 38 39 struct cal_year { 40 int year; /* 19xx, 20xx, 21xx */ 41 int easter; /* Julian day */ 42 int paskha; /* Julian day */ 43 int cny; /* Julian day */ 44 int firstdayofweek; /* 0 .. 6 */ 45 struct cal_month *months; 46 struct cal_year *nextyear; 47 }; 48 49 struct cal_month { 50 int month; /* 01 .. 12 */ 51 int firstdayjulian; /* 000 .. 366 */ 52 int firstdayofweek; /* 0 .. 6 */ 53 struct cal_year *year; /* points back */ 54 struct cal_day *days; 55 struct cal_month *nextmonth; 56 }; 57 58 struct cal_day { 59 int dayofmonth; /* 01 .. 31 */ 60 int julianday; /* 000 .. 366 */ 61 int dayofweek; /* 0 .. 6 */ 62 struct cal_day *nextday; 63 struct cal_month *month; /* points back */ 64 struct cal_year *year; /* points back */ 65 struct event *events; 66 }; 67 68 static bool debug_remember = false; 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 hyear = py; /* The very very very first one */ 122 123 pmp = NULL; 124 pm = py->months; 125 while (pm != NULL) { 126 if (pm->month == m) 127 break; 128 pmp = pm; 129 pm = pm->nextmonth; 130 } 131 132 if (pm == NULL) { 133 pm = (struct cal_month *)calloc(1, sizeof(struct cal_month)); 134 pm->year = py; 135 pm->month = m; 136 cumday = cumdaytab[isleap(y)]; 137 pm->firstdayjulian = cumday[m] + 2; 138 pm->firstdayofweek = 139 (py->firstdayofweek + pm->firstdayjulian -1) % 7; 140 if (pmp != NULL) 141 pmp->nextmonth = pm; 142 } 143 if (pmp == NULL) 144 py->months = pm; 145 146 pdp = NULL; 147 pd = pm->days; 148 while (pd != NULL) { 149 pdp = pd; 150 pd = pd->nextday; 151 } 152 153 if (pd == NULL) { /* Always true */ 154 pd = (struct cal_day *)calloc(1, sizeof(struct cal_day)); 155 pd->month = pm; 156 pd->year = py; 157 pd->dayofmonth = d; 158 pd->julianday = pm->firstdayjulian + d - 1; 159 pd->dayofweek = (pm->firstdayofweek + d - 1) % 7; 160 if (pdp != NULL) 161 pdp->nextday = pd; 162 } 163 if (pdp == NULL) 164 pm->days = pd; 165 } 166 167 void 168 generatedates(struct tm *tp1, struct tm *tp2) 169 { 170 int y1, m1, d1; 171 int y2, m2, d2; 172 int y, m, d; 173 174 y1 = tp1->tm_year; 175 m1 = tp1->tm_mon + 1; 176 d1 = tp1->tm_mday; 177 y2 = tp2->tm_year; 178 m2 = tp2->tm_mon + 1; 179 d2 = tp2->tm_mday; 180 181 if (y1 == y2) { 182 if (m1 == m2) { 183 /* Same year, same month. Easy! */ 184 for (d = d1; d <= d2; d++) 185 createdate(y1, m1, d); 186 return; 187 } 188 /* 189 * Same year, different month. 190 * - Take the leftover days from m1 191 * - Take all days from <m1 .. m2> 192 * - Take the first days from m2 193 */ 194 monthdays = monthdaytab[isleap(y1)]; 195 for (d = d1; d <= monthdays[m1]; d++) 196 createdate(y1, m1, d); 197 for (m = m1 + 1; m < m2; m++) 198 for (d = 1; d <= monthdays[m]; d++) 199 createdate(y1, m, d); 200 for (d = 1; d <= d2; d++) 201 createdate(y1, m2, d); 202 return; 203 } 204 /* 205 * Different year, different month. 206 * - Take the leftover days from y1-m1 207 * - Take all days from y1-<m1 .. 12] 208 * - Take all days from <y1 .. y2> 209 * - Take all days from y2-[1 .. m2> 210 * - Take the first days of y2-m2 211 */ 212 monthdays = monthdaytab[isleap(y1)]; 213 for (d = d1; d <= monthdays[m1]; d++) 214 createdate(y1, m1, d); 215 for (m = m1 + 1; m <= 12; m++) 216 for (d = 1; d <= monthdays[m]; d++) 217 createdate(y1, m, d); 218 for (y = y1 + 1; y < y2; y++) { 219 monthdays = monthdaytab[isleap(y)]; 220 for (m = 1; m <= 12; m++) 221 for (d = 1; d <= monthdays[m]; d++) 222 createdate(y, m, d); 223 } 224 monthdays = monthdaytab[isleap(y2)]; 225 for (m = 1; m < m2; m++) 226 for (d = 1; d <= monthdays[m]; d++) 227 createdate(y2, m, d); 228 for (d = 1; d <= d2; d++) 229 createdate(y2, m2, d); 230 } 231 232 void 233 dumpdates(void) 234 { 235 struct cal_year *y; 236 struct cal_month *m; 237 struct cal_day *d; 238 239 y = hyear; 240 while (y != NULL) { 241 printf("%-5d (wday:%d)\n", y->year, y->firstdayofweek); 242 m = y->months; 243 while (m != NULL) { 244 printf("-- %-5d (julian:%d, dow:%d)\n", m->month, 245 m->firstdayjulian, m->firstdayofweek); 246 d = m->days; 247 while (d != NULL) { 248 printf(" -- %-5d (julian:%d, dow:%d)\n", 249 d->dayofmonth, d->julianday, d->dayofweek); 250 d = d->nextday; 251 } 252 m = m->nextmonth; 253 } 254 y = y->nextyear; 255 } 256 } 257 258 bool 259 remember_ymd(int yy, int mm, int dd) 260 { 261 struct cal_year *y; 262 struct cal_month *m; 263 struct cal_day *d; 264 265 if (debug_remember) 266 printf("remember_ymd: %d - %d - %d\n", yy, mm, dd); 267 268 y = hyear; 269 while (y != NULL) { 270 if (y->year != yy) { 271 y = y->nextyear; 272 continue; 273 } 274 m = y->months; 275 while (m != NULL) { 276 if (m->month != mm) { 277 m = m->nextmonth; 278 continue; 279 } 280 d = m->days; 281 while (d != NULL) { 282 if (d->dayofmonth == dd) 283 return (true); 284 d = d->nextday; 285 continue; 286 } 287 return (false); 288 } 289 return (false); 290 } 291 return (false); 292 } 293 294 bool 295 remember_yd(int yy, int dd, int *rm, int *rd) 296 { 297 struct cal_year *y; 298 struct cal_month *m; 299 struct cal_day *d; 300 301 if (debug_remember) 302 printf("remember_yd: %d - %d\n", yy, dd); 303 304 y = hyear; 305 while (y != NULL) { 306 if (y->year != yy) { 307 y = y->nextyear; 308 continue; 309 } 310 m = y->months; 311 while (m != NULL) { 312 d = m->days; 313 while (d != NULL) { 314 if (d->julianday == dd) { 315 *rm = m->month; 316 *rd = d->dayofmonth; 317 return (true); 318 } 319 d = d->nextday; 320 } 321 m = m->nextmonth; 322 } 323 return (false); 324 } 325 return (false); 326 } 327 328 int 329 first_dayofweek_of_year(int yy) 330 { 331 struct cal_year *y; 332 333 y = hyear; 334 while (y != NULL) { 335 if (y->year == yy) 336 return (y->firstdayofweek); 337 y = y->nextyear; 338 } 339 340 /* Should not happen */ 341 return (-1); 342 } 343 344 int 345 first_dayofweek_of_month(int yy, int mm) 346 { 347 struct cal_year *y; 348 struct cal_month *m; 349 350 y = hyear; 351 while (y != NULL) { 352 if (y->year != yy) { 353 y = y->nextyear; 354 continue; 355 } 356 m = y->months; 357 while (m != NULL) { 358 if (m->month == mm) 359 return (m->firstdayofweek); 360 m = m->nextmonth; 361 } 362 /* No data for this month */ 363 return (-1); 364 } 365 366 /* No data for this year. Error? */ 367 return (-1); 368 } 369 370 int 371 walkthrough_dates(struct event **e) 372 { 373 static struct cal_year *y = NULL; 374 static struct cal_month *m = NULL; 375 static struct cal_day *d = NULL; 376 377 if (y == NULL) { 378 y = hyear; 379 m = y->months; 380 d = m->days; 381 *e = d->events; 382 return (1); 383 } 384 if (d->nextday != NULL) { 385 d = d->nextday; 386 *e = d->events; 387 return (1); 388 } 389 if (m->nextmonth != NULL) { 390 m = m->nextmonth; 391 d = m->days; 392 *e = d->events; 393 return (1); 394 } 395 if (y->nextyear != NULL) { 396 y = y->nextyear; 397 m = y->months; 398 d = m->days; 399 *e = d->events; 400 return (1); 401 } 402 403 return (0); 404 } 405 406 static struct cal_day * 407 find_day(int yy, int mm, int dd) 408 { 409 struct cal_year *y; 410 struct cal_month *m; 411 struct cal_day *d; 412 413 if (debug_remember) 414 printf("remember_ymd: %d - %d - %d\n", yy, mm, dd); 415 416 y = hyear; 417 while (y != NULL) { 418 if (y->year != yy) { 419 y = y->nextyear; 420 continue; 421 } 422 m = y->months; 423 while (m != NULL) { 424 if (m->month != mm) { 425 m = m->nextmonth; 426 continue; 427 } 428 d = m->days; 429 while (d != NULL) { 430 if (d->dayofmonth == dd) 431 return (d); 432 d = d->nextday; 433 continue; 434 } 435 return (NULL); 436 } 437 return (NULL); 438 } 439 return (NULL); 440 } 441 442 void 443 addtodate(struct event *e, int year, int month, int day) 444 { 445 struct cal_day *d; 446 447 d = find_day(year, month, day); 448 e->next = d->events; 449 d->events = e; 450 } 451