1 /* $NetBSD: cal.c,v 1.27 2011/08/29 13:55:22 joerg 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\ 38 The Regents of the University of California. All rights reserved."); 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.27 2011/08/29 13:55:22 joerg 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 <term.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 static 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 static int shift_days[2][4][MAXDAYS + 1]; 85 86 static const char *month_names[12] = { 87 "January", "February", "March", "April", "May", "June", 88 "July", "August", "September", "October", "November", "December", 89 }; 90 91 static const char *day_headings = " S M Tu W Th F S"; 92 static const 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 static 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, 0, 0 }, 167 { "Italy", 1, 1582, 10, 5, 0, 0 }, 168 { "Spain", 1, 1582, 10, 5, 0, 0 }, 169 { "Portugal", 1, 1582, 10, 5, 0, 0 }, 170 { "Poland", 1, 1582, 10, 5, 0, 0 }, 171 { "France", 2, 1582, 12, 10, 0, 0 }, 172 { "Luxembourg", 2, 1582, 12, 22, 0, 0 }, 173 { "Netherlands", 2, 1582, 12, 22, 0, 0 }, 174 { "Bavaria", 0, 1583, 10, 6, 0, 0 }, 175 { "Austria", 2, 1584, 1, 7, 0, 0 }, 176 { "Switzerland", 2, 1584, 1, 12, 0, 0 }, 177 { "Hungary", 0, 1587, 10, 22, 0, 0 }, 178 { "Germany", 0, 1700, 2, 19, 0, 0 }, 179 { "Norway", 0, 1700, 2, 19, 0, 0 }, 180 { "Denmark", 0, 1700, 2, 19, 0, 0 }, 181 { "Great Britain", 0, 1752, 9, 3, 0, 0 }, 182 { "England", 0, 1752, 9, 3, 0, 0 }, 183 { "America", 0, 1752, 9, 3, 0, 0 }, 184 { "Sweden", 0, 1753, 2, 18, 0, 0 }, 185 { "Finland", 0, 1753, 2, 18, 0, 0 }, 186 { "Japan", 0, 1872, 12, 20, 0, 0 }, 187 { "China", 0, 1911, 11, 7, 0, 0 }, 188 { "Bulgaria", 0, 1916, 4, 1, 0, 0 }, 189 { "U.S.S.R.", 0, 1918, 2, 1, 0, 0 }, 190 { "Serbia", 0, 1919, 1, 19, 0, 0 }, 191 { "Romania", 0, 1919, 1, 19, 0, 0 }, 192 { "Greece", 0, 1924, 3, 10, 0, 0 }, 193 { "Turkey", 0, 1925, 12, 19, 0, 0 }, 194 { "Egypt", 0, 1928, 9, 18, 0, 0 }, 195 { NULL, 0, 0, 0, 0, 0, 0 }, 196 }; 197 198 static int julian; 199 static int dow; 200 static int hilite; 201 static const char *md, *me; 202 203 static void init_hilite(void); 204 static int getnum(const char *); 205 static void gregorian_reform(const char *); 206 static void reform_day_array(int, int, int *, int *, int *,int *,int *,int *); 207 static int ascii_day(char *, int); 208 static void center(const char *, int, int); 209 static void day_array(int, int, int *); 210 static int day_in_week(int, int, int); 211 static int day_in_year(int, int, int); 212 static void monthrange(int, int, int, int, int); 213 static void trim_trailing_spaces(char *); 214 __dead static void usage(void); 215 216 int 217 main(int argc, char **argv) 218 { 219 struct tm *local_time; 220 time_t now; 221 int ch, yflag; 222 long month, year; 223 int before, after, use_reform; 224 int yearly = 0; 225 char *when, *eoi; 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 if (after < 0) 235 errx(1, "Argument to -A must be positive"); 236 break; 237 case 'B': 238 before = getnum(optarg); 239 if (before < 0) 240 errx(1, "Argument to -B must be positive"); 241 break; 242 case 'd': 243 dow = getnum(optarg); 244 if (dow < 0 || dow > 6) 245 errx(1, "illegal day of week value: use 0-6"); 246 break; 247 case 'h': 248 init_hilite(); 249 break; 250 case 'j': 251 julian = 1; 252 break; 253 case 'R': 254 when = optarg; 255 break; 256 case 'r': 257 use_reform = 1; 258 break; 259 case 'y': 260 yflag = 1; 261 break; 262 case '3': 263 before = after = 1; 264 break; 265 case '?': 266 default: 267 usage(); 268 /* NOTREACHED */ 269 } 270 } 271 272 argc -= optind; 273 argv += optind; 274 275 if (when != NULL) 276 gregorian_reform(when); 277 if (reform == NULL) 278 gregorian_reform("DEFAULT"); 279 280 month = 0; 281 switch (argc) { 282 case 2: 283 month = strtol(*argv++, &eoi, 10); 284 if (month < 1 || month > 12 || *eoi != '\0') 285 errx(1, "illegal month value: use 1-12"); 286 year = strtol(*argv, &eoi, 10); 287 if (year < 1 || year > 9999 || *eoi != '\0') 288 errx(1, "illegal year value: use 1-9999"); 289 break; 290 case 1: 291 year = strtol(*argv, &eoi, 10); 292 if (year < 1 || year > 9999 || (*eoi != '\0' && *eoi != '/' && *eoi != '-')) 293 errx(1, "illegal year value: use 1-9999"); 294 if (*eoi != '\0') { 295 month = strtol(eoi + 1, &eoi, 10); 296 if (month < 1 || month > 12 || *eoi != '\0') 297 errx(1, "illegal month value: use 1-12"); 298 } 299 break; 300 case 0: 301 (void)time(&now); 302 local_time = localtime(&now); 303 if (use_reform) 304 year = reform->year; 305 else 306 year = local_time->tm_year + TM_YEAR_BASE; 307 if (!yflag) { 308 if (use_reform) 309 month = reform->month; 310 else 311 month = local_time->tm_mon + 1; 312 } 313 break; 314 default: 315 usage(); 316 } 317 318 if (!month) { 319 /* yearly */ 320 month = 1; 321 before = 0; 322 after = 11; 323 yearly = 1; 324 } 325 326 monthrange(month, year, before, after, yearly); 327 328 exit(0); 329 } 330 331 #define DAY_LEN 3 /* 3 spaces per day */ 332 #define J_DAY_LEN 4 /* 4 spaces per day */ 333 #define WEEK_LEN 20 /* 7 * 3 - one space at the end */ 334 #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */ 335 #define HEAD_SEP 2 /* spaces between day headings */ 336 #define J_HEAD_SEP 2 337 #define MONTH_PER_ROW 3 /* how many monthes in a row */ 338 #define J_MONTH_PER_ROW 2 339 340 static void 341 monthrange(int month, int year, int before, int after, int yearly) 342 { 343 int startmonth, startyear; 344 int endmonth, endyear; 345 int i, row; 346 int days[3][MAXDAYS]; 347 char lineout[256]; 348 int inayear; 349 int newyear; 350 int day_len, week_len, head_sep; 351 int month_per_row; 352 int skip, r_off, w_off; 353 354 if (julian) { 355 day_len = J_DAY_LEN; 356 week_len = J_WEEK_LEN; 357 head_sep = J_HEAD_SEP; 358 month_per_row = J_MONTH_PER_ROW; 359 } 360 else { 361 day_len = DAY_LEN; 362 week_len = WEEK_LEN; 363 head_sep = HEAD_SEP; 364 month_per_row = MONTH_PER_ROW; 365 } 366 367 month--; 368 369 startyear = year - (before + 12 - 1 - month) / 12; 370 startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12); 371 endyear = year + (month + after) / 12; 372 endmonth = (month + after) % 12; 373 374 if (startyear < 0 || endyear > 9999) { 375 errx(1, "year should be in 1-9999\n"); 376 } 377 378 year = startyear; 379 month = startmonth; 380 inayear = newyear = (year != endyear || yearly); 381 if (inayear) { 382 skip = month % month_per_row; 383 month -= skip; 384 } 385 else { 386 skip = 0; 387 } 388 389 do { 390 if (newyear) { 391 (void)snprintf(lineout, sizeof(lineout), "%d", year); 392 center(lineout, week_len * month_per_row + 393 head_sep * (month_per_row - 1), 0); 394 (void)printf("\n\n"); 395 newyear = 0; 396 } 397 398 for (i = 0; i < skip; i++) 399 center("", week_len, head_sep); 400 401 for (; i < month_per_row; i++) { 402 int sep; 403 404 if (year == endyear && month + i > endmonth) 405 break; 406 407 sep = (i == month_per_row - 1) ? 0 : head_sep; 408 day_array(month + i + 1, year, days[i]); 409 if (inayear) { 410 center(month_names[month + i], week_len, sep); 411 } 412 else { 413 snprintf(lineout, sizeof(lineout), "%s %d", 414 month_names[month + i], year); 415 center(lineout, week_len, sep); 416 } 417 } 418 printf("\n"); 419 420 for (i = 0; i < skip; i++) 421 center("", week_len, head_sep); 422 423 for (; i < month_per_row; i++) { 424 int sep; 425 426 if (year == endyear && month + i > endmonth) 427 break; 428 429 sep = (i == month_per_row - 1) ? 0 : head_sep; 430 if (dow) { 431 printf("%s ", (julian) ? 432 j_day_headings + 4 * dow : 433 day_headings + 3 * dow); 434 printf("%.*s", dow * (julian ? 4 : 3) - 1, 435 (julian) ? j_day_headings : day_headings); 436 } else 437 printf("%s", (julian) ? j_day_headings : day_headings); 438 printf("%*s", sep, ""); 439 } 440 printf("\n"); 441 442 for (row = 0; row < 6; row++) { 443 char *p = NULL; 444 445 memset(lineout, ' ', sizeof(lineout)); 446 for (i = 0; i < skip; i++) { 447 /* nothing */ 448 } 449 w_off = 0; 450 for (; i < month_per_row; i++) { 451 int col, *dp; 452 453 if (year == endyear && month + i > endmonth) 454 break; 455 456 p = lineout + i * (week_len + 2) + w_off; 457 dp = &days[i][row * 7]; 458 for (col = 0; col < 7; 459 col++, p += day_len + r_off) { 460 r_off = ascii_day(p, *dp++); 461 w_off += r_off; 462 } 463 } 464 *p = '\0'; 465 trim_trailing_spaces(lineout); 466 (void)printf("%s\n", lineout); 467 } 468 469 skip = 0; 470 month += month_per_row; 471 if (month >= 12) { 472 month -= 12; 473 year++; 474 newyear = 1; 475 } 476 } while (year < endyear || (year == endyear && month <= endmonth)); 477 } 478 479 /* 480 * day_array -- 481 * Fill in an array of 42 integers with a calendar. Assume for a moment 482 * that you took the (maximum) 6 rows in a calendar and stretched them 483 * out end to end. You would have 42 numbers or spaces. This routine 484 * builds that array for any month from Jan. 1 through Dec. 9999. 485 */ 486 static void 487 day_array(int month, int year, int *days) 488 { 489 int day, dw, dm; 490 time_t t; 491 struct tm *tm; 492 493 t = time(NULL); 494 tm = localtime(&t); 495 tm->tm_year += TM_YEAR_BASE; 496 tm->tm_mon++; 497 tm->tm_yday++; /* jan 1 is 1 for us, not 0 */ 498 499 for (dm = month + year * 12, dw = 0; dw < 4; dw++) { 500 if (dm == shift_days[julian][dw][MAXDAYS]) { 501 memmove(days, shift_days[julian][dw], 502 MAXDAYS * sizeof(int)); 503 return; 504 } 505 } 506 507 memmove(days, empty, MAXDAYS * sizeof(int)); 508 dm = days_in_month[leap_year(year)][month]; 509 dw = day_in_week(1, month, year); 510 day = julian ? day_in_year(1, month, year) : 1; 511 while (dm--) { 512 if (hilite && year == tm->tm_year && 513 (julian ? (day == tm->tm_yday) : 514 (month == tm->tm_mon && day == tm->tm_mday))) 515 days[dw++] = SPACE - day++; 516 else 517 days[dw++] = day++; 518 } 519 } 520 521 /* 522 * day_in_year -- 523 * return the 1 based day number within the year 524 */ 525 static int 526 day_in_year(int day, int month, int year) 527 { 528 int i, leap; 529 530 leap = leap_year(year); 531 for (i = 1; i < month; i++) 532 day += days_in_month[leap][i]; 533 return (day); 534 } 535 536 /* 537 * day_in_week 538 * return the 0 based day number for any date from 1 Jan. 1 to 539 * 31 Dec. 9999. Returns the day of the week of the first 540 * missing day for any given Gregorian shift. 541 */ 542 static int 543 day_in_week(int day, int month, int year) 544 { 545 long temp; 546 547 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) 548 + day_in_year(day, month, year); 549 if (temp < FIRST_MISSING_DAY) 550 return ((temp - dow + 6 + SATURDAY) % 7); 551 if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) 552 return (((temp - dow + 6 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); 553 return ((FIRST_MISSING_DAY - dow + 6 + SATURDAY) % 7); 554 } 555 556 static int 557 ascii_day(char *p, int day) 558 { 559 int display, val, rc; 560 char *b; 561 static const char *aday[] = { 562 "", 563 " 1", " 2", " 3", " 4", " 5", " 6", " 7", 564 " 8", " 9", "10", "11", "12", "13", "14", 565 "15", "16", "17", "18", "19", "20", "21", 566 "22", "23", "24", "25", "26", "27", "28", 567 "29", "30", "31", 568 }; 569 570 if (day == SPACE) { 571 memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN); 572 return (0); 573 } 574 if (day < SPACE) { 575 b = p; 576 day = SPACE - day; 577 } else 578 b = NULL; 579 if (julian) { 580 if ((val = day / 100) != 0) { 581 day %= 100; 582 *p++ = val + '0'; 583 display = 1; 584 } else { 585 *p++ = ' '; 586 display = 0; 587 } 588 val = day / 10; 589 if (val || display) 590 *p++ = val + '0'; 591 else 592 *p++ = ' '; 593 *p++ = day % 10 + '0'; 594 } else { 595 *p++ = aday[day][0]; 596 *p++ = aday[day][1]; 597 } 598 599 rc = 0; 600 if (b != NULL) { 601 const char *t; 602 char h[64]; 603 int l; 604 605 l = p - b; 606 memcpy(h, b, l); 607 p = b; 608 609 if (md != NULL) { 610 for (t = md; *t; rc++) 611 *p++ = *t++; 612 memcpy(p, h, l); 613 p += l; 614 for (t = me; *t; rc++) 615 *p++ = *t++; 616 } else { 617 for (t = &h[0]; l--; t++) { 618 *p++ = *t; 619 rc++; 620 *p++ = '\b'; 621 rc++; 622 *p++ = *t; 623 } 624 } 625 } 626 627 *p = ' '; 628 return (rc); 629 } 630 631 static void 632 trim_trailing_spaces(char *s) 633 { 634 char *p; 635 636 for (p = s; *p; ++p) 637 continue; 638 while (p > s && isspace((unsigned char)*--p)) 639 continue; 640 if (p > s) 641 ++p; 642 *p = '\0'; 643 } 644 645 static void 646 center(const char *str, int len, int separate) 647 { 648 649 len -= strlen(str); 650 (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, ""); 651 if (separate) 652 (void)printf("%*s", separate, ""); 653 } 654 655 /* 656 * gregorian_reform -- 657 * Given a description of date on which the Gregorian Reform was 658 * applied. The argument can be any of the "country" names 659 * listed in the reforms array (case insensitive) or a date of 660 * the form YYYY/MM/DD. The date and month can be omitted if 661 * doing so would not select more than one different built-in 662 * reform point. 663 */ 664 static void 665 gregorian_reform(const char *p) 666 { 667 int year, month, date; 668 int i, days, diw, diy; 669 char c; 670 671 i = sscanf(p, "%d%*[/,-]%d%*[/,-]%d%c", &year, &month, &date, &c); 672 switch (i) { 673 case 4: 674 /* 675 * If the character was sscanf()ed, then there's more 676 * stuff than we need. 677 */ 678 errx(1, "date specifier %s invalid", p); 679 case 0: 680 /* 681 * Not a form we can sscanf(), so void these, and we 682 * can try matching "country" names later. 683 */ 684 year = month = date = -1; 685 break; 686 case 1: 687 month = 0; 688 /*FALLTHROUGH*/ 689 case 2: 690 date = 0; 691 /*FALLTHROUGH*/ 692 case 3: 693 /* 694 * At last, some sanity checking on the values we were 695 * given. 696 */ 697 if (year < 1 || year > 9999) 698 errx(1, "%d: illegal year value: use 1-9999", year); 699 if (i > 1 && (month < 1 || month > 12)) 700 errx(1, "%d: illegal month value: use 1-12", month); 701 if ((i == 3 && date < 1) || date < 0 || 702 date > days_in_month[1][month]) 703 /* 704 * What about someone specifying a leap day in 705 * a non-leap year? Well...that's a tricky 706 * one. We can't yet *say* whether the year 707 * in question is a leap year. What if the 708 * date given was, for example, 1700/2/29? is 709 * that a valid leap day? 710 * 711 * So...we punt, and hope that saying 29 in 712 * the case of February isn't too bad an idea. 713 */ 714 errx(1, "%d: illegal date value: use 1-%d", date, 715 days_in_month[1][month]); 716 break; 717 } 718 719 /* 720 * A complete date was specified, so use the other pope. 721 */ 722 if (date > 0) { 723 static struct reform Goestheveezl; 724 725 reform = &Goestheveezl; 726 reform->country = "Bompzidaize"; 727 reform->year = year; 728 reform->month = month; 729 reform->date = date; 730 } 731 732 /* 733 * No date information was specified, so let's try to match on 734 * country name. 735 */ 736 else if (year == -1) { 737 for (reform = &reforms[0]; reform->year; reform++) { 738 if (strcasecmp(p, reform->country) == 0) 739 break; 740 } 741 } 742 743 /* 744 * We have *some* date information, but not a complete date. 745 * Let's see if we have enough to pick a single entry from the 746 * list that's not ambiguous. 747 */ 748 else { 749 for (reform = &reforms[0]; reform->year; reform++) { 750 if ((year == 0 || year == reform->year) && 751 (month == 0 || month == reform->month) && 752 (date == 0 || month == reform->date)) 753 break; 754 } 755 756 if (i <= reform->ambiguity) 757 errx(1, "%s: ambiguous short reform date specification", p); 758 } 759 760 /* 761 * Oops...we reached the end of the list. 762 */ 763 if (reform->year == 0) 764 errx(1, "reform name %s invalid", p); 765 766 /* 767 * 768 */ 769 reform->missing_days = 770 j_leap_days(reform->year, reform->month, reform->date) - 771 g_leap_days(reform->year, reform->month, reform->date) - 772 GREGORIAN_MAGIC; 773 774 reform->first_missing_day = 775 (reform->year - 1) * 365 + 776 day_in_year(reform->date, reform->month, reform->year) + 777 date + 778 j_leap_days(reform->year, reform->month, reform->date); 779 780 /* 781 * Once we know the day of the week of the first missing day, 782 * skip back to the first of the month's day of the week. 783 */ 784 diw = day_in_week(reform->date, reform->month, reform->year); 785 diw = (diw + 8 - (reform->date % 7)) % 7; 786 diy = day_in_year(1, reform->month, reform->year); 787 788 /* 789 * We might need all four of these (if you switch from Julian 790 * to Gregorian at some point after 9900, you get a gap of 73 791 * days, and that can affect four months), and it doesn't hurt 792 * all that much to precompute them, so there. 793 */ 794 date = 1; 795 days = 0; 796 for (i = 0; i < 4; i++) 797 reform_day_array(reform->month + i, reform->year, 798 &days, &date, &diw, &diy, 799 shift_days[0][i], 800 shift_days[1][i]); 801 } 802 803 /* 804 * reform_day_array -- 805 * Pre-calculates the given month's calendar (in both "standard" 806 * and "julian day" representations) with respect for days 807 * skipped during a reform period. 808 */ 809 static void 810 reform_day_array(int month, int year, int *done, int *date, int *diw, int *diy, 811 int *scal, int *jcal) 812 { 813 int mdays; 814 815 /* 816 * If the reform was in the month of october or later, then 817 * the month number from the caller could "overflow". 818 */ 819 if (month > 12) { 820 month -= 12; 821 year++; 822 } 823 824 /* 825 * Erase months, and set crib number. The crib number is used 826 * later to determine if the month to be displayed is here or 827 * should be built on the fly with the generic routine 828 */ 829 memmove(scal, empty, MAXDAYS * sizeof(int)); 830 scal[MAXDAYS] = month + year * 12; 831 memmove(jcal, empty, MAXDAYS * sizeof(int)); 832 jcal[MAXDAYS] = month + year * 12; 833 834 /* 835 * It doesn't matter what the actual month is when figuring 836 * out if this is a leap year or not, just so long as February 837 * gets the right number of days in it. 838 */ 839 mdays = days_in_month[g_leap_year(year, 3, 1)][month]; 840 841 /* 842 * Bounce back to the first "row" in the day array, and fill 843 * in any days that actually occur. 844 */ 845 for (*diw %= 7; (*date - *done) <= mdays; (*date)++, (*diy)++) { 846 /* 847 * "date" doesn't get reset by the caller across calls 848 * to this routine, so we can actually tell that we're 849 * looking at April the 41st. Much easier than trying 850 * to calculate the absolute julian day for a given 851 * date and then checking that. 852 */ 853 if (*date < reform->date || 854 *date >= reform->date + reform->missing_days) { 855 scal[*diw] = *date - *done; 856 jcal[*diw] = *diy; 857 (*diw)++; 858 } 859 } 860 *done += mdays; 861 } 862 863 static int 864 getnum(const char *p) 865 { 866 unsigned long result; 867 char *ep; 868 869 errno = 0; 870 result = strtoul(p, &ep, 10); 871 if (p[0] == '\0' || *ep != '\0') 872 goto error; 873 if (errno == ERANGE && result == ULONG_MAX) 874 goto error; 875 if (result > INT_MAX) 876 goto error; 877 878 return (int)result; 879 880 error: 881 errx(1, "bad number: %s", p); 882 /*NOTREACHED*/ 883 } 884 885 static void 886 init_hilite(void) 887 { 888 const char *term; 889 int errret; 890 891 hilite++; 892 893 if (!isatty(fileno(stdout))) 894 return; 895 896 term = getenv("TERM"); 897 if (term == NULL) 898 term = "dumb"; 899 if (setupterm(term, fileno(stdout), &errret) != 0 && errret != 1) 900 return; 901 902 if (hilite > 1) 903 md = enter_reverse_mode; 904 else 905 md = enter_bold_mode; 906 me = exit_attribute_mode; 907 if (me == NULL || md == NULL) 908 md = me = NULL; 909 } 910 911 static void 912 usage(void) 913 { 914 915 (void)fprintf(stderr, 916 "usage: cal [-3hjry] [-A after] [-B before] [-d day-of-week] " 917 "[-R reform-spec]\n [[month] year]\n"); 918 exit(1); 919 } 920