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