]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/cal.c
Imported from util-linux-2.8 tarball.
[thirdparty/util-linux.git] / misc-utils / cal.c
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 /* This defines _LINUX_C_LIB_VERSION_MAJOR, dunno about gnulibc. We
38 don't want it to read /usr/i586-unknown-linux/include/_G_config.h
39 so we specify fill path. Were we got /usr/i586-unknown-linux from?
40 Dunno. */
41
42 #include "/usr/include/_G_config.h"
43
44 #include <sys/types.h>
45
46 #include <ctype.h>
47 #include <err.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <locale.h>
54
55 /* Test changes to deal with gnulibc, Linux libc 5. */
56 /* #if defined(__linux__) && _LINUX_C_LIB_VERSION_MAJOR > 4 */
57 #if _LINUX_C_LIB_VERSION_MAJOR - 0 > 4 || __GNU_LIBRARY__ - 0 >= 5
58 # define LANGINFO 1
59 #else
60 # define LANGINFO 0
61 #endif
62
63 #if LANGINFO
64 # include <langinfo.h>
65 #else
66 # include <localeinfo.h>
67 #endif
68
69 #define THURSDAY 4 /* for reformation */
70 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
71
72 #define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
73 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
74
75 #define MAXDAYS 42 /* max slots in a month array */
76 #define SPACE -1 /* used in day array */
77
78 static int days_in_month[2][13] = {
79 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
80 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
81 };
82
83 int sep1752[MAXDAYS] = {
84 SPACE, SPACE, 1, 2, 14, 15, 16,
85 17, 18, 19, 20, 21, 22, 23,
86 24, 25, 26, 27, 28, 29, 30,
87 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
88 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
89 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
90 }, j_sep1752[MAXDAYS] = {
91 SPACE, SPACE, 245, 246, 258, 259, 260,
92 261, 262, 263, 264, 265, 266, 267,
93 268, 269, 270, 271, 272, 273, 274,
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 }, empty[MAXDAYS] = {
98 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
99 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
100 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
101 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
102 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
103 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
104 };
105
106 char day_headings[] = " S M Tu W Th F S ";
107 char j_day_headings[] = " S M Tu W Th F S ";
108 const char *full_month[12];
109
110 /* leap year -- account for gregorian reformation in 1752 */
111 #define leap_year(yr) \
112 ((yr) <= 1752 ? !((yr) % 4) : \
113 (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
114
115 /* number of centuries since 1700, not inclusive */
116 #define centuries_since_1700(yr) \
117 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
118
119 /* number of centuries since 1700 whose modulo of 400 is 0 */
120 #define quad_centuries_since_1700(yr) \
121 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
122
123 /* number of leap years between year 1 and this year, not inclusive */
124 #define leap_years_since_year_1(yr) \
125 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
126
127 int julian;
128
129 void ascii_day __P((char *, int));
130 void center __P((const char *, int, int));
131 void day_array __P((int, int, int *));
132 int day_in_week __P((int, int, int));
133 int day_in_year __P((int, int, int));
134 void j_yearly __P((int));
135 void monthly __P((int, int));
136 void trim_trailing_spaces __P((char *));
137 void usage __P((void));
138 void yearly __P((int));
139 void headers_init(void);
140
141 int
142 main(argc, argv)
143 int argc;
144 char **argv;
145 {
146 struct tm *local_time;
147 time_t now;
148 int ch, month, year, yflag;
149
150 #ifdef __linux__
151 extern char *__progname;
152 __progname = argv[0];
153 #endif
154
155 setlocale(LC_ALL,"");
156 headers_init();
157 yflag = 0;
158 while ((ch = getopt(argc, argv, "jy")) != EOF)
159 switch(ch) {
160 case 'j':
161 julian = 1;
162 break;
163 case 'y':
164 yflag = 1;
165 break;
166 case '?':
167 default:
168 usage();
169 }
170 argc -= optind;
171 argv += optind;
172
173 month = year = 0;
174 switch(argc) {
175 case 2:
176 if ((month = atoi(*argv++)) < 1 || month > 12)
177 errx(1, "illegal month value: use 1-12");
178 /* FALLTHROUGH */
179 case 1:
180 if ((year = atoi(*argv)) < 1 || year > 9999)
181 errx(1, "illegal year value: use 1-9999");
182 break;
183 case 0:
184 (void)time(&now);
185 local_time = localtime(&now);
186 year = local_time->tm_year + 1900;
187 if (!yflag)
188 month = local_time->tm_mon + 1;
189 break;
190 default:
191 usage();
192 }
193
194 if (month)
195 monthly(month, year);
196 else if (julian)
197 j_yearly(year);
198 else
199 yearly(year);
200 exit(0);
201 }
202
203 #define DAY_LEN 3 /* 3 spaces per day */
204 #define J_DAY_LEN 4 /* 4 spaces per day */
205 #define WEEK_LEN 21 /* 7 days * 3 characters */
206 #define J_WEEK_LEN 28 /* 7 days * 4 characters */
207 #define HEAD_SEP 2 /* spaces between day headings */
208 #define J_HEAD_SEP 2
209
210 void headers_init(void)
211 {
212 int i;
213
214 strcpy(day_headings,"");
215 strcpy(j_day_headings,"");
216
217 for(i = 0 ; i < 7 ; i++ ) {
218 #if defined(__linux__) && (_LINUX_C_LIB_VERSION_MAJOR > 4 || __GNU_LIBRARY__ > 1)
219 strncat(day_headings,nl_langinfo(ABDAY_1+i),2);
220 strcat(j_day_headings,nl_langinfo(ABDAY_1+i));
221 #else
222 strncat(day_headings,_time_info->abbrev_wkday[i],2);
223 strcat(j_day_headings,_time_info->abbrev_wkday[i]);
224 #endif
225 strcat(day_headings," ");
226 strcat(j_day_headings," ");
227 }
228
229 for (i = 0; i < 12; i++) {
230 #if defined(__linux__) && (_LINUX_C_LIB_VERSION_MAJOR > 4 || __GNU_LIBRARY__ > 1)
231 full_month[i] = nl_langinfo(MON_1+i);
232 #else
233 full_month[i] = _time_info->full_month[i];
234 #endif
235 }
236 }
237
238 void
239 monthly(month, year)
240 int month, year;
241 {
242 int col, row, len, days[MAXDAYS];
243 char *p, lineout[30];
244
245 day_array(month, year, days);
246 len = sprintf(lineout, "%s %d", full_month[month - 1], year);
247 (void)printf("%*s%s\n%s\n",
248 ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
249 lineout, julian ? j_day_headings : day_headings);
250 for (row = 0; row < 6; row++) {
251 for (col = 0, p = lineout; col < 7; col++,
252 p += julian ? J_DAY_LEN : DAY_LEN)
253 ascii_day(p, days[row * 7 + col]);
254 *p = '\0';
255 trim_trailing_spaces(lineout);
256 (void)printf("%s\n", lineout);
257 }
258 }
259
260 void
261 j_yearly(year)
262 int year;
263 {
264 int col, *dp, i, month, row, which_cal;
265 int days[12][MAXDAYS];
266 char *p, lineout[80];
267
268 (void)sprintf(lineout, "%d", year);
269 center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
270 (void)printf("\n\n");
271 for (i = 0; i < 12; i++)
272 day_array(i + 1, year, days[i]);
273 (void)memset(lineout, ' ', sizeof(lineout) - 1);
274 lineout[sizeof(lineout) - 1] = '\0';
275 for (month = 0; month < 12; month += 2) {
276 center(full_month[month], J_WEEK_LEN, J_HEAD_SEP);
277 center(full_month[month + 1], J_WEEK_LEN, 0);
278 (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
279 j_day_headings);
280 for (row = 0; row < 6; row++) {
281 for (which_cal = 0; which_cal < 2; which_cal++) {
282 p = lineout + which_cal * (J_WEEK_LEN + 2);
283 dp = &days[month + which_cal][row * 7];
284 for (col = 0; col < 7; col++, p += J_DAY_LEN)
285 ascii_day(p, *dp++);
286 }
287 *p = '\0';
288 trim_trailing_spaces(lineout);
289 (void)printf("%s\n", lineout);
290 }
291 }
292 (void)printf("\n");
293 }
294
295 void
296 yearly(year)
297 int year;
298 {
299 int col, *dp, i, month, row, which_cal;
300 int days[12][MAXDAYS];
301 char *p, lineout[80];
302
303 (void)sprintf(lineout, "%d", year);
304 center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
305 (void)printf("\n\n");
306 for (i = 0; i < 12; i++)
307 day_array(i + 1, year, days[i]);
308 (void)memset(lineout, ' ', sizeof(lineout) - 1);
309 lineout[sizeof(lineout) - 1] = '\0';
310 for (month = 0; month < 12; month += 3) {
311 center(full_month[month], WEEK_LEN, HEAD_SEP);
312 center(full_month[month + 1], WEEK_LEN, HEAD_SEP);
313 center(full_month[month + 2], WEEK_LEN, 0);
314 (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
315 "", day_headings, HEAD_SEP, "", day_headings);
316 for (row = 0; row < 6; row++) {
317 for (which_cal = 0; which_cal < 3; which_cal++) {
318 p = lineout + which_cal * (WEEK_LEN + 2);
319 dp = &days[month + which_cal][row * 7];
320 for (col = 0; col < 7; col++, p += DAY_LEN)
321 ascii_day(p, *dp++);
322 }
323 *p = '\0';
324 trim_trailing_spaces(lineout);
325 (void)printf("%s\n", lineout);
326 }
327 }
328 (void)printf("\n");
329 }
330
331 /*
332 * day_array --
333 * Fill in an array of 42 integers with a calendar. Assume for a moment
334 * that you took the (maximum) 6 rows in a calendar and stretched them
335 * out end to end. You would have 42 numbers or spaces. This routine
336 * builds that array for any month from Jan. 1 through Dec. 9999.
337 */
338 void
339 day_array(month, year, days)
340 int month, year;
341 int *days;
342 {
343 int day, dw, dm;
344
345 if (month == 9 && year == 1752) {
346 memmove(days,
347 julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
348 return;
349 }
350 memmove(days, empty, MAXDAYS * sizeof(int));
351 dm = days_in_month[leap_year(year)][month];
352 dw = day_in_week(1, month, year);
353 day = julian ? day_in_year(1, month, year) : 1;
354 while (dm--)
355 days[dw++] = day++;
356 }
357
358 /*
359 * day_in_year --
360 * return the 1 based day number within the year
361 */
362 int
363 day_in_year(day, month, year)
364 int day, month, year;
365 {
366 int i, leap;
367
368 leap = leap_year(year);
369 for (i = 1; i < month; i++)
370 day += days_in_month[leap][i];
371 return (day);
372 }
373
374 /*
375 * day_in_week
376 * return the 0 based day number for any date from 1 Jan. 1 to
377 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
378 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
379 * missing days.
380 */
381 int
382 day_in_week(day, month, year)
383 int day, month, year;
384 {
385 long temp;
386
387 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
388 + day_in_year(day, month, year);
389 if (temp < FIRST_MISSING_DAY)
390 return ((temp - 1 + SATURDAY) % 7);
391 if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
392 return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
393 return (THURSDAY);
394 }
395
396 void
397 ascii_day(p, day)
398 char *p;
399 int day;
400 {
401 int display, val;
402 static char *aday[] = {
403 "",
404 " 1", " 2", " 3", " 4", " 5", " 6", " 7",
405 " 8", " 9", "10", "11", "12", "13", "14",
406 "15", "16", "17", "18", "19", "20", "21",
407 "22", "23", "24", "25", "26", "27", "28",
408 "29", "30", "31",
409 };
410
411 if (day == SPACE) {
412 memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
413 return;
414 }
415 if (julian) {
416 if ((val = day / 100)) {
417 day %= 100;
418 *p++ = val + '0';
419 display = 1;
420 } else {
421 *p++ = ' ';
422 display = 0;
423 }
424 val = day / 10;
425 if (val || display)
426 *p++ = val + '0';
427 else
428 *p++ = ' ';
429 *p++ = day % 10 + '0';
430 } else {
431 *p++ = aday[day][0];
432 *p++ = aday[day][1];
433 }
434 *p = ' ';
435 }
436
437 void
438 trim_trailing_spaces(s)
439 char *s;
440 {
441 char *p;
442
443 for (p = s; *p; ++p)
444 continue;
445 while (p > s && isspace(*--p))
446 continue;
447 if (p > s)
448 ++p;
449 *p = '\0';
450 }
451
452 void
453 center(str, len, separate)
454 const char *str;
455 int len;
456 int separate;
457 {
458
459 len -= strlen(str);
460 (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
461 if (separate)
462 (void)printf("%*s", separate, "");
463 }
464
465 void
466 usage()
467 {
468
469 (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
470 exit(1);
471 }