xref: /netbsd-src/usr.bin/cal/cal.c (revision 6a5a56cef14ff3c8ad192471edbe8b06642a2657)
1*6a5a56ceSkim /*	$NetBSD: cal.c,v 1.30 2020/06/29 14:01:14 kim Exp $	*/
22db3772cSglass 
361f28255Scgd /*
42db3772cSglass  * Copyright (c) 1989, 1993, 1994
52db3772cSglass  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kim Letkeman.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
1889aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35b4d27c3aSlukem #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3798e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
3898e5374cSlukem  The Regents of the University of California.  All rights reserved.");
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd #ifndef lint
422db3772cSglass #if 0
432db3772cSglass static char sccsid[] = "@(#)cal.c	8.4 (Berkeley) 4/2/94";
442db3772cSglass #else
45*6a5a56ceSkim __RCSID("$NetBSD: cal.c,v 1.30 2020/06/29 14:01:14 kim Exp $");
462db3772cSglass #endif
4761f28255Scgd #endif /* not lint */
4861f28255Scgd 
4961f28255Scgd #include <sys/types.h>
502db3772cSglass 
5161f28255Scgd #include <ctype.h>
522db3772cSglass #include <err.h>
53c81873c7Syamt #include <errno.h>
54c81873c7Syamt #include <limits.h>
552db3772cSglass #include <stdio.h>
562db3772cSglass #include <stdlib.h>
572db3772cSglass #include <string.h>
5898eb8895Sroy #include <term.h>
592db3772cSglass #include <time.h>
607bc244d5Schristos #include <tzfile.h>
612db3772cSglass #include <unistd.h>
6261f28255Scgd 
6361f28255Scgd #define	SATURDAY 		6		/* 1 Jan 1 was a Saturday */
6461f28255Scgd 
65cfda7110Satatat #define	FIRST_MISSING_DAY 	reform->first_missing_day
66cfda7110Satatat #define	NUMBER_MISSING_DAYS 	reform->missing_days
6761f28255Scgd 
6861f28255Scgd #define	MAXDAYS			42		/* max slots in a month array */
6961f28255Scgd #define	SPACE			-1		/* used in day array */
7061f28255Scgd 
7161f28255Scgd static int days_in_month[2][13] = {
7261f28255Scgd 	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
7361f28255Scgd 	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
7461f28255Scgd };
7561f28255Scgd 
7649dbe234Sjoerg static int empty[MAXDAYS] = {
7761f28255Scgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
7861f28255Scgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
7961f28255Scgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
8061f28255Scgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
8161f28255Scgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
8261f28255Scgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
8361f28255Scgd };
8449dbe234Sjoerg static int shift_days[2][4][MAXDAYS + 1];
8561f28255Scgd 
8649dbe234Sjoerg static const char *month_names[12] = {
8761f28255Scgd 	"January", "February", "March", "April", "May", "June",
8861f28255Scgd 	"July", "August", "September", "October", "November", "December",
8961f28255Scgd };
9061f28255Scgd 
91*6a5a56ceSkim static const char *day_headings = "Su Mo Tu We Th Fr Sa";
92*6a5a56ceSkim static const char *j_day_headings = " Su  Mo  Tu  We  Th  Fr  Sa";
9361f28255Scgd 
94cfda7110Satatat /* leap years according to the julian calendar */
95cfda7110Satatat #define j_leap_year(y, m, d) \
96cfda7110Satatat 	(((m) > 2) && \
97cfda7110Satatat 	 !((y) % 4))
98cfda7110Satatat 
99cfda7110Satatat /* leap years according to the gregorian calendar */
100cfda7110Satatat #define g_leap_year(y, m, d) \
101cfda7110Satatat 	(((m) > 2) && \
102cfda7110Satatat 	 ((!((y) % 4) && ((y) % 100)) || \
103cfda7110Satatat 	  !((y) % 400)))
104cfda7110Satatat 
105cfda7110Satatat /* leap year -- account for gregorian reformation at some point */
10661f28255Scgd #define	leap_year(yr) \
107cfda7110Satatat 	((yr) <= reform->year ? j_leap_year((yr), 3, 1) : \
108cfda7110Satatat 	g_leap_year((yr), 3, 1))
10961f28255Scgd 
110cfda7110Satatat /* number of julian leap days that have passed by a given date */
111cfda7110Satatat #define j_leap_days(y, m, d) \
112cfda7110Satatat 	((((y) - 1) / 4) + j_leap_year(y, m, d))
11361f28255Scgd 
114cfda7110Satatat /* number of gregorian leap days that have passed by a given date */
115cfda7110Satatat #define g_leap_days(y, m, d) \
116cfda7110Satatat 	((((y) - 1) / 4) - (((y) - 1) / 100) + (((y) - 1) / 400) + \
117cfda7110Satatat 	g_leap_year(y, m, d))
118cfda7110Satatat 
119cfda7110Satatat /*
120cfda7110Satatat  * Subtracting the gregorian leap day count (for a given date) from
121cfda7110Satatat  * the julian leap day count (for the same date) describes the number
122cfda7110Satatat  * of days from the date before the shift to the next date that
123cfda7110Satatat  * appears in the calendar.  Since we want to know the number of
124cfda7110Satatat  * *missing* days, not the number of days that the shift spans, we
125cfda7110Satatat  * subtract 2.
126cfda7110Satatat  *
127cfda7110Satatat  * Alternately...
128cfda7110Satatat  *
129cfda7110Satatat  * There's a reason they call the Dark ages the Dark Ages.  Part of it
130cfda7110Satatat  * is that we don't have that many records of that period of time.
131cfda7110Satatat  * One of the reasons for this is that a lot of the Dark Ages never
132cfda7110Satatat  * actually took place.  At some point in the first millenium A.D., a
133cfda7110Satatat  * ruler of some power decided that he wanted the number of the year
134cfda7110Satatat  * to be different than what it was, so he changed it to coincide
135cfda7110Satatat  * nicely with some event (a birthday or anniversary, perhaps a
136cfda7110Satatat  * wedding, or maybe a centennial for a largish city).  One of the
137cfda7110Satatat  * side effects of this upon the Gregorian reform is that two Julian
138cfda7110Satatat  * leap years (leap days celebrated during centennial years that are
139cfda7110Satatat  * not quatro-centennial years) were skipped.
140cfda7110Satatat  */
141cfda7110Satatat #define GREGORIAN_MAGIC 2
142cfda7110Satatat 
143cfda7110Satatat /* number of centuries since the reform, not inclusive */
144cfda7110Satatat #define	centuries_since_reform(yr) \
145cfda7110Satatat 	((yr) > reform->year ? ((yr) / 100) - (reform->year / 100) : 0)
146cfda7110Satatat 
147cfda7110Satatat /* number of centuries since the reform whose modulo of 400 is 0 */
148cfda7110Satatat #define	quad_centuries_since_reform(yr) \
149cfda7110Satatat 	((yr) > reform->year ? ((yr) / 400) - (reform->year / 400) : 0)
15061f28255Scgd 
15161f28255Scgd /* number of leap years between year 1 and this year, not inclusive */
15261f28255Scgd #define	leap_years_since_year_1(yr) \
153cfda7110Satatat 	((yr) / 4 - centuries_since_reform(yr) + quad_centuries_since_reform(yr))
154cfda7110Satatat 
15549dbe234Sjoerg static struct reform {
156cfda7110Satatat 	const char *country;
157cfda7110Satatat 	int ambiguity, year, month, date;
158cfda7110Satatat 	long first_missing_day;
159cfda7110Satatat 	int missing_days;
160cfda7110Satatat 	/*
161cfda7110Satatat 	 * That's 2 for standard/julian display, 4 for months possibly
162cfda7110Satatat 	 * affected by the Gregorian shift, and MAXDAYS + 1 for the
163cfda7110Satatat 	 * days that get displayed, plus a crib slot.
164cfda7110Satatat 	 */
165cfda7110Satatat } *reform, reforms[] = {
16653d5aab9Slukem 	{ "DEFAULT",		0, 1752,  9,  3, 0, 0 },
16753d5aab9Slukem 	{ "Italy",		1, 1582, 10,  5, 0, 0 },
16853d5aab9Slukem 	{ "Spain",		1, 1582, 10,  5, 0, 0 },
16953d5aab9Slukem 	{ "Portugal",		1, 1582, 10,  5, 0, 0 },
17053d5aab9Slukem 	{ "Poland",		1, 1582, 10,  5, 0, 0 },
17153d5aab9Slukem 	{ "France",		2, 1582, 12, 10, 0, 0 },
17253d5aab9Slukem 	{ "Luxembourg",		2, 1582, 12, 22, 0, 0 },
17353d5aab9Slukem 	{ "Netherlands",	2, 1582, 12, 22, 0, 0 },
17453d5aab9Slukem 	{ "Bavaria",		0, 1583, 10,  6, 0, 0 },
17553d5aab9Slukem 	{ "Austria",		2, 1584,  1,  7, 0, 0 },
17653d5aab9Slukem 	{ "Switzerland",	2, 1584,  1, 12, 0, 0 },
17753d5aab9Slukem 	{ "Hungary",		0, 1587, 10, 22, 0, 0 },
17853d5aab9Slukem 	{ "Germany",		0, 1700,  2, 19, 0, 0 },
17953d5aab9Slukem 	{ "Norway",		0, 1700,  2, 19, 0, 0 },
18053d5aab9Slukem 	{ "Denmark",		0, 1700,  2, 19, 0, 0 },
18153d5aab9Slukem 	{ "Great Britain",	0, 1752,  9,  3, 0, 0 },
18253d5aab9Slukem 	{ "England",		0, 1752,  9,  3, 0, 0 },
18353d5aab9Slukem 	{ "America",		0, 1752,  9,  3, 0, 0 },
18453d5aab9Slukem 	{ "Sweden",		0, 1753,  2, 18, 0, 0 },
18553d5aab9Slukem 	{ "Finland",		0, 1753,  2, 18, 0, 0 },
18653d5aab9Slukem 	{ "Japan",		0, 1872, 12, 20, 0, 0 },
18753d5aab9Slukem 	{ "China",		0, 1911, 11,  7, 0, 0 },
18853d5aab9Slukem 	{ "Bulgaria",		0, 1916,  4,  1, 0, 0 },
18953d5aab9Slukem 	{ "U.S.S.R.",		0, 1918,  2,  1, 0, 0 },
19053d5aab9Slukem 	{ "Serbia",		0, 1919,  1, 19, 0, 0 },
19153d5aab9Slukem 	{ "Romania",		0, 1919,  1, 19, 0, 0 },
19253d5aab9Slukem 	{ "Greece",		0, 1924,  3, 10, 0, 0 },
19353d5aab9Slukem 	{ "Turkey",		0, 1925, 12, 19, 0, 0 },
19453d5aab9Slukem 	{ "Egypt",		0, 1928,  9, 18, 0, 0 },
19553d5aab9Slukem 	{ NULL,			0,    0,  0,  0, 0, 0 },
196cfda7110Satatat };
19761f28255Scgd 
19849dbe234Sjoerg static int julian;
19949dbe234Sjoerg static int dow;
20049dbe234Sjoerg static int hilite;
20149dbe234Sjoerg static const char *md, *me;
20261f28255Scgd 
20349dbe234Sjoerg static void	init_hilite(void);
20449dbe234Sjoerg static int	getnum(const char *);
20549dbe234Sjoerg static void	gregorian_reform(const char *);
20649dbe234Sjoerg static void	reform_day_array(int, int, int *, int *, int *,int *,int *,int *);
20749dbe234Sjoerg static int	ascii_day(char *, int);
20849dbe234Sjoerg static void	center(const char *, int, int);
20949dbe234Sjoerg static void	day_array(int, int, int *);
21049dbe234Sjoerg static int	day_in_week(int, int, int);
21149dbe234Sjoerg static int	day_in_year(int, int, int);
21249dbe234Sjoerg static void	monthrange(int, int, int, int, int);
21349dbe234Sjoerg static void	trim_trailing_spaces(char *);
21449dbe234Sjoerg __dead static void	usage(void);
2152db3772cSglass 
2162db3772cSglass int
main(int argc,char ** argv)217534ffc6dSperry main(int argc, char **argv)
21861f28255Scgd {
21961f28255Scgd 	struct tm *local_time;
2202db3772cSglass 	time_t now;
221c98485dbSjoerg 	int ch, yflag;
222c98485dbSjoerg 	long month, year;
223cfda7110Satatat 	int before, after, use_reform;
224c81873c7Syamt 	int yearly = 0;
225c98485dbSjoerg 	char *when, *eoi;
22661f28255Scgd 
227c81873c7Syamt 	before = after = 0;
228cfda7110Satatat 	use_reform = yflag = year = 0;
229cfda7110Satatat 	when = NULL;
230819d4545Schristos 	while ((ch = getopt(argc, argv, "A:B:C:d:hjR:ry3")) != -1) {
23161f28255Scgd 		switch (ch) {
232c81873c7Syamt 		case 'A':
233c81873c7Syamt 			after = getnum(optarg);
2345e6732ebSdholland 			if (after < 0)
2355e6732ebSdholland 				errx(1, "Argument to -A must be positive");
236c81873c7Syamt 			break;
237c81873c7Syamt 		case 'B':
238c81873c7Syamt 			before = getnum(optarg);
2395e6732ebSdholland 			if (before < 0)
2405e6732ebSdholland 				errx(1, "Argument to -B must be positive");
241c81873c7Syamt 			break;
242819d4545Schristos 		case 'C':
243819d4545Schristos 			after = before = getnum(optarg);
244819d4545Schristos 			if (after < 0)
245819d4545Schristos 				errx(1, "Argument to -C must be positive");
246819d4545Schristos 			break;
247cfda7110Satatat 		case 'd':
248cfda7110Satatat 			dow = getnum(optarg);
249cfda7110Satatat 			if (dow < 0 || dow > 6)
250cfda7110Satatat 				errx(1, "illegal day of week value: use 0-6");
251cfda7110Satatat 			break;
25285cee2b4Satatat 		case 'h':
25385cee2b4Satatat 			init_hilite();
25485cee2b4Satatat 			break;
25561f28255Scgd 		case 'j':
25661f28255Scgd 			julian = 1;
25761f28255Scgd 			break;
258cfda7110Satatat 		case 'R':
259cfda7110Satatat 			when = optarg;
260cfda7110Satatat 			break;
261cfda7110Satatat 		case 'r':
262cfda7110Satatat 			use_reform = 1;
263cfda7110Satatat 			break;
26461f28255Scgd 		case 'y':
26561f28255Scgd 			yflag = 1;
26661f28255Scgd 			break;
267037dfc0eSperry 		case '3':
268c81873c7Syamt 			before = after = 1;
269037dfc0eSperry 			break;
27061f28255Scgd 		case '?':
27161f28255Scgd 		default:
27261f28255Scgd 			usage();
273534ffc6dSperry 			/* NOTREACHED */
274534ffc6dSperry 		}
27561f28255Scgd 	}
276037dfc0eSperry 
27761f28255Scgd 	argc -= optind;
27861f28255Scgd 	argv += optind;
27961f28255Scgd 
280cfda7110Satatat 	if (when != NULL)
281cfda7110Satatat 		gregorian_reform(when);
282cfda7110Satatat 	if (reform == NULL)
283cfda7110Satatat 		gregorian_reform("DEFAULT");
284cfda7110Satatat 
28561f28255Scgd 	month = 0;
28661f28255Scgd 	switch (argc) {
28761f28255Scgd 	case 2:
288c98485dbSjoerg 		month = strtol(*argv++, &eoi, 10);
289c98485dbSjoerg 		if (month < 1 || month > 12 || *eoi != '\0')
2902db3772cSglass 			errx(1, "illegal month value: use 1-12");
291c98485dbSjoerg 		year = strtol(*argv, &eoi, 10);
292c98485dbSjoerg 		if (year < 1 || year > 9999 || *eoi != '\0')
2932db3772cSglass 			errx(1, "illegal year value: use 1-9999");
29461f28255Scgd 		break;
295c98485dbSjoerg 	case 1:
296c98485dbSjoerg 		year = strtol(*argv, &eoi, 10);
297358c956dSjoerg 		if (year < 1 || year > 9999 || (*eoi != '\0' && *eoi != '/' && *eoi != '-'))
298c98485dbSjoerg 			errx(1, "illegal year value: use 1-9999");
299358c956dSjoerg 		if (*eoi != '\0') {
300c98485dbSjoerg 			month = strtol(eoi + 1, &eoi, 10);
301c98485dbSjoerg 			if (month < 1 || month > 12 || *eoi != '\0')
302c98485dbSjoerg 				errx(1, "illegal month value: use 1-12");
303c98485dbSjoerg 		}
304c98485dbSjoerg 		break;
30561f28255Scgd 	case 0:
30661f28255Scgd 		(void)time(&now);
30761f28255Scgd 		local_time = localtime(&now);
308cfda7110Satatat 		if (use_reform)
309cfda7110Satatat 			year = reform->year;
310cfda7110Satatat 		else
3117bc244d5Schristos 			year = local_time->tm_year + TM_YEAR_BASE;
312cfda7110Satatat 		if (!yflag) {
313cfda7110Satatat 			if (use_reform)
314cfda7110Satatat 				month = reform->month;
315cfda7110Satatat 			else
31661f28255Scgd 				month = local_time->tm_mon + 1;
317cfda7110Satatat 		}
31861f28255Scgd 		break;
31961f28255Scgd 	default:
32061f28255Scgd 		usage();
32161f28255Scgd 	}
32261f28255Scgd 
323c81873c7Syamt 	if (!month) {
324c81873c7Syamt 		/* yearly */
325c81873c7Syamt 		month = 1;
326c81873c7Syamt 		before = 0;
327c81873c7Syamt 		after = 11;
328c81873c7Syamt 		yearly = 1;
329c81873c7Syamt 	}
330c81873c7Syamt 
331c81873c7Syamt 	monthrange(month, year, before, after, yearly);
332037dfc0eSperry 
33361f28255Scgd 	exit(0);
33461f28255Scgd }
33561f28255Scgd 
33661f28255Scgd #define	DAY_LEN		3		/* 3 spaces per day */
33761f28255Scgd #define	J_DAY_LEN	4		/* 4 spaces per day */
33861f28255Scgd #define	WEEK_LEN	20		/* 7 * 3 - one space at the end */
33961f28255Scgd #define	J_WEEK_LEN	27		/* 7 * 4 - one space at the end */
34061f28255Scgd #define	HEAD_SEP	2		/* spaces between day headings */
34161f28255Scgd #define	J_HEAD_SEP	2
342c81873c7Syamt #define	MONTH_PER_ROW	3		/* how many monthes in a row */
343c81873c7Syamt #define	J_MONTH_PER_ROW	2
34461f28255Scgd 
34549dbe234Sjoerg static void
monthrange(int month,int year,int before,int after,int yearly)346c81873c7Syamt monthrange(int month, int year, int before, int after, int yearly)
34761f28255Scgd {
348c81873c7Syamt 	int startmonth, startyear;
349c81873c7Syamt 	int endmonth, endyear;
350c81873c7Syamt 	int i, row;
351c81873c7Syamt 	int days[3][MAXDAYS];
35285cee2b4Satatat 	char lineout[256];
353c81873c7Syamt 	int inayear;
354c81873c7Syamt 	int newyear;
355c81873c7Syamt 	int day_len, week_len, head_sep;
356c81873c7Syamt 	int month_per_row;
35785cee2b4Satatat 	int skip, r_off, w_off;
35861f28255Scgd 
359c81873c7Syamt 	if (julian) {
360c81873c7Syamt 		day_len = J_DAY_LEN;
361c81873c7Syamt 		week_len = J_WEEK_LEN;
362c81873c7Syamt 		head_sep = J_HEAD_SEP;
363c81873c7Syamt 		month_per_row = J_MONTH_PER_ROW;
36461f28255Scgd 	}
365c81873c7Syamt 	else {
366c81873c7Syamt 		day_len = DAY_LEN;
367c81873c7Syamt 		week_len = WEEK_LEN;
368c81873c7Syamt 		head_sep = HEAD_SEP;
369c81873c7Syamt 		month_per_row = MONTH_PER_ROW;
37061f28255Scgd 	}
37161f28255Scgd 
372037dfc0eSperry 	month--;
373037dfc0eSperry 
374c81873c7Syamt 	startyear = year - (before + 12 - 1 - month) / 12;
375c81873c7Syamt 	startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12);
376c81873c7Syamt 	endyear = year + (month + after) / 12;
377c81873c7Syamt 	endmonth = (month + after) % 12;
378037dfc0eSperry 
379c81873c7Syamt 	if (startyear < 0 || endyear > 9999) {
380ff532697Schristos 		errx(1, "year should be in 1-9999");
381c81873c7Syamt 	}
382037dfc0eSperry 
383c81873c7Syamt 	year = startyear;
384c81873c7Syamt 	month = startmonth;
385c81873c7Syamt 	inayear = newyear = (year != endyear || yearly);
386c81873c7Syamt 	if (inayear) {
387c81873c7Syamt 		skip = month % month_per_row;
388c81873c7Syamt 		month -= skip;
389c81873c7Syamt 	}
390c81873c7Syamt 	else {
391c81873c7Syamt 		skip = 0;
392c81873c7Syamt 	}
393c81873c7Syamt 
394c81873c7Syamt 	do {
395c81873c7Syamt 		if (newyear) {
396c81873c7Syamt 			(void)snprintf(lineout, sizeof(lineout), "%d", year);
397c81873c7Syamt 			center(lineout, week_len * month_per_row +
398c81873c7Syamt 			    head_sep * (month_per_row - 1), 0);
399c81873c7Syamt 			(void)printf("\n\n");
400c81873c7Syamt 			newyear = 0;
401c81873c7Syamt 		}
402c81873c7Syamt 
403c81873c7Syamt 		for (i = 0; i < skip; i++)
404c81873c7Syamt 			center("", week_len, head_sep);
405c81873c7Syamt 
406c81873c7Syamt 		for (; i < month_per_row; i++) {
407c81873c7Syamt 			int sep;
408c81873c7Syamt 
409c81873c7Syamt 			if (year == endyear && month + i > endmonth)
410c81873c7Syamt 				break;
411c81873c7Syamt 
412c81873c7Syamt 			sep = (i == month_per_row - 1) ? 0 : head_sep;
413c81873c7Syamt 			day_array(month + i + 1, year, days[i]);
414c81873c7Syamt 			if (inayear) {
415c81873c7Syamt 				center(month_names[month + i], week_len, sep);
416c81873c7Syamt 			}
417c81873c7Syamt 			else {
418c81873c7Syamt 				snprintf(lineout, sizeof(lineout), "%s %d",
419c81873c7Syamt 				    month_names[month + i], year);
420c81873c7Syamt 				center(lineout, week_len, sep);
421c81873c7Syamt 			}
422c81873c7Syamt 		}
423c81873c7Syamt 		printf("\n");
424c81873c7Syamt 
425c81873c7Syamt 		for (i = 0; i < skip; i++)
426c81873c7Syamt 			center("", week_len, head_sep);
427c81873c7Syamt 
428c81873c7Syamt 		for (; i < month_per_row; i++) {
429c81873c7Syamt 			int sep;
430c81873c7Syamt 
431c81873c7Syamt 			if (year == endyear && month + i > endmonth)
432c81873c7Syamt 				break;
433c81873c7Syamt 
434c81873c7Syamt 			sep = (i == month_per_row - 1) ? 0 : head_sep;
435cfda7110Satatat 			if (dow) {
436cfda7110Satatat 				printf("%s ", (julian) ?
437cfda7110Satatat 				    j_day_headings + 4 * dow :
438cfda7110Satatat 				    day_headings + 3 * dow);
439cfda7110Satatat 				printf("%.*s", dow * (julian ? 4 : 3) - 1,
440cfda7110Satatat 				       (julian) ? j_day_headings : day_headings);
441cfda7110Satatat 			} else
442cfda7110Satatat 				printf("%s", (julian) ? j_day_headings : day_headings);
443cfda7110Satatat 			printf("%*s", sep, "");
444c81873c7Syamt 		}
445c81873c7Syamt 		printf("\n");
446c81873c7Syamt 
447037dfc0eSperry 		for (row = 0; row < 6; row++) {
448658ed336Slukem 			char *p = NULL;
449808b43aeSyamt 
450808b43aeSyamt 			memset(lineout, ' ', sizeof(lineout));
451c81873c7Syamt 			for (i = 0; i < skip; i++) {
452808b43aeSyamt 				/* nothing */
453c81873c7Syamt 			}
45485cee2b4Satatat 			w_off = 0;
455c81873c7Syamt 			for (; i < month_per_row; i++) {
456c81873c7Syamt 				int col, *dp;
457c81873c7Syamt 
458c81873c7Syamt 				if (year == endyear && month + i > endmonth)
459c81873c7Syamt 					break;
460c81873c7Syamt 
46185cee2b4Satatat 				p = lineout + i * (week_len + 2) + w_off;
462c81873c7Syamt 				dp = &days[i][row * 7];
46385cee2b4Satatat 				for (col = 0; col < 7;
46485cee2b4Satatat 				     col++, p += day_len + r_off) {
46585cee2b4Satatat 					r_off = ascii_day(p, *dp++);
46685cee2b4Satatat 					w_off += r_off;
46785cee2b4Satatat 				}
468037dfc0eSperry 			}
469037dfc0eSperry 			*p = '\0';
470037dfc0eSperry 			trim_trailing_spaces(lineout);
471037dfc0eSperry 			(void)printf("%s\n", lineout);
472037dfc0eSperry 		}
473c81873c7Syamt 
474c81873c7Syamt 		skip = 0;
475c81873c7Syamt 		month += month_per_row;
476c81873c7Syamt 		if (month >= 12) {
477c81873c7Syamt 			month -= 12;
478c81873c7Syamt 			year++;
479c81873c7Syamt 			newyear = 1;
480c81873c7Syamt 		}
481c81873c7Syamt 	} while (year < endyear || (year == endyear && month <= endmonth));
482037dfc0eSperry }
483037dfc0eSperry 
48461f28255Scgd /*
48561f28255Scgd  * day_array --
48661f28255Scgd  *	Fill in an array of 42 integers with a calendar.  Assume for a moment
48761f28255Scgd  *	that you took the (maximum) 6 rows in a calendar and stretched them
48861f28255Scgd  *	out end to end.  You would have 42 numbers or spaces.  This routine
48961f28255Scgd  *	builds that array for any month from Jan. 1 through Dec. 9999.
49061f28255Scgd  */
49149dbe234Sjoerg static void
day_array(int month,int year,int * days)492534ffc6dSperry day_array(int month, int year, int *days)
49361f28255Scgd {
4942db3772cSglass 	int day, dw, dm;
49585cee2b4Satatat 	time_t t;
49685cee2b4Satatat 	struct tm *tm;
49785cee2b4Satatat 
49885cee2b4Satatat 	t = time(NULL);
49985cee2b4Satatat 	tm = localtime(&t);
50085cee2b4Satatat 	tm->tm_year += TM_YEAR_BASE;
50185cee2b4Satatat 	tm->tm_mon++;
50285cee2b4Satatat 	tm->tm_yday++; /* jan 1 is 1 for us, not 0 */
50361f28255Scgd 
504cfda7110Satatat 	for (dm = month + year * 12, dw = 0; dw < 4; dw++) {
505cfda7110Satatat 		if (dm == shift_days[julian][dw][MAXDAYS]) {
506cfda7110Satatat 			memmove(days, shift_days[julian][dw],
507cfda7110Satatat 				MAXDAYS * sizeof(int));
50861f28255Scgd 			return;
50961f28255Scgd 		}
510cfda7110Satatat 	}
511cfda7110Satatat 
5122db3772cSglass 	memmove(days, empty, MAXDAYS * sizeof(int));
51361f28255Scgd 	dm = days_in_month[leap_year(year)][month];
51461f28255Scgd 	dw = day_in_week(1, month, year);
51561f28255Scgd 	day = julian ? day_in_year(1, month, year) : 1;
51685cee2b4Satatat 	while (dm--) {
51785cee2b4Satatat 		if (hilite && year == tm->tm_year &&
51885cee2b4Satatat 		    (julian ? (day == tm->tm_yday) :
51985cee2b4Satatat 		     (month == tm->tm_mon && day == tm->tm_mday)))
520cfda7110Satatat 			days[dw++] = SPACE - day++;
52185cee2b4Satatat 		else
52261f28255Scgd 			days[dw++] = day++;
52361f28255Scgd 	}
52485cee2b4Satatat }
52561f28255Scgd 
52661f28255Scgd /*
52761f28255Scgd  * day_in_year --
52861f28255Scgd  *	return the 1 based day number within the year
52961f28255Scgd  */
53049dbe234Sjoerg static int
day_in_year(int day,int month,int year)531534ffc6dSperry day_in_year(int day, int month, int year)
53261f28255Scgd {
5332db3772cSglass 	int i, leap;
53461f28255Scgd 
53561f28255Scgd 	leap = leap_year(year);
53661f28255Scgd 	for (i = 1; i < month; i++)
53761f28255Scgd 		day += days_in_month[leap][i];
53861f28255Scgd 	return (day);
53961f28255Scgd }
54061f28255Scgd 
54161f28255Scgd /*
54261f28255Scgd  * day_in_week
54361f28255Scgd  *	return the 0 based day number for any date from 1 Jan. 1 to
544cfda7110Satatat  *	31 Dec. 9999.  Returns the day of the week of the first
545cfda7110Satatat  *	missing day for any given Gregorian shift.
54661f28255Scgd  */
54749dbe234Sjoerg static int
day_in_week(int day,int month,int year)548534ffc6dSperry day_in_week(int day, int month, int year)
54961f28255Scgd {
55061f28255Scgd 	long temp;
55161f28255Scgd 
55261f28255Scgd 	temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
55361f28255Scgd 	    + day_in_year(day, month, year);
55461f28255Scgd 	if (temp < FIRST_MISSING_DAY)
555cfda7110Satatat 		return ((temp - dow + 6 + SATURDAY) % 7);
55661f28255Scgd 	if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
557cfda7110Satatat 		return (((temp - dow + 6 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
558cfda7110Satatat 	return ((FIRST_MISSING_DAY - dow + 6 + SATURDAY) % 7);
55961f28255Scgd }
56061f28255Scgd 
56149dbe234Sjoerg static int
ascii_day(char * p,int day)562534ffc6dSperry ascii_day(char *p, int day)
56361f28255Scgd {
56485cee2b4Satatat 	int display, val, rc;
56585cee2b4Satatat 	char *b;
56653d5aab9Slukem 	static const char *aday[] = {
56761f28255Scgd 		"",
56861f28255Scgd 		" 1", " 2", " 3", " 4", " 5", " 6", " 7",
56961f28255Scgd 		" 8", " 9", "10", "11", "12", "13", "14",
57061f28255Scgd 		"15", "16", "17", "18", "19", "20", "21",
57161f28255Scgd 		"22", "23", "24", "25", "26", "27", "28",
57261f28255Scgd 		"29", "30", "31",
57361f28255Scgd 	};
57461f28255Scgd 
57561f28255Scgd 	if (day == SPACE) {
57661f28255Scgd 		memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
57785cee2b4Satatat 		return (0);
57861f28255Scgd 	}
579cfda7110Satatat 	if (day < SPACE) {
58085cee2b4Satatat 		b = p;
581cfda7110Satatat 		day = SPACE - day;
58285cee2b4Satatat 	} else
58385cee2b4Satatat 		b = NULL;
58461f28255Scgd 	if (julian) {
585b4d27c3aSlukem 		if ((val = day / 100) != 0) {
58661f28255Scgd 			day %= 100;
58761f28255Scgd 			*p++ = val + '0';
58861f28255Scgd 			display = 1;
58961f28255Scgd 		} else {
59061f28255Scgd 			*p++ = ' ';
59161f28255Scgd 			display = 0;
59261f28255Scgd 		}
59361f28255Scgd 		val = day / 10;
59461f28255Scgd 		if (val || display)
59561f28255Scgd 			*p++ = val + '0';
59661f28255Scgd 		else
59761f28255Scgd 			*p++ = ' ';
59861f28255Scgd 		*p++ = day % 10 + '0';
59961f28255Scgd 	} else {
60061f28255Scgd 		*p++ = aday[day][0];
60161f28255Scgd 		*p++ = aday[day][1];
60261f28255Scgd 	}
60385cee2b4Satatat 
60485cee2b4Satatat 	rc = 0;
60585cee2b4Satatat 	if (b != NULL) {
60698eb8895Sroy 		const char *t;
60798eb8895Sroy 		char h[64];
60885cee2b4Satatat 		int l;
60985cee2b4Satatat 
61085cee2b4Satatat 		l = p - b;
61185cee2b4Satatat 		memcpy(h, b, l);
61285cee2b4Satatat 		p = b;
61385cee2b4Satatat 
61485cee2b4Satatat 		if (md != NULL) {
61585cee2b4Satatat 			for (t = md; *t; rc++)
61685cee2b4Satatat 				*p++ = *t++;
61785cee2b4Satatat 			memcpy(p, h, l);
61885cee2b4Satatat 			p += l;
61985cee2b4Satatat 			for (t = me; *t; rc++)
62085cee2b4Satatat 				*p++ = *t++;
62185cee2b4Satatat 		} else {
62285cee2b4Satatat 			for (t = &h[0]; l--; t++) {
62385cee2b4Satatat 				*p++ = *t;
62485cee2b4Satatat 				rc++;
62585cee2b4Satatat 				*p++ = '\b';
62685cee2b4Satatat 				rc++;
62785cee2b4Satatat 				*p++ = *t;
62885cee2b4Satatat 			}
62985cee2b4Satatat 		}
63085cee2b4Satatat 	}
63185cee2b4Satatat 
63261f28255Scgd 	*p = ' ';
63385cee2b4Satatat 	return (rc);
63461f28255Scgd }
63561f28255Scgd 
63649dbe234Sjoerg static void
trim_trailing_spaces(char * s)637534ffc6dSperry trim_trailing_spaces(char *s)
63861f28255Scgd {
6392db3772cSglass 	char *p;
64061f28255Scgd 
6412db3772cSglass 	for (p = s; *p; ++p)
6422db3772cSglass 		continue;
643ac193186Schristos 	while (p > s && isspace((unsigned char)*--p))
6442db3772cSglass 		continue;
64561f28255Scgd 	if (p > s)
64661f28255Scgd 		++p;
64761f28255Scgd 	*p = '\0';
64861f28255Scgd }
64961f28255Scgd 
65049dbe234Sjoerg static void
center(const char * str,int len,int separate)65153d5aab9Slukem center(const char *str, int len, int separate)
65261f28255Scgd {
6532db3772cSglass 
65461f28255Scgd 	len -= strlen(str);
65561f28255Scgd 	(void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
65661f28255Scgd 	if (separate)
65761f28255Scgd 		(void)printf("%*s", separate, "");
65861f28255Scgd }
65961f28255Scgd 
660cfda7110Satatat /*
661cfda7110Satatat  * gregorian_reform --
662cfda7110Satatat  *	Given a description of date on which the Gregorian Reform was
663cfda7110Satatat  *	applied.  The argument can be any of the "country" names
664cfda7110Satatat  *	listed in the reforms array (case insensitive) or a date of
665cfda7110Satatat  *	the form YYYY/MM/DD.  The date and month can be omitted if
666cfda7110Satatat  *	doing so would not select more than one different built-in
667cfda7110Satatat  *	reform point.
668cfda7110Satatat  */
66949dbe234Sjoerg static void
gregorian_reform(const char * p)670cfda7110Satatat gregorian_reform(const char *p)
671cfda7110Satatat {
672cfda7110Satatat 	int year, month, date;
673cfda7110Satatat 	int i, days, diw, diy;
674cfda7110Satatat 	char c;
675cfda7110Satatat 
676cfda7110Satatat 	i = sscanf(p, "%d%*[/,-]%d%*[/,-]%d%c", &year, &month, &date, &c);
677cfda7110Satatat 	switch (i) {
678cfda7110Satatat 	case 4:
679cfda7110Satatat 		/*
680cfda7110Satatat 		 * If the character was sscanf()ed, then there's more
681cfda7110Satatat 		 * stuff than we need.
682cfda7110Satatat 		 */
683cfda7110Satatat 		errx(1, "date specifier %s invalid", p);
684cfda7110Satatat 	case 0:
685cfda7110Satatat 		/*
686cfda7110Satatat 		 * Not a form we can sscanf(), so void these, and we
687cfda7110Satatat 		 * can try matching "country" names later.
688cfda7110Satatat 		 */
689cfda7110Satatat 		year = month = date = -1;
690cfda7110Satatat 		break;
691cfda7110Satatat 	case 1:
692cfda7110Satatat 		month = 0;
693cfda7110Satatat 		/*FALLTHROUGH*/
694cfda7110Satatat 	case 2:
695cfda7110Satatat 		date = 0;
696cfda7110Satatat 		/*FALLTHROUGH*/
697cfda7110Satatat 	    case 3:
698cfda7110Satatat 		/*
699cfda7110Satatat 		 * At last, some sanity checking on the values we were
700cfda7110Satatat 		 * given.
701cfda7110Satatat 		 */
702cfda7110Satatat 		if (year < 1 || year > 9999)
703cfda7110Satatat 			errx(1, "%d: illegal year value: use 1-9999", year);
704cfda7110Satatat 		if (i > 1 && (month < 1 || month > 12))
705cfda7110Satatat 			errx(1, "%d: illegal month value: use 1-12", month);
706cfda7110Satatat 		if ((i == 3 && date < 1) || date < 0 ||
707cfda7110Satatat 		    date > days_in_month[1][month])
708cfda7110Satatat 			/*
709cfda7110Satatat 			 * What about someone specifying a leap day in
710cfda7110Satatat 			 * a non-leap year?  Well...that's a tricky
711cfda7110Satatat 			 * one.  We can't yet *say* whether the year
712cfda7110Satatat 			 * in question is a leap year.  What if the
713cfda7110Satatat 			 * date given was, for example, 1700/2/29?  is
714cfda7110Satatat 			 * that a valid leap day?
715cfda7110Satatat 			 *
716cfda7110Satatat 			 * So...we punt, and hope that saying 29 in
717cfda7110Satatat 			 * the case of February isn't too bad an idea.
718cfda7110Satatat 			 */
719cfda7110Satatat 			errx(1, "%d: illegal date value: use 1-%d", date,
720cfda7110Satatat 			     days_in_month[1][month]);
721cfda7110Satatat 		break;
722cfda7110Satatat 	}
723cfda7110Satatat 
724cfda7110Satatat 	/*
725cfda7110Satatat 	 * A complete date was specified, so use the other pope.
726cfda7110Satatat 	 */
727cfda7110Satatat 	if (date > 0) {
728cfda7110Satatat 		static struct reform Goestheveezl;
729cfda7110Satatat 
730cfda7110Satatat 		reform = &Goestheveezl;
731cfda7110Satatat 		reform->country = "Bompzidaize";
732cfda7110Satatat 		reform->year = year;
733cfda7110Satatat 		reform->month = month;
734cfda7110Satatat 		reform->date = date;
735cfda7110Satatat 	}
736cfda7110Satatat 
737cfda7110Satatat 	/*
738cfda7110Satatat 	 * No date information was specified, so let's try to match on
739cfda7110Satatat 	 * country name.
740cfda7110Satatat 	 */
741cfda7110Satatat 	else if (year == -1) {
742cfda7110Satatat 		for (reform = &reforms[0]; reform->year; reform++) {
743cfda7110Satatat 			if (strcasecmp(p, reform->country) == 0)
744cfda7110Satatat 				break;
745cfda7110Satatat 		}
746cfda7110Satatat 	}
747cfda7110Satatat 
748cfda7110Satatat 	/*
749cfda7110Satatat 	 * We have *some* date information, but not a complete date.
750cfda7110Satatat 	 * Let's see if we have enough to pick a single entry from the
751cfda7110Satatat 	 * list that's not ambiguous.
752cfda7110Satatat 	 */
753cfda7110Satatat 	else {
754cfda7110Satatat 		for (reform = &reforms[0]; reform->year; reform++) {
755cfda7110Satatat 			if ((year == 0 || year == reform->year) &&
756cfda7110Satatat 			    (month == 0 || month == reform->month) &&
757cfda7110Satatat 			    (date == 0 || month == reform->date))
758cfda7110Satatat 				break;
759cfda7110Satatat 		}
760cfda7110Satatat 
761cfda7110Satatat 		if (i <= reform->ambiguity)
762cfda7110Satatat 			errx(1, "%s: ambiguous short reform date specification", p);
763cfda7110Satatat 	}
764cfda7110Satatat 
765cfda7110Satatat 	/*
766cfda7110Satatat 	 * Oops...we reached the end of the list.
767cfda7110Satatat 	 */
768cfda7110Satatat 	if (reform->year == 0)
769cfda7110Satatat 		errx(1, "reform name %s invalid", p);
770cfda7110Satatat 
771cfda7110Satatat 	/*
772cfda7110Satatat 	 *
773cfda7110Satatat 	 */
774cfda7110Satatat 	reform->missing_days =
775cfda7110Satatat 		j_leap_days(reform->year, reform->month, reform->date) -
776cfda7110Satatat 		g_leap_days(reform->year, reform->month, reform->date) -
777cfda7110Satatat 		GREGORIAN_MAGIC;
778cfda7110Satatat 
779cfda7110Satatat 	reform->first_missing_day =
780cfda7110Satatat 		(reform->year - 1) * 365 +
781cfda7110Satatat 		day_in_year(reform->date, reform->month, reform->year) +
782cfda7110Satatat 		date +
783cfda7110Satatat 		j_leap_days(reform->year, reform->month, reform->date);
784cfda7110Satatat 
785cfda7110Satatat 	/*
786cfda7110Satatat 	 * Once we know the day of the week of the first missing day,
787cfda7110Satatat 	 * skip back to the first of the month's day of the week.
788cfda7110Satatat 	 */
789cfda7110Satatat 	diw = day_in_week(reform->date, reform->month, reform->year);
790cfda7110Satatat 	diw = (diw + 8 - (reform->date % 7)) % 7;
791cfda7110Satatat 	diy = day_in_year(1, reform->month, reform->year);
792cfda7110Satatat 
793cfda7110Satatat 	/*
794cfda7110Satatat 	 * We might need all four of these (if you switch from Julian
795cfda7110Satatat 	 * to Gregorian at some point after 9900, you get a gap of 73
796cfda7110Satatat 	 * days, and that can affect four months), and it doesn't hurt
797cfda7110Satatat 	 * all that much to precompute them, so there.
798cfda7110Satatat 	 */
799cfda7110Satatat 	date = 1;
800cfda7110Satatat 	days = 0;
801cfda7110Satatat 	for (i = 0; i < 4; i++)
802cfda7110Satatat 		reform_day_array(reform->month + i, reform->year,
803cfda7110Satatat 				 &days, &date, &diw, &diy,
804cfda7110Satatat 				 shift_days[0][i],
805cfda7110Satatat 				 shift_days[1][i]);
806cfda7110Satatat }
807cfda7110Satatat 
808cfda7110Satatat /*
809cfda7110Satatat  * reform_day_array --
810cfda7110Satatat  *	Pre-calculates the given month's calendar (in both "standard"
811cfda7110Satatat  *	and "julian day" representations) with respect for days
812cfda7110Satatat  *	skipped during a reform period.
813cfda7110Satatat  */
81449dbe234Sjoerg static void
reform_day_array(int month,int year,int * done,int * date,int * diw,int * diy,int * scal,int * jcal)815cfda7110Satatat reform_day_array(int month, int year, int *done, int *date, int *diw, int *diy,
816cfda7110Satatat 	int *scal, int *jcal)
817cfda7110Satatat {
818cfda7110Satatat 	int mdays;
819cfda7110Satatat 
820cfda7110Satatat 	/*
821cfda7110Satatat 	 * If the reform was in the month of october or later, then
822cfda7110Satatat 	 * the month number from the caller could "overflow".
823cfda7110Satatat 	 */
824cfda7110Satatat 	if (month > 12) {
825cfda7110Satatat 		month -= 12;
826cfda7110Satatat 		year++;
827cfda7110Satatat 	}
828cfda7110Satatat 
829cfda7110Satatat 	/*
830cfda7110Satatat 	 * Erase months, and set crib number.  The crib number is used
831cfda7110Satatat 	 * later to determine if the month to be displayed is here or
832cfda7110Satatat 	 * should be built on the fly with the generic routine
833cfda7110Satatat 	 */
834cfda7110Satatat 	memmove(scal, empty, MAXDAYS * sizeof(int));
835cfda7110Satatat 	scal[MAXDAYS] = month + year * 12;
836cfda7110Satatat 	memmove(jcal, empty, MAXDAYS * sizeof(int));
837cfda7110Satatat 	jcal[MAXDAYS] = month + year * 12;
838cfda7110Satatat 
839cfda7110Satatat 	/*
840cfda7110Satatat 	 * It doesn't matter what the actual month is when figuring
841cfda7110Satatat 	 * out if this is a leap year or not, just so long as February
842cfda7110Satatat 	 * gets the right number of days in it.
843cfda7110Satatat 	 */
844cfda7110Satatat 	mdays = days_in_month[g_leap_year(year, 3, 1)][month];
845cfda7110Satatat 
846cfda7110Satatat 	/*
847cfda7110Satatat 	 * Bounce back to the first "row" in the day array, and fill
848cfda7110Satatat 	 * in any days that actually occur.
849cfda7110Satatat 	 */
850cfda7110Satatat 	for (*diw %= 7; (*date - *done) <= mdays; (*date)++, (*diy)++) {
851cfda7110Satatat 		/*
852cfda7110Satatat 		 * "date" doesn't get reset by the caller across calls
853cfda7110Satatat 		 * to this routine, so we can actually tell that we're
854cfda7110Satatat 		 * looking at April the 41st.  Much easier than trying
855cfda7110Satatat 		 * to calculate the absolute julian day for a given
856cfda7110Satatat 		 * date and then checking that.
857cfda7110Satatat 		 */
858cfda7110Satatat 		if (*date < reform->date ||
859cfda7110Satatat 		    *date >= reform->date + reform->missing_days) {
860cfda7110Satatat 			scal[*diw] = *date - *done;
861cfda7110Satatat 			jcal[*diw] = *diy;
862cfda7110Satatat 			(*diw)++;
863cfda7110Satatat 		}
864cfda7110Satatat 	}
865cfda7110Satatat 	*done += mdays;
866cfda7110Satatat }
867cfda7110Satatat 
86849dbe234Sjoerg static int
getnum(const char * p)869c81873c7Syamt getnum(const char *p)
870c81873c7Syamt {
87153d5aab9Slukem 	unsigned long result;
872c81873c7Syamt 	char *ep;
873c81873c7Syamt 
874c81873c7Syamt 	errno = 0;
875c81873c7Syamt 	result = strtoul(p, &ep, 10);
876c81873c7Syamt 	if (p[0] == '\0' || *ep != '\0')
877c81873c7Syamt 		goto error;
878c81873c7Syamt 	if (errno == ERANGE && result == ULONG_MAX)
879c81873c7Syamt 		goto error;
880c81873c7Syamt 	if (result > INT_MAX)
881c81873c7Syamt 		goto error;
882c81873c7Syamt 
883c81873c7Syamt 	return (int)result;
884c81873c7Syamt 
885c81873c7Syamt error:
886c81873c7Syamt 	errx(1, "bad number: %s", p);
887c81873c7Syamt 	/*NOTREACHED*/
888c81873c7Syamt }
889c81873c7Syamt 
89049dbe234Sjoerg static void
init_hilite(void)89185cee2b4Satatat init_hilite(void)
89285cee2b4Satatat {
89353d5aab9Slukem 	const char *term;
89498eb8895Sroy 	int errret;
89585cee2b4Satatat 
89685cee2b4Satatat 	hilite++;
89785cee2b4Satatat 
89885cee2b4Satatat 	if (!isatty(fileno(stdout)))
89985cee2b4Satatat 		return;
90085cee2b4Satatat 
90153d5aab9Slukem 	term = getenv("TERM");
90253d5aab9Slukem 	if (term == NULL)
90353d5aab9Slukem 		term = "dumb";
90498eb8895Sroy 	if (setupterm(term, fileno(stdout), &errret) != 0 && errret != 1)
90585cee2b4Satatat 		return;
90685cee2b4Satatat 
90798eb8895Sroy 	if (hilite > 1)
90898eb8895Sroy 		md = enter_reverse_mode;
90998eb8895Sroy 	else
91098eb8895Sroy 		md = enter_bold_mode;
91198eb8895Sroy 	me = exit_attribute_mode;
91285cee2b4Satatat 	if (me == NULL || md == NULL)
91385cee2b4Satatat 		md = me = NULL;
91485cee2b4Satatat }
91585cee2b4Satatat 
91649dbe234Sjoerg static void
usage(void)917534ffc6dSperry usage(void)
91861f28255Scgd {
9192db3772cSglass 
920c81873c7Syamt 	(void)fprintf(stderr,
921819d4545Schristos 	    "usage: cal [-3hjry] [-A after] [-B before] [-C context] [-d day-of-week] "
922cfda7110Satatat 	    "[-R reform-spec]\n           [[month] year]\n");
92361f28255Scgd 	exit(1);
92461f28255Scgd }
925