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