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