1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kim Letkeman.
7 *
8 * %sccs.include.redist.c%
9 */
10
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1989, 1993, 1994\n\
14 The Regents of the University of California. All rights reserved.\n";
15 #endif /* not lint */
16
17 #ifndef lint
18 static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 04/02/94";
19 #endif /* not lint */
20
21 #include <sys/types.h>
22
23 #include <ctype.h>
24 #include <err.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #define THURSDAY 4 /* for reformation */
32 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
33
34 #define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
35 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
36
37 #define MAXDAYS 42 /* max slots in a month array */
38 #define SPACE -1 /* used in day array */
39
40 static int days_in_month[2][13] = {
41 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
42 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
43 };
44
45 int sep1752[MAXDAYS] = {
46 SPACE, SPACE, 1, 2, 14, 15, 16,
47 17, 18, 19, 20, 21, 22, 23,
48 24, 25, 26, 27, 28, 29, 30,
49 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
50 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
51 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
52 }, j_sep1752[MAXDAYS] = {
53 SPACE, SPACE, 245, 246, 258, 259, 260,
54 261, 262, 263, 264, 265, 266, 267,
55 268, 269, 270, 271, 272, 273, 274,
56 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
57 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
58 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
59 }, empty[MAXDAYS] = {
60 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
61 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
62 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
63 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
64 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
65 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
66 };
67
68 char *month_names[12] = {
69 "January", "February", "March", "April", "May", "June",
70 "July", "August", "September", "October", "November", "December",
71 };
72
73 char *day_headings = " S M Tu W Th F S";
74 char *j_day_headings = " S M Tu W Th F S";
75
76 /* leap year -- account for gregorian reformation in 1752 */
77 #define leap_year(yr) \
78 ((yr) <= 1752 ? !((yr) % 4) : \
79 !((yr) % 4) && ((yr) % 100) || !((yr) % 400))
80
81 /* number of centuries since 1700, not inclusive */
82 #define centuries_since_1700(yr) \
83 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
84
85 /* number of centuries since 1700 whose modulo of 400 is 0 */
86 #define quad_centuries_since_1700(yr) \
87 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
88
89 /* number of leap years between year 1 and this year, not inclusive */
90 #define leap_years_since_year_1(yr) \
91 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
92
93 int julian;
94
95 void ascii_day __P((char *, int));
96 void center __P((char *, int, int));
97 void day_array __P((int, int, int *));
98 int day_in_week __P((int, int, int));
99 int day_in_year __P((int, int, int));
100 void j_yearly __P((int));
101 void monthly __P((int, int));
102 void trim_trailing_spaces __P((char *));
103 void usage __P((void));
104 void yearly __P((int));
105
106 int
main(argc,argv)107 main(argc, argv)
108 int argc;
109 char **argv;
110 {
111 struct tm *local_time;
112 time_t now;
113 int ch, month, year, yflag;
114
115 yflag = 0;
116 while ((ch = getopt(argc, argv, "jy")) != EOF)
117 switch(ch) {
118 case 'j':
119 julian = 1;
120 break;
121 case 'y':
122 yflag = 1;
123 break;
124 case '?':
125 default:
126 usage();
127 }
128 argc -= optind;
129 argv += optind;
130
131 month = 0;
132 switch(argc) {
133 case 2:
134 if ((month = atoi(*argv++)) < 1 || month > 12)
135 errx(1, "illegal month value: use 1-12");
136 /* FALLTHROUGH */
137 case 1:
138 if ((year = atoi(*argv)) < 1 || year > 9999)
139 errx(1, "illegal year value: use 1-9999");
140 break;
141 case 0:
142 (void)time(&now);
143 local_time = localtime(&now);
144 year = local_time->tm_year + 1900;
145 if (!yflag)
146 month = local_time->tm_mon + 1;
147 break;
148 default:
149 usage();
150 }
151
152 if (month)
153 monthly(month, year);
154 else if (julian)
155 j_yearly(year);
156 else
157 yearly(year);
158 exit(0);
159 }
160
161 #define DAY_LEN 3 /* 3 spaces per day */
162 #define J_DAY_LEN 4 /* 4 spaces per day */
163 #define WEEK_LEN 20 /* 7 * 3 - one space at the end */
164 #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */
165 #define HEAD_SEP 2 /* spaces between day headings */
166 #define J_HEAD_SEP 2
167
168 void
monthly(month,year)169 monthly(month, year)
170 int month, year;
171 {
172 int col, row, len, days[MAXDAYS];
173 char *p, lineout[30];
174
175 day_array(month, year, days);
176 len = sprintf(lineout, "%s %d", month_names[month - 1], year);
177 (void)printf("%*s%s\n%s\n",
178 ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
179 lineout, julian ? j_day_headings : day_headings);
180 for (row = 0; row < 6; row++) {
181 for (col = 0, p = lineout; col < 7; col++,
182 p += julian ? J_DAY_LEN : DAY_LEN)
183 ascii_day(p, days[row * 7 + col]);
184 *p = '\0';
185 trim_trailing_spaces(lineout);
186 (void)printf("%s\n", lineout);
187 }
188 }
189
190 void
j_yearly(year)191 j_yearly(year)
192 int year;
193 {
194 int col, *dp, i, month, row, which_cal;
195 int days[12][MAXDAYS];
196 char *p, lineout[80];
197
198 (void)sprintf(lineout, "%d", year);
199 center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
200 (void)printf("\n\n");
201 for (i = 0; i < 12; i++)
202 day_array(i + 1, year, days[i]);
203 (void)memset(lineout, ' ', sizeof(lineout) - 1);
204 lineout[sizeof(lineout) - 1] = '\0';
205 for (month = 0; month < 12; month += 2) {
206 center(month_names[month], J_WEEK_LEN, J_HEAD_SEP);
207 center(month_names[month + 1], J_WEEK_LEN, 0);
208 (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
209 j_day_headings);
210 for (row = 0; row < 6; row++) {
211 for (which_cal = 0; which_cal < 2; which_cal++) {
212 p = lineout + which_cal * (J_WEEK_LEN + 2);
213 dp = &days[month + which_cal][row * 7];
214 for (col = 0; col < 7; col++, p += J_DAY_LEN)
215 ascii_day(p, *dp++);
216 }
217 *p = '\0';
218 trim_trailing_spaces(lineout);
219 (void)printf("%s\n", lineout);
220 }
221 }
222 (void)printf("\n");
223 }
224
225 void
yearly(year)226 yearly(year)
227 int year;
228 {
229 int col, *dp, i, month, row, which_cal;
230 int days[12][MAXDAYS];
231 char *p, lineout[80];
232
233 (void)sprintf(lineout, "%d", year);
234 center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
235 (void)printf("\n\n");
236 for (i = 0; i < 12; i++)
237 day_array(i + 1, year, days[i]);
238 (void)memset(lineout, ' ', sizeof(lineout) - 1);
239 lineout[sizeof(lineout) - 1] = '\0';
240 for (month = 0; month < 12; month += 3) {
241 center(month_names[month], WEEK_LEN, HEAD_SEP);
242 center(month_names[month + 1], WEEK_LEN, HEAD_SEP);
243 center(month_names[month + 2], WEEK_LEN, 0);
244 (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
245 "", day_headings, HEAD_SEP, "", day_headings);
246 for (row = 0; row < 6; row++) {
247 for (which_cal = 0; which_cal < 3; which_cal++) {
248 p = lineout + which_cal * (WEEK_LEN + 2);
249 dp = &days[month + which_cal][row * 7];
250 for (col = 0; col < 7; col++, p += DAY_LEN)
251 ascii_day(p, *dp++);
252 }
253 *p = '\0';
254 trim_trailing_spaces(lineout);
255 (void)printf("%s\n", lineout);
256 }
257 }
258 (void)printf("\n");
259 }
260
261 /*
262 * day_array --
263 * Fill in an array of 42 integers with a calendar. Assume for a moment
264 * that you took the (maximum) 6 rows in a calendar and stretched them
265 * out end to end. You would have 42 numbers or spaces. This routine
266 * builds that array for any month from Jan. 1 through Dec. 9999.
267 */
268 void
day_array(month,year,days)269 day_array(month, year, days)
270 int month, year;
271 int *days;
272 {
273 int day, dw, dm;
274
275 if (month == 9 && year == 1752) {
276 memmove(days,
277 julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
278 return;
279 }
280 memmove(days, empty, MAXDAYS * sizeof(int));
281 dm = days_in_month[leap_year(year)][month];
282 dw = day_in_week(1, month, year);
283 day = julian ? day_in_year(1, month, year) : 1;
284 while (dm--)
285 days[dw++] = day++;
286 }
287
288 /*
289 * day_in_year --
290 * return the 1 based day number within the year
291 */
292 int
day_in_year(day,month,year)293 day_in_year(day, month, year)
294 int day, month, year;
295 {
296 int i, leap;
297
298 leap = leap_year(year);
299 for (i = 1; i < month; i++)
300 day += days_in_month[leap][i];
301 return (day);
302 }
303
304 /*
305 * day_in_week
306 * return the 0 based day number for any date from 1 Jan. 1 to
307 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
308 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
309 * missing days.
310 */
311 int
day_in_week(day,month,year)312 day_in_week(day, month, year)
313 int day, month, year;
314 {
315 long temp;
316
317 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
318 + day_in_year(day, month, year);
319 if (temp < FIRST_MISSING_DAY)
320 return ((temp - 1 + SATURDAY) % 7);
321 if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
322 return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
323 return (THURSDAY);
324 }
325
326 void
ascii_day(p,day)327 ascii_day(p, day)
328 char *p;
329 int day;
330 {
331 int display, val;
332 static char *aday[] = {
333 "",
334 " 1", " 2", " 3", " 4", " 5", " 6", " 7",
335 " 8", " 9", "10", "11", "12", "13", "14",
336 "15", "16", "17", "18", "19", "20", "21",
337 "22", "23", "24", "25", "26", "27", "28",
338 "29", "30", "31",
339 };
340
341 if (day == SPACE) {
342 memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
343 return;
344 }
345 if (julian) {
346 if (val = day / 100) {
347 day %= 100;
348 *p++ = val + '0';
349 display = 1;
350 } else {
351 *p++ = ' ';
352 display = 0;
353 }
354 val = day / 10;
355 if (val || display)
356 *p++ = val + '0';
357 else
358 *p++ = ' ';
359 *p++ = day % 10 + '0';
360 } else {
361 *p++ = aday[day][0];
362 *p++ = aday[day][1];
363 }
364 *p = ' ';
365 }
366
367 void
trim_trailing_spaces(s)368 trim_trailing_spaces(s)
369 char *s;
370 {
371 char *p;
372
373 for (p = s; *p; ++p)
374 continue;
375 while (p > s && isspace(*--p))
376 continue;
377 if (p > s)
378 ++p;
379 *p = '\0';
380 }
381
382 void
center(str,len,separate)383 center(str, len, separate)
384 char *str;
385 int len;
386 int separate;
387 {
388
389 len -= strlen(str);
390 (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
391 if (separate)
392 (void)printf("%*s", separate, "");
393 }
394
395 void
usage()396 usage()
397 {
398
399 (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
400 exit(1);
401 }
402