1 /* $NetBSD: cal.c,v 1.19 2005/06/02 01:38:50 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kim Letkeman. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\ 38 The Regents of the University of California. All rights reserved.\n"); 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 4/2/94"; 44 #else 45 __RCSID("$NetBSD: cal.c,v 1.19 2005/06/02 01:38:50 lukem Exp $"); 46 #endif 47 #endif /* not lint */ 48 49 #include <sys/types.h> 50 51 #include <ctype.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <limits.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <termcap.h> 59 #include <time.h> 60 #include <tzfile.h> 61 #include <unistd.h> 62 63 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */ 64 65 #define FIRST_MISSING_DAY reform->first_missing_day 66 #define NUMBER_MISSING_DAYS reform->missing_days 67 68 #define MAXDAYS 42 /* max slots in a month array */ 69 #define SPACE -1 /* used in day array */ 70 71 static int days_in_month[2][13] = { 72 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 73 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 74 }; 75 76 int empty[MAXDAYS] = { 77 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 78 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 79 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 80 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 81 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 82 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 83 }; 84 int shift_days[2][4][MAXDAYS + 1]; 85 86 char *month_names[12] = { 87 "January", "February", "March", "April", "May", "June", 88 "July", "August", "September", "October", "November", "December", 89 }; 90 91 char *day_headings = " S M Tu W Th F S"; 92 char *j_day_headings = " S M Tu W Th F S"; 93 94 /* leap years according to the julian calendar */ 95 #define j_leap_year(y, m, d) \ 96 (((m) > 2) && \ 97 !((y) % 4)) 98 99 /* leap years according to the gregorian calendar */ 100 #define g_leap_year(y, m, d) \ 101 (((m) > 2) && \ 102 ((!((y) % 4) && ((y) % 100)) || \ 103 !((y) % 400))) 104 105 /* leap year -- account for gregorian reformation at some point */ 106 #define leap_year(yr) \ 107 ((yr) <= reform->year ? j_leap_year((yr), 3, 1) : \ 108 g_leap_year((yr), 3, 1)) 109 110 /* number of julian leap days that have passed by a given date */ 111 #define j_leap_days(y, m, d) \ 112 ((((y) - 1) / 4) + j_leap_year(y, m, d)) 113 114 /* number of gregorian leap days that have passed by a given date */ 115 #define g_leap_days(y, m, d) \ 116 ((((y) - 1) / 4) - (((y) - 1) / 100) + (((y) - 1) / 400) + \ 117 g_leap_year(y, m, d)) 118 119 /* 120 * Subtracting the gregorian leap day count (for a given date) from 121 * the julian leap day count (for the same date) describes the number 122 * of days from the date before the shift to the next date that 123 * appears in the calendar. Since we want to know the number of 124 * *missing* days, not the number of days that the shift spans, we 125 * subtract 2. 126 * 127 * Alternately... 128 * 129 * There's a reason they call the Dark ages the Dark Ages. Part of it 130 * is that we don't have that many records of that period of time. 131 * One of the reasons for this is that a lot of the Dark Ages never 132 * actually took place. At some point in the first millenium A.D., a 133 * ruler of some power decided that he wanted the number of the year 134 * to be different than what it was, so he changed it to coincide 135 * nicely with some event (a birthday or anniversary, perhaps a 136 * wedding, or maybe a centennial for a largish city). One of the 137 * side effects of this upon the Gregorian reform is that two Julian 138 * leap years (leap days celebrated during centennial years that are 139 * not quatro-centennial years) were skipped. 140 */ 141 #define GREGORIAN_MAGIC 2 142 143 /* number of centuries since the reform, not inclusive */ 144 #define centuries_since_reform(yr) \ 145 ((yr) > reform->year ? ((yr) / 100) - (reform->year / 100) : 0) 146 147 /* number of centuries since the reform whose modulo of 400 is 0 */ 148 #define quad_centuries_since_reform(yr) \ 149 ((yr) > reform->year ? ((yr) / 400) - (reform->year / 400) : 0) 150 151 /* number of leap years between year 1 and this year, not inclusive */ 152 #define leap_years_since_year_1(yr) \ 153 ((yr) / 4 - centuries_since_reform(yr) + quad_centuries_since_reform(yr)) 154 155 struct reform { 156 const char *country; 157 int ambiguity, year, month, date; 158 long first_missing_day; 159 int missing_days; 160 /* 161 * That's 2 for standard/julian display, 4 for months possibly 162 * affected by the Gregorian shift, and MAXDAYS + 1 for the 163 * days that get displayed, plus a crib slot. 164 */ 165 } *reform, reforms[] = { 166 { "DEFAULT", 0, 1752, 9, 3 }, 167 { "Italy", 1, 1582, 10, 5 }, 168 { "Spain", 1, 1582, 10, 5 }, 169 { "Portugal", 1, 1582, 10, 5 }, 170 { "Poland", 1, 1582, 10, 5 }, 171 { "France", 2, 1582, 12, 10 }, 172 { "Luxembourg", 2, 1582, 12, 22 }, 173 { "Netherlands", 2, 1582, 12, 22 }, 174 { "Bavaria", 0, 1583, 10, 6 }, 175 { "Austria", 2, 1584, 1, 7 }, 176 { "Switzerland", 2, 1584, 1, 12 }, 177 { "Hungary", 0, 1587, 10, 22 }, 178 { "Germany", 0, 1700, 2, 19 }, 179 { "Norway", 0, 1700, 2, 19 }, 180 { "Denmark", 0, 1700, 2, 19 }, 181 { "Great Britain", 0, 1752, 9, 3 }, 182 { "England", 0, 1752, 9, 3 }, 183 { "America", 0, 1752, 9, 3 }, 184 { "Sweden", 0, 1753, 2, 18 }, 185 { "Finland", 0, 1753, 2, 18 }, 186 { "Japan", 0, 1872, 12, 20 }, 187 { "China", 0, 1911, 11, 7 }, 188 { "Bulgaria", 0, 1916, 4, 1 }, 189 { "U.S.S.R.", 0, 1918, 2, 1 }, 190 { "Serbia", 0, 1919, 1, 19 }, 191 { "Romania", 0, 1919, 1, 19 }, 192 { "Greece", 0, 1924, 3, 10 }, 193 { "Turkey", 0, 1925, 12, 19 }, 194 { "Egypt", 0, 1928, 9, 18 }, 195 { NULL, 0, 0, 0, 0 }, 196 }; 197 198 int julian; 199 int dow; 200 int hilite; 201 char *md, *me; 202 203 void init_hilite(void); 204 int getnum(const char *); 205 void gregorian_reform(const char *); 206 void reform_day_array(int, int, int *, int *, int *,int *,int *,int *); 207 int ascii_day(char *, int); 208 void center(char *, int, int); 209 void day_array(int, int, int *); 210 int day_in_week(int, int, int); 211 int day_in_year(int, int, int); 212 void monthrange(int, int, int, int, int); 213 int main(int, char **); 214 void trim_trailing_spaces(char *); 215 void usage(void); 216 217 int 218 main(int argc, char **argv) 219 { 220 struct tm *local_time; 221 time_t now; 222 int ch, month, year, yflag; 223 int before, after, use_reform; 224 int yearly = 0; 225 char *when; 226 227 before = after = 0; 228 use_reform = yflag = year = 0; 229 when = NULL; 230 while ((ch = getopt(argc, argv, "A:B:d:hjR:ry3")) != -1) { 231 switch (ch) { 232 case 'A': 233 after = getnum(optarg); 234 break; 235 case 'B': 236 before = getnum(optarg); 237 break; 238 case 'd': 239 dow = getnum(optarg); 240 if (dow < 0 || dow > 6) 241 errx(1, "illegal day of week value: use 0-6"); 242 break; 243 case 'h': 244 init_hilite(); 245 break; 246 case 'j': 247 julian = 1; 248 break; 249 case 'R': 250 when = optarg; 251 break; 252 case 'r': 253 use_reform = 1; 254 break; 255 case 'y': 256 yflag = 1; 257 break; 258 case '3': 259 before = after = 1; 260 break; 261 case '?': 262 default: 263 usage(); 264 /* NOTREACHED */ 265 } 266 } 267 268 argc -= optind; 269 argv += optind; 270 271 if (when != NULL) 272 gregorian_reform(when); 273 if (reform == NULL) 274 gregorian_reform("DEFAULT"); 275 276 month = 0; 277 switch (argc) { 278 case 2: 279 if ((month = atoi(*argv++)) < 1 || month > 12) 280 errx(1, "illegal month value: use 1-12"); 281 /* FALLTHROUGH */ 282 case 1: 283 if ((year = atoi(*argv)) < 1 || year > 9999) 284 errx(1, "illegal year value: use 1-9999"); 285 break; 286 case 0: 287 (void)time(&now); 288 local_time = localtime(&now); 289 if (use_reform) 290 year = reform->year; 291 else 292 year = local_time->tm_year + TM_YEAR_BASE; 293 if (!yflag) { 294 if (use_reform) 295 month = reform->month; 296 else 297 month = local_time->tm_mon + 1; 298 } 299 break; 300 default: 301 usage(); 302 } 303 304 if (!month) { 305 /* yearly */ 306 month = 1; 307 before = 0; 308 after = 11; 309 yearly = 1; 310 } 311 312 monthrange(month, year, before, after, yearly); 313 314 exit(0); 315 } 316 317 #define DAY_LEN 3 /* 3 spaces per day */ 318 #define J_DAY_LEN 4 /* 4 spaces per day */ 319 #define WEEK_LEN 20 /* 7 * 3 - one space at the end */ 320 #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */ 321 #define HEAD_SEP 2 /* spaces between day headings */ 322 #define J_HEAD_SEP 2 323 #define MONTH_PER_ROW 3 /* how many monthes in a row */ 324 #define J_MONTH_PER_ROW 2 325 326 void 327 monthrange(int month, int year, int before, int after, int yearly) 328 { 329 int startmonth, startyear; 330 int endmonth, endyear; 331 int i, row; 332 int days[3][MAXDAYS]; 333 char lineout[256]; 334 int inayear; 335 int newyear; 336 int day_len, week_len, head_sep; 337 int month_per_row; 338 int skip, r_off, w_off; 339 340 if (julian) { 341 day_len = J_DAY_LEN; 342 week_len = J_WEEK_LEN; 343 head_sep = J_HEAD_SEP; 344 month_per_row = J_MONTH_PER_ROW; 345 } 346 else { 347 day_len = DAY_LEN; 348 week_len = WEEK_LEN; 349 head_sep = HEAD_SEP; 350 month_per_row = MONTH_PER_ROW; 351 } 352 353 month--; 354 355 startyear = year - (before + 12 - 1 - month) / 12; 356 startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12); 357 endyear = year + (month + after) / 12; 358 endmonth = (month + after) % 12; 359 360 if (startyear < 0 || endyear > 9999) { 361 errx(1, "year should be in 1-9999\n"); 362 } 363 364 year = startyear; 365 month = startmonth; 366 inayear = newyear = (year != endyear || yearly); 367 if (inayear) { 368 skip = month % month_per_row; 369 month -= skip; 370 } 371 else { 372 skip = 0; 373 } 374 375 do { 376 if (newyear) { 377 (void)snprintf(lineout, sizeof(lineout), "%d", year); 378 center(lineout, week_len * month_per_row + 379 head_sep * (month_per_row - 1), 0); 380 (void)printf("\n\n"); 381 newyear = 0; 382 } 383 384 for (i = 0; i < skip; i++) 385 center("", week_len, head_sep); 386 387 for (; i < month_per_row; i++) { 388 int sep; 389 390 if (year == endyear && month + i > endmonth) 391 break; 392 393 sep = (i == month_per_row - 1) ? 0 : head_sep; 394 day_array(month + i + 1, year, days[i]); 395 if (inayear) { 396 center(month_names[month + i], week_len, sep); 397 } 398 else { 399 snprintf(lineout, sizeof(lineout), "%s %d", 400 month_names[month + i], year); 401 center(lineout, week_len, sep); 402 } 403 } 404 printf("\n"); 405 406 for (i = 0; i < skip; i++) 407 center("", week_len, head_sep); 408 409 for (; i < month_per_row; i++) { 410 int sep; 411 412 if (year == endyear && month + i > endmonth) 413 break; 414 415 sep = (i == month_per_row - 1) ? 0 : head_sep; 416 if (dow) { 417 printf("%s ", (julian) ? 418 j_day_headings + 4 * dow : 419 day_headings + 3 * dow); 420 printf("%.*s", dow * (julian ? 4 : 3) - 1, 421 (julian) ? j_day_headings : day_headings); 422 } else 423 printf("%s", (julian) ? j_day_headings : day_headings); 424 printf("%*s", sep, ""); 425 } 426 printf("\n"); 427 428 for (row = 0; row < 6; row++) { 429 char *p = NULL; 430 431 memset(lineout, ' ', sizeof(lineout)); 432 for (i = 0; i < skip; i++) { 433 /* nothing */ 434 } 435 w_off = 0; 436 for (; i < month_per_row; i++) { 437 int col, *dp; 438 439 if (year == endyear && month + i > endmonth) 440 break; 441 442 p = lineout + i * (week_len + 2) + w_off; 443 dp = &days[i][row * 7]; 444 for (col = 0; col < 7; 445 col++, p += day_len + r_off) { 446 r_off = ascii_day(p, *dp++); 447 w_off += r_off; 448 } 449 } 450 *p = '\0'; 451 trim_trailing_spaces(lineout); 452 (void)printf("%s\n", lineout); 453 } 454 455 skip = 0; 456 month += month_per_row; 457 if (month >= 12) { 458 month -= 12; 459 year++; 460 newyear = 1; 461 } 462 } while (year < endyear || (year == endyear && month <= endmonth)); 463 } 464 465 /* 466 * day_array -- 467 * Fill in an array of 42 integers with a calendar. Assume for a moment 468 * that you took the (maximum) 6 rows in a calendar and stretched them 469 * out end to end. You would have 42 numbers or spaces. This routine 470 * builds that array for any month from Jan. 1 through Dec. 9999. 471 */ 472 void 473 day_array(int month, int year, int *days) 474 { 475 int day, dw, dm; 476 time_t t; 477 struct tm *tm; 478 479 t = time(NULL); 480 tm = localtime(&t); 481 tm->tm_year += TM_YEAR_BASE; 482 tm->tm_mon++; 483 tm->tm_yday++; /* jan 1 is 1 for us, not 0 */ 484 485 for (dm = month + year * 12, dw = 0; dw < 4; dw++) { 486 if (dm == shift_days[julian][dw][MAXDAYS]) { 487 memmove(days, shift_days[julian][dw], 488 MAXDAYS * sizeof(int)); 489 return; 490 } 491 } 492 493 memmove(days, empty, MAXDAYS * sizeof(int)); 494 dm = days_in_month[leap_year(year)][month]; 495 dw = day_in_week(1, month, year); 496 day = julian ? day_in_year(1, month, year) : 1; 497 while (dm--) { 498 if (hilite && year == tm->tm_year && 499 (julian ? (day == tm->tm_yday) : 500 (month == tm->tm_mon && day == tm->tm_mday))) 501 days[dw++] = SPACE - day++; 502 else 503 days[dw++] = day++; 504 } 505 } 506 507 /* 508 * day_in_year -- 509 * return the 1 based day number within the year 510 */ 511 int 512 day_in_year(int day, int month, int year) 513 { 514 int i, leap; 515 516 leap = leap_year(year); 517 for (i = 1; i < month; i++) 518 day += days_in_month[leap][i]; 519 return (day); 520 } 521 522 /* 523 * day_in_week 524 * return the 0 based day number for any date from 1 Jan. 1 to 525 * 31 Dec. 9999. Returns the day of the week of the first 526 * missing day for any given Gregorian shift. 527 */ 528 int 529 day_in_week(int day, int month, int year) 530 { 531 long temp; 532 533 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) 534 + day_in_year(day, month, year); 535 if (temp < FIRST_MISSING_DAY) 536 return ((temp - dow + 6 + SATURDAY) % 7); 537 if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) 538 return (((temp - dow + 6 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); 539 return ((FIRST_MISSING_DAY - dow + 6 + SATURDAY) % 7); 540 } 541 542 int 543 ascii_day(char *p, int day) 544 { 545 int display, val, rc; 546 char *b; 547 static char *aday[] = { 548 "", 549 " 1", " 2", " 3", " 4", " 5", " 6", " 7", 550 " 8", " 9", "10", "11", "12", "13", "14", 551 "15", "16", "17", "18", "19", "20", "21", 552 "22", "23", "24", "25", "26", "27", "28", 553 "29", "30", "31", 554 }; 555 556 if (day == SPACE) { 557 memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN); 558 return (0); 559 } 560 if (day < SPACE) { 561 b = p; 562 day = SPACE - day; 563 } else 564 b = NULL; 565 if (julian) { 566 if ((val = day / 100) != 0) { 567 day %= 100; 568 *p++ = val + '0'; 569 display = 1; 570 } else { 571 *p++ = ' '; 572 display = 0; 573 } 574 val = day / 10; 575 if (val || display) 576 *p++ = val + '0'; 577 else 578 *p++ = ' '; 579 *p++ = day % 10 + '0'; 580 } else { 581 *p++ = aday[day][0]; 582 *p++ = aday[day][1]; 583 } 584 585 rc = 0; 586 if (b != NULL) { 587 char *t, h[64]; 588 int l; 589 590 l = p - b; 591 memcpy(h, b, l); 592 p = b; 593 594 if (md != NULL) { 595 for (t = md; *t; rc++) 596 *p++ = *t++; 597 memcpy(p, h, l); 598 p += l; 599 for (t = me; *t; rc++) 600 *p++ = *t++; 601 } else { 602 for (t = &h[0]; l--; t++) { 603 *p++ = *t; 604 rc++; 605 *p++ = '\b'; 606 rc++; 607 *p++ = *t; 608 } 609 } 610 } 611 612 *p = ' '; 613 return (rc); 614 } 615 616 void 617 trim_trailing_spaces(char *s) 618 { 619 char *p; 620 621 for (p = s; *p; ++p) 622 continue; 623 while (p > s && isspace((unsigned char)*--p)) 624 continue; 625 if (p > s) 626 ++p; 627 *p = '\0'; 628 } 629 630 void 631 center(char *str, int len, int separate) 632 { 633 634 len -= strlen(str); 635 (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, ""); 636 if (separate) 637 (void)printf("%*s", separate, ""); 638 } 639 640 /* 641 * gregorian_reform -- 642 * Given a description of date on which the Gregorian Reform was 643 * applied. The argument can be any of the "country" names 644 * listed in the reforms array (case insensitive) or a date of 645 * the form YYYY/MM/DD. The date and month can be omitted if 646 * doing so would not select more than one different built-in 647 * reform point. 648 */ 649 void 650 gregorian_reform(const char *p) 651 { 652 int year, month, date; 653 int i, days, diw, diy; 654 char c; 655 656 i = sscanf(p, "%d%*[/,-]%d%*[/,-]%d%c", &year, &month, &date, &c); 657 switch (i) { 658 case 4: 659 /* 660 * If the character was sscanf()ed, then there's more 661 * stuff than we need. 662 */ 663 errx(1, "date specifier %s invalid", p); 664 case 0: 665 /* 666 * Not a form we can sscanf(), so void these, and we 667 * can try matching "country" names later. 668 */ 669 year = month = date = -1; 670 break; 671 case 1: 672 month = 0; 673 /*FALLTHROUGH*/ 674 case 2: 675 date = 0; 676 /*FALLTHROUGH*/ 677 case 3: 678 /* 679 * At last, some sanity checking on the values we were 680 * given. 681 */ 682 if (year < 1 || year > 9999) 683 errx(1, "%d: illegal year value: use 1-9999", year); 684 if (i > 1 && (month < 1 || month > 12)) 685 errx(1, "%d: illegal month value: use 1-12", month); 686 if ((i == 3 && date < 1) || date < 0 || 687 date > days_in_month[1][month]) 688 /* 689 * What about someone specifying a leap day in 690 * a non-leap year? Well...that's a tricky 691 * one. We can't yet *say* whether the year 692 * in question is a leap year. What if the 693 * date given was, for example, 1700/2/29? is 694 * that a valid leap day? 695 * 696 * So...we punt, and hope that saying 29 in 697 * the case of February isn't too bad an idea. 698 */ 699 errx(1, "%d: illegal date value: use 1-%d", date, 700 days_in_month[1][month]); 701 break; 702 } 703 704 /* 705 * A complete date was specified, so use the other pope. 706 */ 707 if (date > 0) { 708 static struct reform Goestheveezl; 709 710 reform = &Goestheveezl; 711 reform->country = "Bompzidaize"; 712 reform->year = year; 713 reform->month = month; 714 reform->date = date; 715 } 716 717 /* 718 * No date information was specified, so let's try to match on 719 * country name. 720 */ 721 else if (year == -1) { 722 for (reform = &reforms[0]; reform->year; reform++) { 723 if (strcasecmp(p, reform->country) == 0) 724 break; 725 } 726 } 727 728 /* 729 * We have *some* date information, but not a complete date. 730 * Let's see if we have enough to pick a single entry from the 731 * list that's not ambiguous. 732 */ 733 else { 734 for (reform = &reforms[0]; reform->year; reform++) { 735 if ((year == 0 || year == reform->year) && 736 (month == 0 || month == reform->month) && 737 (date == 0 || month == reform->date)) 738 break; 739 } 740 741 if (i <= reform->ambiguity) 742 errx(1, "%s: ambiguous short reform date specification", p); 743 } 744 745 /* 746 * Oops...we reached the end of the list. 747 */ 748 if (reform->year == 0) 749 errx(1, "reform name %s invalid", p); 750 751 /* 752 * 753 */ 754 reform->missing_days = 755 j_leap_days(reform->year, reform->month, reform->date) - 756 g_leap_days(reform->year, reform->month, reform->date) - 757 GREGORIAN_MAGIC; 758 759 reform->first_missing_day = 760 (reform->year - 1) * 365 + 761 day_in_year(reform->date, reform->month, reform->year) + 762 date + 763 j_leap_days(reform->year, reform->month, reform->date); 764 765 /* 766 * Once we know the day of the week of the first missing day, 767 * skip back to the first of the month's day of the week. 768 */ 769 diw = day_in_week(reform->date, reform->month, reform->year); 770 diw = (diw + 8 - (reform->date % 7)) % 7; 771 diy = day_in_year(1, reform->month, reform->year); 772 773 /* 774 * We might need all four of these (if you switch from Julian 775 * to Gregorian at some point after 9900, you get a gap of 73 776 * days, and that can affect four months), and it doesn't hurt 777 * all that much to precompute them, so there. 778 */ 779 date = 1; 780 days = 0; 781 for (i = 0; i < 4; i++) 782 reform_day_array(reform->month + i, reform->year, 783 &days, &date, &diw, &diy, 784 shift_days[0][i], 785 shift_days[1][i]); 786 } 787 788 /* 789 * reform_day_array -- 790 * Pre-calculates the given month's calendar (in both "standard" 791 * and "julian day" representations) with respect for days 792 * skipped during a reform period. 793 */ 794 void 795 reform_day_array(int month, int year, int *done, int *date, int *diw, int *diy, 796 int *scal, int *jcal) 797 { 798 int mdays; 799 800 /* 801 * If the reform was in the month of october or later, then 802 * the month number from the caller could "overflow". 803 */ 804 if (month > 12) { 805 month -= 12; 806 year++; 807 } 808 809 /* 810 * Erase months, and set crib number. The crib number is used 811 * later to determine if the month to be displayed is here or 812 * should be built on the fly with the generic routine 813 */ 814 memmove(scal, empty, MAXDAYS * sizeof(int)); 815 scal[MAXDAYS] = month + year * 12; 816 memmove(jcal, empty, MAXDAYS * sizeof(int)); 817 jcal[MAXDAYS] = month + year * 12; 818 819 /* 820 * It doesn't matter what the actual month is when figuring 821 * out if this is a leap year or not, just so long as February 822 * gets the right number of days in it. 823 */ 824 mdays = days_in_month[g_leap_year(year, 3, 1)][month]; 825 826 /* 827 * Bounce back to the first "row" in the day array, and fill 828 * in any days that actually occur. 829 */ 830 for (*diw %= 7; (*date - *done) <= mdays; (*date)++, (*diy)++) { 831 /* 832 * "date" doesn't get reset by the caller across calls 833 * to this routine, so we can actually tell that we're 834 * looking at April the 41st. Much easier than trying 835 * to calculate the absolute julian day for a given 836 * date and then checking that. 837 */ 838 if (*date < reform->date || 839 *date >= reform->date + reform->missing_days) { 840 scal[*diw] = *date - *done; 841 jcal[*diw] = *diy; 842 (*diw)++; 843 } 844 } 845 *done += mdays; 846 } 847 848 int 849 getnum(const char *p) 850 { 851 long result; 852 char *ep; 853 854 errno = 0; 855 result = strtoul(p, &ep, 10); 856 if (p[0] == '\0' || *ep != '\0') 857 goto error; 858 if (errno == ERANGE && result == ULONG_MAX) 859 goto error; 860 if (result > INT_MAX) 861 goto error; 862 863 return (int)result; 864 865 error: 866 errx(1, "bad number: %s", p); 867 /*NOTREACHED*/ 868 } 869 870 void 871 init_hilite(void) 872 { 873 static char control[128]; 874 char cap[1024]; 875 char *tc; 876 877 hilite++; 878 879 if (!isatty(fileno(stdout))) 880 return; 881 882 tc = getenv("TERM"); 883 if (tc == NULL) 884 tc = "dumb"; 885 if (tgetent(&cap[0], tc) != 1) 886 return; 887 888 tc = &control[0]; 889 if ((md = tgetstr(hilite > 1 ? "mr" : "md", &tc))) 890 *tc++ = '\0'; 891 if ((me = tgetstr("me", &tc))) 892 *tc++ = '\0'; 893 if (me == NULL || md == NULL) 894 md = me = NULL; 895 } 896 897 void 898 usage(void) 899 { 900 901 (void)fprintf(stderr, 902 "usage: cal [-hjry3] [-d day-of-week] [-B before] [-A after] " 903 "[-R reform-spec]\n [[month] year]\n"); 904 exit(1); 905 } 906