1ed8e7804Sbostic /*
2*b5f15b9dSpendry * Copyright (c) 1989, 1993, 1994
3a576e1b7Sbostic * The Regents of the University of California. All rights reserved.
4ed8e7804Sbostic *
5ed8e7804Sbostic * This code is derived from software contributed to Berkeley by
6ed8e7804Sbostic * Kim Letkeman.
7ed8e7804Sbostic *
80a10ec2cSbostic * %sccs.include.redist.c%
9ed8e7804Sbostic */
10ed8e7804Sbostic
11ced7fbbfSsam #ifndef lint
12a576e1b7Sbostic static char copyright[] =
13*b5f15b9dSpendry "@(#) Copyright (c) 1989, 1993, 1994\n\
14a576e1b7Sbostic The Regents of the University of California. All rights reserved.\n";
15ed8e7804Sbostic #endif /* not lint */
16ed8e7804Sbostic
17ed8e7804Sbostic #ifndef lint
18*b5f15b9dSpendry static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 04/02/94";
19ed8e7804Sbostic #endif /* not lint */
20ced7fbbfSsam
2112b68e8bSsam #include <sys/types.h>
226ffb1990Spendry
23ed8e7804Sbostic #include <ctype.h>
246ffb1990Spendry #include <err.h>
256ffb1990Spendry #include <stdio.h>
266ffb1990Spendry #include <stdlib.h>
276ffb1990Spendry #include <string.h>
286ffb1990Spendry #include <time.h>
296ffb1990Spendry #include <unistd.h>
3012b68e8bSsam
31ed8e7804Sbostic #define THURSDAY 4 /* for reformation */
32ed8e7804Sbostic #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
33de432eacSbostic
34ed8e7804Sbostic #define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
35ed8e7804Sbostic #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
36de432eacSbostic
37de432eacSbostic #define MAXDAYS 42 /* max slots in a month array */
38ad703e26Sbostic #define SPACE -1 /* used in day array */
39ed8e7804Sbostic
40ed8e7804Sbostic static int days_in_month[2][13] = {
41ed8e7804Sbostic {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
42ed8e7804Sbostic {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
43dc686bb6Swnj };
44ed8e7804Sbostic
45de432eacSbostic int sep1752[MAXDAYS] = {
46ed8e7804Sbostic SPACE, SPACE, 1, 2, 14, 15, 16,
47ed8e7804Sbostic 17, 18, 19, 20, 21, 22, 23,
48ed8e7804Sbostic 24, 25, 26, 27, 28, 29, 30,
49ed8e7804Sbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
50ed8e7804Sbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
51ed8e7804Sbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
52de432eacSbostic }, j_sep1752[MAXDAYS] = {
53ad703e26Sbostic SPACE, SPACE, 245, 246, 258, 259, 260,
54ad703e26Sbostic 261, 262, 263, 264, 265, 266, 267,
55ad703e26Sbostic 268, 269, 270, 271, 272, 273, 274,
56ad703e26Sbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
57ad703e26Sbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
58ad703e26Sbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
59de432eacSbostic }, empty[MAXDAYS] = {
60de432eacSbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
61de432eacSbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
62de432eacSbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
63de432eacSbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
64de432eacSbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
65de432eacSbostic SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
66dc686bb6Swnj };
67ed8e7804Sbostic
68de432eacSbostic char *month_names[12] = {
69ed8e7804Sbostic "January", "February", "March", "April", "May", "June",
70ed8e7804Sbostic "July", "August", "September", "October", "November", "December",
71ed8e7804Sbostic };
72ed8e7804Sbostic
73de432eacSbostic char *day_headings = " S M Tu W Th F S";
74de432eacSbostic char *j_day_headings = " S M Tu W Th F S";
75ed8e7804Sbostic
76ed8e7804Sbostic /* leap year -- account for gregorian reformation in 1752 */
77ed8e7804Sbostic #define leap_year(yr) \
78ed8e7804Sbostic ((yr) <= 1752 ? !((yr) % 4) : \
79ed8e7804Sbostic !((yr) % 4) && ((yr) % 100) || !((yr) % 400))
80ed8e7804Sbostic
81ed8e7804Sbostic /* number of centuries since 1700, not inclusive */
82ed8e7804Sbostic #define centuries_since_1700(yr) \
83ed8e7804Sbostic ((yr) > 1700 ? (yr) / 100 - 17 : 0)
84ed8e7804Sbostic
85ed8e7804Sbostic /* number of centuries since 1700 whose modulo of 400 is 0 */
86ed8e7804Sbostic #define quad_centuries_since_1700(yr) \
87ed8e7804Sbostic ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
88ed8e7804Sbostic
89ed8e7804Sbostic /* number of leap years between year 1 and this year, not inclusive */
90ed8e7804Sbostic #define leap_years_since_year_1(yr) \
91ed8e7804Sbostic ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
92ed8e7804Sbostic
93ad703e26Sbostic int julian;
94ad703e26Sbostic
956ffb1990Spendry void ascii_day __P((char *, int));
966ffb1990Spendry void center __P((char *, int, int));
976ffb1990Spendry void day_array __P((int, int, int *));
986ffb1990Spendry int day_in_week __P((int, int, int));
996ffb1990Spendry int day_in_year __P((int, int, int));
1006ffb1990Spendry void j_yearly __P((int));
1016ffb1990Spendry void monthly __P((int, int));
1026ffb1990Spendry void trim_trailing_spaces __P((char *));
1036ffb1990Spendry void usage __P((void));
1046ffb1990Spendry void yearly __P((int));
1056ffb1990Spendry
1066ffb1990Spendry int
main(argc,argv)107dc686bb6Swnj main(argc, argv)
108ed8e7804Sbostic int argc;
109ed8e7804Sbostic char **argv;
110dc686bb6Swnj {
111ed8e7804Sbostic struct tm *local_time;
1126ffb1990Spendry time_t now;
1139ec34152Sbostic int ch, month, year, yflag;
114dc686bb6Swnj
1159ec34152Sbostic yflag = 0;
116ad703e26Sbostic while ((ch = getopt(argc, argv, "jy")) != EOF)
1179ec34152Sbostic switch(ch) {
118ad703e26Sbostic case 'j':
119ad703e26Sbostic julian = 1;
120ad703e26Sbostic break;
1219ec34152Sbostic case 'y':
1229ec34152Sbostic yflag = 1;
1239ec34152Sbostic break;
1249ec34152Sbostic case '?':
1259ec34152Sbostic default:
1269ec34152Sbostic usage();
1279ec34152Sbostic }
1289ec34152Sbostic argc -= optind;
1299ec34152Sbostic argv += optind;
1309ec34152Sbostic
131de432eacSbostic month = 0;
132ed8e7804Sbostic switch(argc) {
1339ec34152Sbostic case 2:
1346ffb1990Spendry if ((month = atoi(*argv++)) < 1 || month > 12)
1356ffb1990Spendry errx(1, "illegal month value: use 1-12");
136ed8e7804Sbostic /* FALLTHROUGH */
1379ec34152Sbostic case 1:
1386ffb1990Spendry if ((year = atoi(*argv)) < 1 || year > 9999)
1396ffb1990Spendry errx(1, "illegal year value: use 1-9999");
140ed8e7804Sbostic break;
1419ec34152Sbostic case 0:
142ed8e7804Sbostic (void)time(&now);
143ed8e7804Sbostic local_time = localtime(&now);
144ed8e7804Sbostic year = local_time->tm_year + 1900;
1459ec34152Sbostic if (!yflag)
1469ec34152Sbostic month = local_time->tm_mon + 1;
147ed8e7804Sbostic break;
148ed8e7804Sbostic default:
1499ec34152Sbostic usage();
150dc686bb6Swnj }
151de432eacSbostic
152ed8e7804Sbostic if (month)
153ad703e26Sbostic monthly(month, year);
154ad703e26Sbostic else if (julian)
155ad703e26Sbostic j_yearly(year);
156ed8e7804Sbostic else
157ad703e26Sbostic yearly(year);
158ed8e7804Sbostic exit(0);
159dc686bb6Swnj }
160dc686bb6Swnj
161ad703e26Sbostic #define DAY_LEN 3 /* 3 spaces per day */
162ad703e26Sbostic #define J_DAY_LEN 4 /* 4 spaces per day */
163ad703e26Sbostic #define WEEK_LEN 20 /* 7 * 3 - one space at the end */
164ad703e26Sbostic #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */
165ad703e26Sbostic #define HEAD_SEP 2 /* spaces between day headings */
166de432eacSbostic #define J_HEAD_SEP 2
167ad703e26Sbostic
1686ffb1990Spendry void
monthly(month,year)169ad703e26Sbostic monthly(month, year)
170ed8e7804Sbostic int month, year;
171dc686bb6Swnj {
1726ffb1990Spendry int col, row, len, days[MAXDAYS];
1736ffb1990Spendry char *p, lineout[30];
174dc686bb6Swnj
175ad703e26Sbostic day_array(month, year, days);
176ad703e26Sbostic len = sprintf(lineout, "%s %d", month_names[month - 1], year);
177ad703e26Sbostic (void)printf("%*s%s\n%s\n",
178ad703e26Sbostic ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
179ad703e26Sbostic lineout, julian ? j_day_headings : day_headings);
180ed8e7804Sbostic for (row = 0; row < 6; row++) {
181ad703e26Sbostic for (col = 0, p = lineout; col < 7; col++,
182ad703e26Sbostic p += julian ? J_DAY_LEN : DAY_LEN)
183ed8e7804Sbostic ascii_day(p, days[row * 7 + col]);
184e81938bcSpendry *p = '\0';
185ed8e7804Sbostic trim_trailing_spaces(lineout);
186ed8e7804Sbostic (void)printf("%s\n", lineout);
187ed8e7804Sbostic }
188ed8e7804Sbostic }
189dc686bb6Swnj
1906ffb1990Spendry void
j_yearly(year)191ad703e26Sbostic j_yearly(year)
192ed8e7804Sbostic int year;
193ed8e7804Sbostic {
1946ffb1990Spendry int col, *dp, i, month, row, which_cal;
195de432eacSbostic int days[12][MAXDAYS];
1966ffb1990Spendry char *p, lineout[80];
197dc686bb6Swnj
198ad703e26Sbostic (void)sprintf(lineout, "%d", year);
199ad703e26Sbostic center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
200ad703e26Sbostic (void)printf("\n\n");
201ed8e7804Sbostic for (i = 0; i < 12; i++)
202de432eacSbostic day_array(i + 1, year, days[i]);
203ed8e7804Sbostic (void)memset(lineout, ' ', sizeof(lineout) - 1);
204ed8e7804Sbostic lineout[sizeof(lineout) - 1] = '\0';
205ad703e26Sbostic for (month = 0; month < 12; month += 2) {
206ad703e26Sbostic center(month_names[month], J_WEEK_LEN, J_HEAD_SEP);
207ad703e26Sbostic center(month_names[month + 1], J_WEEK_LEN, 0);
208ad703e26Sbostic (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
209ad703e26Sbostic j_day_headings);
210ed8e7804Sbostic for (row = 0; row < 6; row++) {
211ad703e26Sbostic for (which_cal = 0; which_cal < 2; which_cal++) {
212ad703e26Sbostic p = lineout + which_cal * (J_WEEK_LEN + 2);
213ed8e7804Sbostic dp = &days[month + which_cal][row * 7];
214ad703e26Sbostic for (col = 0; col < 7; col++, p += J_DAY_LEN)
215ed8e7804Sbostic ascii_day(p, *dp++);
216ed8e7804Sbostic }
217e81938bcSpendry *p = '\0';
218ed8e7804Sbostic trim_trailing_spaces(lineout);
219ed8e7804Sbostic (void)printf("%s\n", lineout);
220ed8e7804Sbostic }
221ed8e7804Sbostic }
222ad703e26Sbostic (void)printf("\n");
223ad703e26Sbostic }
224ad703e26Sbostic
2256ffb1990Spendry void
yearly(year)226ad703e26Sbostic yearly(year)
227ad703e26Sbostic int year;
228ad703e26Sbostic {
2296ffb1990Spendry int col, *dp, i, month, row, which_cal;
230de432eacSbostic int days[12][MAXDAYS];
2316ffb1990Spendry char *p, lineout[80];
232ad703e26Sbostic
233ad703e26Sbostic (void)sprintf(lineout, "%d", year);
234ad703e26Sbostic center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
235ad703e26Sbostic (void)printf("\n\n");
236ad703e26Sbostic for (i = 0; i < 12; i++)
237de432eacSbostic day_array(i + 1, year, days[i]);
238ad703e26Sbostic (void)memset(lineout, ' ', sizeof(lineout) - 1);
239ad703e26Sbostic lineout[sizeof(lineout) - 1] = '\0';
240ad703e26Sbostic for (month = 0; month < 12; month += 3) {
241ad703e26Sbostic center(month_names[month], WEEK_LEN, HEAD_SEP);
242ad703e26Sbostic center(month_names[month + 1], WEEK_LEN, HEAD_SEP);
243ad703e26Sbostic center(month_names[month + 2], WEEK_LEN, 0);
244ad703e26Sbostic (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
245ad703e26Sbostic "", day_headings, HEAD_SEP, "", day_headings);
246ad703e26Sbostic for (row = 0; row < 6; row++) {
247ad703e26Sbostic for (which_cal = 0; which_cal < 3; which_cal++) {
248ad703e26Sbostic p = lineout + which_cal * (WEEK_LEN + 2);
249ad703e26Sbostic dp = &days[month + which_cal][row * 7];
250ad703e26Sbostic for (col = 0; col < 7; col++, p += DAY_LEN)
251ad703e26Sbostic ascii_day(p, *dp++);
252ad703e26Sbostic }
253e81938bcSpendry *p = '\0';
254ad703e26Sbostic trim_trailing_spaces(lineout);
255ad703e26Sbostic (void)printf("%s\n", lineout);
256ad703e26Sbostic }
257ad703e26Sbostic }
258ad703e26Sbostic (void)printf("\n");
259dc686bb6Swnj }
260dc686bb6Swnj
261dc686bb6Swnj /*
262ad703e26Sbostic * day_array --
263ed8e7804Sbostic * Fill in an array of 42 integers with a calendar. Assume for a moment
264ed8e7804Sbostic * that you took the (maximum) 6 rows in a calendar and stretched them
265ed8e7804Sbostic * out end to end. You would have 42 numbers or spaces. This routine
266ed8e7804Sbostic * builds that array for any month from Jan. 1 through Dec. 9999.
267dc686bb6Swnj */
2686ffb1990Spendry void
day_array(month,year,days)269ad703e26Sbostic day_array(month, year, days)
270ad703e26Sbostic int month, year;
2716ffb1990Spendry int *days;
272ed8e7804Sbostic {
2736ffb1990Spendry int day, dw, dm;
274dc686bb6Swnj
275ad703e26Sbostic if (month == 9 && year == 1752) {
2766ffb1990Spendry memmove(days,
2776ffb1990Spendry julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
278ad703e26Sbostic return;
279ed8e7804Sbostic }
2806ffb1990Spendry memmove(days, empty, MAXDAYS * sizeof(int));
281de432eacSbostic dm = days_in_month[leap_year(year)][month];
282de432eacSbostic dw = day_in_week(1, month, year);
283ad703e26Sbostic day = julian ? day_in_year(1, month, year) : 1;
284ad703e26Sbostic while (dm--)
285ad703e26Sbostic days[dw++] = day++;
286ed8e7804Sbostic }
287dc686bb6Swnj
288ed8e7804Sbostic /*
289ed8e7804Sbostic * day_in_year --
290ed8e7804Sbostic * return the 1 based day number within the year
291ed8e7804Sbostic */
2926ffb1990Spendry int
day_in_year(day,month,year)293ed8e7804Sbostic day_in_year(day, month, year)
2946ffb1990Spendry int day, month, year;
295ed8e7804Sbostic {
2966ffb1990Spendry int i, leap;
297ed8e7804Sbostic
298ed8e7804Sbostic leap = leap_year(year);
299ed8e7804Sbostic for (i = 1; i < month; i++)
300ed8e7804Sbostic day += days_in_month[leap][i];
301ed8e7804Sbostic return (day);
302ed8e7804Sbostic }
303ed8e7804Sbostic
304ed8e7804Sbostic /*
305ed8e7804Sbostic * day_in_week
306ed8e7804Sbostic * return the 0 based day number for any date from 1 Jan. 1 to
307ed8e7804Sbostic * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
308ed8e7804Sbostic * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
309ed8e7804Sbostic * missing days.
310ed8e7804Sbostic */
3116ffb1990Spendry int
day_in_week(day,month,year)312ed8e7804Sbostic day_in_week(day, month, year)
313ed8e7804Sbostic int day, month, year;
314ed8e7804Sbostic {
315ed8e7804Sbostic long temp;
316ed8e7804Sbostic
317ed8e7804Sbostic temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
318ed8e7804Sbostic + day_in_year(day, month, year);
319ed8e7804Sbostic if (temp < FIRST_MISSING_DAY)
320ed8e7804Sbostic return ((temp - 1 + SATURDAY) % 7);
321ed8e7804Sbostic if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
322ed8e7804Sbostic return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
323ed8e7804Sbostic return (THURSDAY);
324ed8e7804Sbostic }
325ed8e7804Sbostic
3266ffb1990Spendry void
ascii_day(p,day)327ed8e7804Sbostic ascii_day(p, day)
3286ffb1990Spendry char *p;
3296ffb1990Spendry int day;
330ed8e7804Sbostic {
3316ffb1990Spendry int display, val;
332de432eacSbostic static char *aday[] = {
333de432eacSbostic "",
334de432eacSbostic " 1", " 2", " 3", " 4", " 5", " 6", " 7",
335de432eacSbostic " 8", " 9", "10", "11", "12", "13", "14",
336de432eacSbostic "15", "16", "17", "18", "19", "20", "21",
337de432eacSbostic "22", "23", "24", "25", "26", "27", "28",
338de432eacSbostic "29", "30", "31",
339de432eacSbostic };
340ed8e7804Sbostic
341ed8e7804Sbostic if (day == SPACE) {
342de432eacSbostic memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
343ad703e26Sbostic return;
344ad703e26Sbostic }
345b6dd8126Sbostic if (julian) {
346ad703e26Sbostic if (val = day / 100) {
347ad703e26Sbostic day %= 100;
348ad703e26Sbostic *p++ = val + '0';
349ad703e26Sbostic display = 1;
350de432eacSbostic } else {
351ed8e7804Sbostic *p++ = ' ';
352de432eacSbostic display = 0;
353ed8e7804Sbostic }
354ad703e26Sbostic val = day / 10;
355ad703e26Sbostic if (val || display)
356ad703e26Sbostic *p++ = val + '0';
357ad703e26Sbostic else
358ad703e26Sbostic *p++ = ' ';
359ed8e7804Sbostic *p++ = day % 10 + '0';
360b6dd8126Sbostic } else {
361b6dd8126Sbostic *p++ = aday[day][0];
362b6dd8126Sbostic *p++ = aday[day][1];
363b6dd8126Sbostic }
364ed8e7804Sbostic *p = ' ';
365ed8e7804Sbostic }
366ed8e7804Sbostic
3676ffb1990Spendry void
trim_trailing_spaces(s)368ed8e7804Sbostic trim_trailing_spaces(s)
3696ffb1990Spendry char *s;
370ed8e7804Sbostic {
3716ffb1990Spendry char *p;
372ed8e7804Sbostic
3736ffb1990Spendry for (p = s; *p; ++p)
3746ffb1990Spendry continue;
3756ffb1990Spendry while (p > s && isspace(*--p))
3766ffb1990Spendry continue;
377ed8e7804Sbostic if (p > s)
378ed8e7804Sbostic ++p;
379ed8e7804Sbostic *p = '\0';
380dc686bb6Swnj }
3819ec34152Sbostic
3826ffb1990Spendry void
center(str,len,separate)383ad703e26Sbostic center(str, len, separate)
384ad703e26Sbostic char *str;
3856ffb1990Spendry int len;
386ad703e26Sbostic int separate;
387ad703e26Sbostic {
3886ffb1990Spendry
389ad703e26Sbostic len -= strlen(str);
390ad703e26Sbostic (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
391ad703e26Sbostic if (separate)
392ad703e26Sbostic (void)printf("%*s", separate, "");
393ad703e26Sbostic }
394ad703e26Sbostic
3956ffb1990Spendry void
usage()3969ec34152Sbostic usage()
3979ec34152Sbostic {
3986ffb1990Spendry
399ad703e26Sbostic (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
4009ec34152Sbostic exit(1);
4019ec34152Sbostic }
402