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