Annotation of src/usr.bin/cal/cal.c, Revision 1.1.1.1
1.1 deraadt 1: /* $NetBSD: cal.c,v 1.6 1995/03/26 03:10:24 glass Exp $ */
2:
3: /*
4: * Copyright (c) 1989, 1993, 1994
5: * The Regents of the University of California. All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Kim Letkeman.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the University of
21: * California, Berkeley and its contributors.
22: * 4. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
39: #ifndef lint
40: static char copyright[] =
41: "@(#) Copyright (c) 1989, 1993, 1994\n\
42: The Regents of the University of California. All rights reserved.\n";
43: #endif /* not lint */
44:
45: #ifndef lint
46: #if 0
47: static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 4/2/94";
48: #else
49: static char rcsid[] = "$NetBSD: cal.c,v 1.6 1995/03/26 03:10:24 glass Exp $";
50: #endif
51: #endif /* not lint */
52:
53: #include <sys/types.h>
54:
55: #include <ctype.h>
56: #include <err.h>
57: #include <stdio.h>
58: #include <stdlib.h>
59: #include <string.h>
60: #include <time.h>
61: #include <unistd.h>
62:
63: #define THURSDAY 4 /* for reformation */
64: #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
65:
66: #define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
67: #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
68:
69: #define MAXDAYS 42 /* max slots in a month array */
70: #define SPACE -1 /* used in day array */
71:
72: static int days_in_month[2][13] = {
73: {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
74: {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
75: };
76:
77: int sep1752[MAXDAYS] = {
78: SPACE, SPACE, 1, 2, 14, 15, 16,
79: 17, 18, 19, 20, 21, 22, 23,
80: 24, 25, 26, 27, 28, 29, 30,
81: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
82: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
83: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
84: }, j_sep1752[MAXDAYS] = {
85: SPACE, SPACE, 245, 246, 258, 259, 260,
86: 261, 262, 263, 264, 265, 266, 267,
87: 268, 269, 270, 271, 272, 273, 274,
88: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
89: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
90: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
91: }, empty[MAXDAYS] = {
92: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
93: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
94: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
95: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
96: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
97: SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
98: };
99:
100: char *month_names[12] = {
101: "January", "February", "March", "April", "May", "June",
102: "July", "August", "September", "October", "November", "December",
103: };
104:
105: char *day_headings = " S M Tu W Th F S";
106: char *j_day_headings = " S M Tu W Th F S";
107:
108: /* leap year -- account for gregorian reformation in 1752 */
109: #define leap_year(yr) \
110: ((yr) <= 1752 ? !((yr) % 4) : \
111: !((yr) % 4) && ((yr) % 100) || !((yr) % 400))
112:
113: /* number of centuries since 1700, not inclusive */
114: #define centuries_since_1700(yr) \
115: ((yr) > 1700 ? (yr) / 100 - 17 : 0)
116:
117: /* number of centuries since 1700 whose modulo of 400 is 0 */
118: #define quad_centuries_since_1700(yr) \
119: ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
120:
121: /* number of leap years between year 1 and this year, not inclusive */
122: #define leap_years_since_year_1(yr) \
123: ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
124:
125: int julian;
126:
127: void ascii_day __P((char *, int));
128: void center __P((char *, int, int));
129: void day_array __P((int, int, int *));
130: int day_in_week __P((int, int, int));
131: int day_in_year __P((int, int, int));
132: void j_yearly __P((int));
133: void monthly __P((int, int));
134: void trim_trailing_spaces __P((char *));
135: void usage __P((void));
136: void yearly __P((int));
137:
138: int
139: main(argc, argv)
140: int argc;
141: char **argv;
142: {
143: struct tm *local_time;
144: time_t now;
145: int ch, month, year, yflag;
146:
147: yflag = 0;
148: while ((ch = getopt(argc, argv, "jy")) != EOF)
149: switch(ch) {
150: case 'j':
151: julian = 1;
152: break;
153: case 'y':
154: yflag = 1;
155: break;
156: case '?':
157: default:
158: usage();
159: }
160: argc -= optind;
161: argv += optind;
162:
163: month = 0;
164: switch(argc) {
165: case 2:
166: if ((month = atoi(*argv++)) < 1 || month > 12)
167: errx(1, "illegal month value: use 1-12");
168: /* FALLTHROUGH */
169: case 1:
170: if ((year = atoi(*argv)) < 1 || year > 9999)
171: errx(1, "illegal year value: use 1-9999");
172: break;
173: case 0:
174: (void)time(&now);
175: local_time = localtime(&now);
176: year = local_time->tm_year + 1900;
177: if (!yflag)
178: month = local_time->tm_mon + 1;
179: break;
180: default:
181: usage();
182: }
183:
184: if (month)
185: monthly(month, year);
186: else if (julian)
187: j_yearly(year);
188: else
189: yearly(year);
190: exit(0);
191: }
192:
193: #define DAY_LEN 3 /* 3 spaces per day */
194: #define J_DAY_LEN 4 /* 4 spaces per day */
195: #define WEEK_LEN 20 /* 7 * 3 - one space at the end */
196: #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */
197: #define HEAD_SEP 2 /* spaces between day headings */
198: #define J_HEAD_SEP 2
199:
200: void
201: monthly(month, year)
202: int month, year;
203: {
204: int col, row, len, days[MAXDAYS];
205: char *p, lineout[30];
206:
207: day_array(month, year, days);
208: len = sprintf(lineout, "%s %d", month_names[month - 1], year);
209: (void)printf("%*s%s\n%s\n",
210: ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
211: lineout, julian ? j_day_headings : day_headings);
212: for (row = 0; row < 6; row++) {
213: for (col = 0, p = lineout; col < 7; col++,
214: p += julian ? J_DAY_LEN : DAY_LEN)
215: ascii_day(p, days[row * 7 + col]);
216: *p = '\0';
217: trim_trailing_spaces(lineout);
218: (void)printf("%s\n", lineout);
219: }
220: }
221:
222: void
223: j_yearly(year)
224: int year;
225: {
226: int col, *dp, i, month, row, which_cal;
227: int days[12][MAXDAYS];
228: char *p, lineout[80];
229:
230: (void)sprintf(lineout, "%d", year);
231: center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
232: (void)printf("\n\n");
233: for (i = 0; i < 12; i++)
234: day_array(i + 1, year, days[i]);
235: (void)memset(lineout, ' ', sizeof(lineout) - 1);
236: lineout[sizeof(lineout) - 1] = '\0';
237: for (month = 0; month < 12; month += 2) {
238: center(month_names[month], J_WEEK_LEN, J_HEAD_SEP);
239: center(month_names[month + 1], J_WEEK_LEN, 0);
240: (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
241: j_day_headings);
242: for (row = 0; row < 6; row++) {
243: for (which_cal = 0; which_cal < 2; which_cal++) {
244: p = lineout + which_cal * (J_WEEK_LEN + 2);
245: dp = &days[month + which_cal][row * 7];
246: for (col = 0; col < 7; col++, p += J_DAY_LEN)
247: ascii_day(p, *dp++);
248: }
249: *p = '\0';
250: trim_trailing_spaces(lineout);
251: (void)printf("%s\n", lineout);
252: }
253: }
254: (void)printf("\n");
255: }
256:
257: void
258: yearly(year)
259: int year;
260: {
261: int col, *dp, i, month, row, which_cal;
262: int days[12][MAXDAYS];
263: char *p, lineout[80];
264:
265: (void)sprintf(lineout, "%d", year);
266: center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
267: (void)printf("\n\n");
268: for (i = 0; i < 12; i++)
269: day_array(i + 1, year, days[i]);
270: (void)memset(lineout, ' ', sizeof(lineout) - 1);
271: lineout[sizeof(lineout) - 1] = '\0';
272: for (month = 0; month < 12; month += 3) {
273: center(month_names[month], WEEK_LEN, HEAD_SEP);
274: center(month_names[month + 1], WEEK_LEN, HEAD_SEP);
275: center(month_names[month + 2], WEEK_LEN, 0);
276: (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
277: "", day_headings, HEAD_SEP, "", day_headings);
278: for (row = 0; row < 6; row++) {
279: for (which_cal = 0; which_cal < 3; which_cal++) {
280: p = lineout + which_cal * (WEEK_LEN + 2);
281: dp = &days[month + which_cal][row * 7];
282: for (col = 0; col < 7; col++, p += DAY_LEN)
283: ascii_day(p, *dp++);
284: }
285: *p = '\0';
286: trim_trailing_spaces(lineout);
287: (void)printf("%s\n", lineout);
288: }
289: }
290: (void)printf("\n");
291: }
292:
293: /*
294: * day_array --
295: * Fill in an array of 42 integers with a calendar. Assume for a moment
296: * that you took the (maximum) 6 rows in a calendar and stretched them
297: * out end to end. You would have 42 numbers or spaces. This routine
298: * builds that array for any month from Jan. 1 through Dec. 9999.
299: */
300: void
301: day_array(month, year, days)
302: int month, year;
303: int *days;
304: {
305: int day, dw, dm;
306:
307: if (month == 9 && year == 1752) {
308: memmove(days,
309: julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
310: return;
311: }
312: memmove(days, empty, MAXDAYS * sizeof(int));
313: dm = days_in_month[leap_year(year)][month];
314: dw = day_in_week(1, month, year);
315: day = julian ? day_in_year(1, month, year) : 1;
316: while (dm--)
317: days[dw++] = day++;
318: }
319:
320: /*
321: * day_in_year --
322: * return the 1 based day number within the year
323: */
324: int
325: day_in_year(day, month, year)
326: int day, month, year;
327: {
328: int i, leap;
329:
330: leap = leap_year(year);
331: for (i = 1; i < month; i++)
332: day += days_in_month[leap][i];
333: return (day);
334: }
335:
336: /*
337: * day_in_week
338: * return the 0 based day number for any date from 1 Jan. 1 to
339: * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
340: * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
341: * missing days.
342: */
343: int
344: day_in_week(day, month, year)
345: int day, month, year;
346: {
347: long temp;
348:
349: temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
350: + day_in_year(day, month, year);
351: if (temp < FIRST_MISSING_DAY)
352: return ((temp - 1 + SATURDAY) % 7);
353: if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
354: return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
355: return (THURSDAY);
356: }
357:
358: void
359: ascii_day(p, day)
360: char *p;
361: int day;
362: {
363: int display, val;
364: static char *aday[] = {
365: "",
366: " 1", " 2", " 3", " 4", " 5", " 6", " 7",
367: " 8", " 9", "10", "11", "12", "13", "14",
368: "15", "16", "17", "18", "19", "20", "21",
369: "22", "23", "24", "25", "26", "27", "28",
370: "29", "30", "31",
371: };
372:
373: if (day == SPACE) {
374: memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
375: return;
376: }
377: if (julian) {
378: if (val = day / 100) {
379: day %= 100;
380: *p++ = val + '0';
381: display = 1;
382: } else {
383: *p++ = ' ';
384: display = 0;
385: }
386: val = day / 10;
387: if (val || display)
388: *p++ = val + '0';
389: else
390: *p++ = ' ';
391: *p++ = day % 10 + '0';
392: } else {
393: *p++ = aday[day][0];
394: *p++ = aday[day][1];
395: }
396: *p = ' ';
397: }
398:
399: void
400: trim_trailing_spaces(s)
401: char *s;
402: {
403: char *p;
404:
405: for (p = s; *p; ++p)
406: continue;
407: while (p > s && isspace(*--p))
408: continue;
409: if (p > s)
410: ++p;
411: *p = '\0';
412: }
413:
414: void
415: center(str, len, separate)
416: char *str;
417: int len;
418: int separate;
419: {
420:
421: len -= strlen(str);
422: (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
423: if (separate)
424: (void)printf("%*s", separate, "");
425: }
426:
427: void
428: usage()
429: {
430:
431: (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
432: exit(1);
433: }