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