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