xref: /csrg-svn/usr.bin/cal/cal.c (revision 66647)
139234Sbostic /*
2*66647Spendry  * Copyright (c) 1989, 1993, 1994
361929Sbostic  *	The Regents of the University of California.  All rights reserved.
439234Sbostic  *
539234Sbostic  * This code is derived from software contributed to Berkeley by
639234Sbostic  * Kim Letkeman.
739234Sbostic  *
842720Sbostic  * %sccs.include.redist.c%
939234Sbostic  */
1039234Sbostic 
1114481Ssam #ifndef lint
1261929Sbostic static char copyright[] =
13*66647Spendry "@(#) Copyright (c) 1989, 1993, 1994\n\
1461929Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1539234Sbostic #endif /* not lint */
1614481Ssam 
1739234Sbostic #ifndef lint
18*66647Spendry static char sccsid[] = "@(#)cal.c	8.4 (Berkeley) 04/02/94";
1939234Sbostic #endif /* not lint */
2039234Sbostic 
2131229Ssam #include <sys/types.h>
2266570Spendry 
2366570Spendry #include <ctype.h>
2466570Spendry #include <err.h>
2531229Ssam #include <stdio.h>
2666570Spendry #include <stdlib.h>
2766570Spendry #include <string.h>
2866570Spendry #include <time.h>
2966570Spendry #include <unistd.h>
3031229Ssam 
3139234Sbostic #define	THURSDAY		4		/* for reformation */
3239234Sbostic #define	SATURDAY 		6		/* 1 Jan 1 was a Saturday */
3348365Sbostic 
3439234Sbostic #define	FIRST_MISSING_DAY 	639787		/* 3 Sep 1752 */
3539234Sbostic #define	NUMBER_MISSING_DAYS 	11		/* 11 day correction */
3648365Sbostic 
3748365Sbostic #define	MAXDAYS			42		/* max slots in a month array */
3839638Sbostic #define	SPACE			-1		/* used in day array */
3939234Sbostic 
4039234Sbostic static int days_in_month[2][13] = {
4139234Sbostic 	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
4239234Sbostic 	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
436009Swnj };
4439234Sbostic 
4548365Sbostic int sep1752[MAXDAYS] = {
4639638Sbostic 	SPACE,	SPACE,	1,	2,	14,	15,	16,
4739638Sbostic 	17,	18,	19,	20,	21,	22,	23,
4839638Sbostic 	24,	25,	26,	27,	28,	29,	30,
4939638Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
5039638Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
5139638Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
5248365Sbostic }, j_sep1752[MAXDAYS] = {
5339638Sbostic 	SPACE,	SPACE,	245,	246,	258,	259,	260,
5439638Sbostic 	261,	262,	263,	264,	265,	266,	267,
5539638Sbostic 	268,	269,	270,	271,	272,	273,	274,
5639638Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
5739638Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
5839638Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
5948365Sbostic }, empty[MAXDAYS] = {
6048365Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
6148365Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
6248365Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
6348365Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
6448365Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
6548365Sbostic 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
666009Swnj };
6739234Sbostic 
6848365Sbostic char *month_names[12] = {
6939234Sbostic 	"January", "February", "March", "April", "May", "June",
7039234Sbostic 	"July", "August", "September", "October", "November", "December",
7139234Sbostic };
7239234Sbostic 
7348365Sbostic char *day_headings = " S  M Tu  W Th  F  S";
7448365Sbostic char *j_day_headings = "  S   M  Tu   W  Th   F   S";
7539234Sbostic 
7639234Sbostic /* leap year -- account for gregorian reformation in 1752 */
7739234Sbostic #define	leap_year(yr) \
7839234Sbostic 	((yr) <= 1752 ? !((yr) % 4) : \
7939234Sbostic 	!((yr) % 4) && ((yr) % 100) || !((yr) % 400))
8039234Sbostic 
8139234Sbostic /* number of centuries since 1700, not inclusive */
8239234Sbostic #define	centuries_since_1700(yr) \
8339234Sbostic 	((yr) > 1700 ? (yr) / 100 - 17 : 0)
8439234Sbostic 
8539234Sbostic /* number of centuries since 1700 whose modulo of 400 is 0 */
8639234Sbostic #define	quad_centuries_since_1700(yr) \
8739234Sbostic 	((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
8839234Sbostic 
8939234Sbostic /* number of leap years between year 1 and this year, not inclusive */
9039234Sbostic #define	leap_years_since_year_1(yr) \
9139234Sbostic 	((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
9239234Sbostic 
9339638Sbostic int julian;
9439638Sbostic 
9566570Spendry void	ascii_day __P((char *, int));
9666570Spendry void	center __P((char *, int, int));
9766570Spendry void	day_array __P((int, int, int *));
9866570Spendry int	day_in_week __P((int, int, int));
9966570Spendry int	day_in_year __P((int, int, int));
10066570Spendry void	j_yearly __P((int));
10166570Spendry void	monthly __P((int, int));
10266570Spendry void	trim_trailing_spaces __P((char *));
10366570Spendry void	usage __P((void));
10466570Spendry void	yearly __P((int));
10566570Spendry 
10666570Spendry int
main(argc,argv)1076009Swnj main(argc, argv)
10839234Sbostic 	int argc;
10939234Sbostic 	char **argv;
1106009Swnj {
11139234Sbostic 	struct tm *local_time;
11266570Spendry 	time_t now;
11339409Sbostic 	int ch, month, year, yflag;
1146009Swnj 
11539409Sbostic 	yflag = 0;
11639638Sbostic 	while ((ch = getopt(argc, argv, "jy")) != EOF)
11739409Sbostic 		switch(ch) {
11839638Sbostic 		case 'j':
11939638Sbostic 			julian = 1;
12039638Sbostic 			break;
12139409Sbostic 		case 'y':
12239409Sbostic 			yflag = 1;
12339409Sbostic 			break;
12439409Sbostic 		case '?':
12539409Sbostic 		default:
12639409Sbostic 			usage();
12739409Sbostic 		}
12839409Sbostic 	argc -= optind;
12939409Sbostic 	argv += optind;
13039409Sbostic 
13148365Sbostic 	month = 0;
13239234Sbostic 	switch(argc) {
13339409Sbostic 	case 2:
13466570Spendry 		if ((month = atoi(*argv++)) < 1 || month > 12)
13566570Spendry 			errx(1, "illegal month value: use 1-12");
13639234Sbostic 		/* FALLTHROUGH */
13739409Sbostic 	case 1:
13866570Spendry 		if ((year = atoi(*argv)) < 1 || year > 9999)
13966570Spendry 			errx(1, "illegal year value: use 1-9999");
14039234Sbostic 		break;
14139409Sbostic 	case 0:
14239234Sbostic 		(void)time(&now);
14339234Sbostic 		local_time = localtime(&now);
14439234Sbostic 		year = local_time->tm_year + 1900;
14539409Sbostic 		if (!yflag)
14639409Sbostic 			month = local_time->tm_mon + 1;
14739234Sbostic 		break;
14839234Sbostic 	default:
14939409Sbostic 		usage();
15031229Ssam 	}
15148365Sbostic 
15239234Sbostic 	if (month)
15339638Sbostic 		monthly(month, year);
15439638Sbostic 	else if (julian)
15539638Sbostic 		j_yearly(year);
15639234Sbostic 	else
15739638Sbostic 		yearly(year);
1586009Swnj 	exit(0);
1596009Swnj }
1606009Swnj 
16139638Sbostic #define	DAY_LEN		3		/* 3 spaces per day */
16239638Sbostic #define	J_DAY_LEN	4		/* 4 spaces per day */
16339638Sbostic #define	WEEK_LEN	20		/* 7 * 3 - one space at the end */
16439638Sbostic #define	J_WEEK_LEN	27		/* 7 * 4 - one space at the end */
16539638Sbostic #define	HEAD_SEP	2		/* spaces between day headings */
16648365Sbostic #define	J_HEAD_SEP	2
16739638Sbostic 
16866570Spendry void
monthly(month,year)16939638Sbostic monthly(month, year)
17039234Sbostic 	int month, year;
1716009Swnj {
17266570Spendry 	int col, row, len, days[MAXDAYS];
17366570Spendry 	char *p, lineout[30];
1746009Swnj 
17539638Sbostic 	day_array(month, year, days);
17639638Sbostic 	len = sprintf(lineout, "%s %d", month_names[month - 1], year);
17739638Sbostic 	(void)printf("%*s%s\n%s\n",
17839638Sbostic 	    ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
17939638Sbostic 	    lineout, julian ? j_day_headings : day_headings);
18039234Sbostic 	for (row = 0; row < 6; row++) {
18139638Sbostic 		for (col = 0, p = lineout; col < 7; col++,
18239638Sbostic 		    p += julian ? J_DAY_LEN : DAY_LEN)
18339234Sbostic 			ascii_day(p, days[row * 7 + col]);
18464680Spendry 		*p = '\0';
18539234Sbostic 		trim_trailing_spaces(lineout);
18639234Sbostic 		(void)printf("%s\n", lineout);
18739234Sbostic 	}
1886009Swnj }
1896009Swnj 
19066570Spendry void
j_yearly(year)19139638Sbostic j_yearly(year)
19239234Sbostic 	int year;
1936009Swnj {
19466570Spendry 	int col, *dp, i, month, row, which_cal;
19548365Sbostic 	int days[12][MAXDAYS];
19666570Spendry 	char *p, lineout[80];
1976009Swnj 
19839638Sbostic 	(void)sprintf(lineout, "%d", year);
19939638Sbostic 	center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
20039638Sbostic 	(void)printf("\n\n");
20139234Sbostic 	for (i = 0; i < 12; i++)
20248365Sbostic 		day_array(i + 1, year, days[i]);
20339234Sbostic 	(void)memset(lineout, ' ', sizeof(lineout) - 1);
20439234Sbostic 	lineout[sizeof(lineout) - 1] = '\0';
20539638Sbostic 	for (month = 0; month < 12; month += 2) {
20639638Sbostic 		center(month_names[month], J_WEEK_LEN, J_HEAD_SEP);
20739638Sbostic 		center(month_names[month + 1], J_WEEK_LEN, 0);
20839638Sbostic 		(void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
20939638Sbostic 		    j_day_headings);
21039638Sbostic 		for (row = 0; row < 6; row++) {
21139638Sbostic 			for (which_cal = 0; which_cal < 2; which_cal++) {
21239638Sbostic 				p = lineout + which_cal * (J_WEEK_LEN + 2);
21339638Sbostic 				dp = &days[month + which_cal][row * 7];
21439638Sbostic 				for (col = 0; col < 7; col++, p += J_DAY_LEN)
21539638Sbostic 					ascii_day(p, *dp++);
21639638Sbostic 			}
21764680Spendry 			*p = '\0';
21839638Sbostic 			trim_trailing_spaces(lineout);
21939638Sbostic 			(void)printf("%s\n", lineout);
22039638Sbostic 		}
22139638Sbostic 	}
22239638Sbostic 	(void)printf("\n");
22339638Sbostic }
22439638Sbostic 
22566570Spendry void
yearly(year)22639638Sbostic yearly(year)
22739638Sbostic 	int year;
22839638Sbostic {
22966570Spendry 	int col, *dp, i, month, row, which_cal;
23048365Sbostic 	int days[12][MAXDAYS];
23166570Spendry 	char *p, lineout[80];
23239638Sbostic 
23339638Sbostic 	(void)sprintf(lineout, "%d", year);
23439638Sbostic 	center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
23539638Sbostic 	(void)printf("\n\n");
23639638Sbostic 	for (i = 0; i < 12; i++)
23748365Sbostic 		day_array(i + 1, year, days[i]);
23839638Sbostic 	(void)memset(lineout, ' ', sizeof(lineout) - 1);
23939638Sbostic 	lineout[sizeof(lineout) - 1] = '\0';
24039234Sbostic 	for (month = 0; month < 12; month += 3) {
24139638Sbostic 		center(month_names[month], WEEK_LEN, HEAD_SEP);
24239638Sbostic 		center(month_names[month + 1], WEEK_LEN, HEAD_SEP);
24339638Sbostic 		center(month_names[month + 2], WEEK_LEN, 0);
24439638Sbostic 		(void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
24539638Sbostic 		    "", day_headings, HEAD_SEP, "", day_headings);
24639234Sbostic 		for (row = 0; row < 6; row++) {
24739234Sbostic 			for (which_cal = 0; which_cal < 3; which_cal++) {
24839638Sbostic 				p = lineout + which_cal * (WEEK_LEN + 2);
24939234Sbostic 				dp = &days[month + which_cal][row * 7];
25039638Sbostic 				for (col = 0; col < 7; col++, p += DAY_LEN)
25139234Sbostic 					ascii_day(p, *dp++);
25239234Sbostic 			}
25364680Spendry 			*p = '\0';
25439234Sbostic 			trim_trailing_spaces(lineout);
25539234Sbostic 			(void)printf("%s\n", lineout);
2566009Swnj 		}
2576009Swnj 	}
25839638Sbostic 	(void)printf("\n");
2596009Swnj }
2606009Swnj 
2616009Swnj /*
26239638Sbostic  * day_array --
26339234Sbostic  *	Fill in an array of 42 integers with a calendar.  Assume for a moment
26439234Sbostic  *	that you took the (maximum) 6 rows in a calendar and stretched them
26539234Sbostic  *	out end to end.  You would have 42 numbers or spaces.  This routine
26639234Sbostic  *	builds that array for any month from Jan. 1 through Dec. 9999.
2676009Swnj  */
26866570Spendry void
day_array(month,year,days)26939638Sbostic day_array(month, year, days)
27039638Sbostic 	int month, year;
27166570Spendry 	int *days;
2726009Swnj {
27366570Spendry 	int day, dw, dm;
2746009Swnj 
27539638Sbostic 	if (month == 9 && year == 1752) {
27666570Spendry 		memmove(days,
27766570Spendry 			julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
27839638Sbostic 		return;
27939234Sbostic 	}
28066570Spendry 	memmove(days, empty, MAXDAYS * sizeof(int));
28148365Sbostic 	dm = days_in_month[leap_year(year)][month];
28248365Sbostic 	dw = day_in_week(1, month, year);
28339638Sbostic 	day = julian ? day_in_year(1, month, year) : 1;
28439638Sbostic 	while (dm--)
28539638Sbostic 		days[dw++] = day++;
28639234Sbostic }
28739234Sbostic 
2886009Swnj /*
28939234Sbostic  * day_in_year --
29039234Sbostic  *	return the 1 based day number within the year
2916009Swnj  */
29266570Spendry int
day_in_year(day,month,year)29339234Sbostic day_in_year(day, month, year)
29466570Spendry 	int day, month, year;
29539234Sbostic {
29666570Spendry 	int i, leap;
2976009Swnj 
29839234Sbostic 	leap = leap_year(year);
29939234Sbostic 	for (i = 1; i < month; i++)
30039234Sbostic 		day += days_in_month[leap][i];
30166570Spendry 	return (day);
30239234Sbostic }
3036009Swnj 
3046009Swnj /*
30539234Sbostic  * day_in_week
30639234Sbostic  *	return the 0 based day number for any date from 1 Jan. 1 to
30739234Sbostic  *	31 Dec. 9999.  Assumes the Gregorian reformation eliminates
30839234Sbostic  *	3 Sep. 1752 through 13 Sep. 1752.  Returns Thursday for all
30939234Sbostic  *	missing days.
3106009Swnj  */
31166570Spendry int
day_in_week(day,month,year)31239234Sbostic day_in_week(day, month, year)
31339234Sbostic 	int day, month, year;
31439234Sbostic {
31539234Sbostic 	long temp;
3166009Swnj 
31739234Sbostic 	temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
31839234Sbostic 	    + day_in_year(day, month, year);
31939234Sbostic 	if (temp < FIRST_MISSING_DAY)
32066570Spendry 		return ((temp - 1 + SATURDAY) % 7);
32139234Sbostic 	if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
32266570Spendry 		return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
32366570Spendry 	return (THURSDAY);
32439234Sbostic }
32539234Sbostic 
32666570Spendry void
ascii_day(p,day)32739234Sbostic ascii_day(p, day)
32866570Spendry 	char *p;
32966570Spendry 	int day;
33039234Sbostic {
33166570Spendry 	int display, val;
33248365Sbostic 	static char *aday[] = {
33348365Sbostic 		"",
33448366Sbostic 		" 1", " 2", " 3", " 4", " 5", " 6", " 7",
33548366Sbostic 		" 8", " 9", "10", "11", "12", "13", "14",
33648366Sbostic 		"15", "16", "17", "18", "19", "20", "21",
33748366Sbostic 		"22", "23", "24", "25", "26", "27", "28",
33848366Sbostic 		"29", "30", "31",
33948365Sbostic 	};
34039234Sbostic 
34139234Sbostic 	if (day == SPACE) {
34248365Sbostic 		memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
34339638Sbostic 		return;
3446009Swnj 	}
34548366Sbostic 	if (julian) {
34648366Sbostic 		if (val = day / 100) {
34748366Sbostic 			day %= 100;
34848366Sbostic 			*p++ = val + '0';
34948366Sbostic 			display = 1;
35048366Sbostic 		} else {
35148366Sbostic 			*p++ = ' ';
35248366Sbostic 			display = 0;
35348366Sbostic 		}
35448366Sbostic 		val = day / 10;
35548366Sbostic 		if (val || display)
35648366Sbostic 			*p++ = val + '0';
35748366Sbostic 		else
35848366Sbostic 			*p++ = ' ';
35948366Sbostic 		*p++ = day % 10 + '0';
36048366Sbostic 	} else {
36148365Sbostic 		*p++ = aday[day][0];
36248365Sbostic 		*p++ = aday[day][1];
36339234Sbostic 	}
36439234Sbostic 	*p = ' ';
36539234Sbostic }
3666009Swnj 
36766570Spendry void
trim_trailing_spaces(s)36839234Sbostic trim_trailing_spaces(s)
36966570Spendry 	char *s;
37039234Sbostic {
37166570Spendry 	char *p;
3726009Swnj 
37366570Spendry 	for (p = s; *p; ++p)
37466570Spendry 		continue;
37566570Spendry 	while (p > s && isspace(*--p))
37666570Spendry 		continue;
37739234Sbostic 	if (p > s)
37839234Sbostic 		++p;
37939234Sbostic 	*p = '\0';
3806009Swnj }
38139409Sbostic 
38266570Spendry void
center(str,len,separate)38339638Sbostic center(str, len, separate)
38439638Sbostic 	char *str;
38566570Spendry 	int len;
38639638Sbostic 	int separate;
38739638Sbostic {
38866570Spendry 
38939638Sbostic 	len -= strlen(str);
39039638Sbostic 	(void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
39139638Sbostic 	if (separate)
39239638Sbostic 		(void)printf("%*s", separate, "");
39339638Sbostic }
39439638Sbostic 
39566570Spendry void
usage()39639409Sbostic usage()
39739409Sbostic {
39866570Spendry 
39939638Sbostic 	(void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
40039409Sbostic 	exit(1);
40139409Sbostic }
402