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