]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/cal.c
aba1b6f061fd53638ce77814d2ec18bf433788eb
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
37 /* 1999-02-01 Jean-Francois Bignolles: added option '-m' to display
38 * monday as the first day of the week.
39 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
40 * - added Native Language Support
42 * 2000-09-01 Michael Charles Pruznick <dummy@netwiz.net>
43 * Added "-3" option to print prev/next month with current.
44 * Added over-ridable default NUM_MONTHS and "-1" option to
45 * get traditional output when -3 is the default. I hope that
46 * enough people will like -3 as the default that one day the
47 * product can be shipped that way.
50 #include <sys/types.h>
61 #include "../defines.h"
63 #ifdef HAVE_langinfo_h
64 # include <langinfo.h>
66 # include <localeinfo.h> /* libc4 only */
69 /* allow compile-time define to over-ride default */
74 #if ( NUM_MONTHS != 1 && NUM_MONTHS !=3 )
75 #error NUM_MONTHS must be 1 or 3
78 #define THURSDAY 4 /* for reformation */
79 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
81 #define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
82 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
84 #define MAXDAYS 43 /* max slots in a month array */
85 #define SPACE -1 /* used in day array */
87 static int days_in_month
[2][13] = {
88 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
89 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
92 int sep1752
[MAXDAYS
] = {
93 SPACE
, SPACE
, 1, 2, 14, 15, 16,
94 17, 18, 19, 20, 21, 22, 23,
95 24, 25, 26, 27, 28, 29, 30,
96 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
97 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
98 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
100 }, j_sep1752
[MAXDAYS
] = {
101 SPACE
, SPACE
, 245, 246, 258, 259, 260,
102 261, 262, 263, 264, 265, 266, 267,
103 268, 269, 270, 271, 272, 273, 274,
104 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
105 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
106 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
108 }, empty
[MAXDAYS
] = {
109 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
110 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
111 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
112 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
113 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
114 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
118 char day_headings
[] = " S M Tu W Th F S ";
119 /* week1stday = 1 => " M Tu W Th F S S " */
120 char j_day_headings
[] = "Sun Mon Tue Wed Thu Fri Sat ";
121 /* week1stday = 1 => " M Tu W Th F S S " */
122 const char *full_month
[12];
124 /* leap year -- account for gregorian reformation in 1752 */
125 #define leap_year(yr) \
126 ((yr) <= 1752 ? !((yr) % 4) : \
127 (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
129 /* number of centuries since 1700, not inclusive */
130 #define centuries_since_1700(yr) \
131 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
133 /* number of centuries since 1700 whose modulo of 400 is 0 */
134 #define quad_centuries_since_1700(yr) \
135 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
137 /* number of leap years between year 1 and this year, not inclusive */
138 #define leap_years_since_year_1(yr) \
139 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
141 /* 0 => sunday (default), 1 => monday */
145 #define FMT_ST_LINES 8
146 #define FMT_ST_CHARS 300 /* 90 suffices in most locales */
149 char s
[FMT_ST_LINES
][FMT_ST_CHARS
];
152 void ascii_day
__P((char *, int));
153 void center
__P((const char *, int, int));
154 void day_array
__P((int, int, int *));
155 int day_in_week
__P((int, int, int));
156 int day_in_year
__P((int, int, int));
157 void j_yearly
__P((int));
158 void do_monthly
__P((int, int, struct fmt_st
*));
159 void monthly
__P((int, int));
160 void monthly3
__P((int, int));
161 void trim_trailing_spaces
__P((char *));
162 void usage
__P((void));
163 void yearly
__P((int));
164 void headers_init(void);
165 extern char *__progname
;
168 main(int argc
, char **argv
) {
169 struct tm
*local_time
;
171 int ch
, month
, year
, yflag
;
173 int num_months
= NUM_MONTHS
;
176 if ((p
= strrchr(progname
, '/')) != NULL
)
178 __progname
= progname
;
180 setlocale(LC_ALL
, "");
181 bindtextdomain(PACKAGE
, LOCALEDIR
);
185 while ((ch
= getopt(argc
, argv
, "13mjyV")) != EOF
)
203 printf(_("%s from %s\n"),
204 progname
, util_linux_version
);
216 if ((month
= atoi(*argv
++)) < 1 || month
> 12)
217 errx(1, _("illegal month value: use 1-12"));
220 if ((year
= atoi(*argv
)) < 1 || year
> 9999)
221 errx(1, _("illegal year value: use 1-9999"));
225 local_time
= localtime(&now
);
226 year
= local_time
->tm_year
+ 1900;
228 month
= local_time
->tm_mon
+ 1;
235 if (month
&& num_months
== 1)
236 monthly(month
, year
);
237 else if (month
&& num_months
== 3)
238 monthly3(month
, year
);
246 #define DAY_LEN 3 /* 3 spaces per day */
247 #define J_DAY_LEN 4 /* 4 spaces per day */
248 #define WEEK_LEN 21 /* 7 days * 3 characters */
249 #define J_WEEK_LEN 28 /* 7 days * 4 characters */
250 #define HEAD_SEP 2 /* spaces between day headings */
253 void headers_init(void)
257 strcpy(day_headings
,"");
258 strcpy(j_day_headings
,"");
260 #ifdef HAVE_langinfo_h
261 # define weekday(wd) nl_langinfo(ABDAY_1+wd)
263 # define weekday(wd) _time_info->abbrev_wkday[wd]
266 for(i
= 0 ; i
< 7 ; i
++ ) {
267 wd
= (i
+ week1stday
) % 7;
268 strncat(day_headings
,weekday(wd
),2);
269 strncat(j_day_headings
,weekday(wd
),3);
270 if (strlen(weekday(wd
)) == 2)
271 strcat(j_day_headings
," ");
272 strcat(day_headings
," ");
273 strcat(j_day_headings
," ");
278 for (i
= 0; i
< 12; i
++) {
279 #ifdef HAVE_langinfo_h
280 full_month
[i
] = nl_langinfo(MON_1
+i
);
282 full_month
[i
] = _time_info
->full_month
[i
];
288 do_monthly(month
, year
, out
)
292 int col
, row
, len
, days
[MAXDAYS
];
293 char *p
, lineout
[300];
295 day_array(month
, year
, days
);
296 len
= sprintf(lineout
, "%s %d", full_month
[month
- 1], year
);
297 (void)sprintf(out
->s
[0],"%*s%s",
298 ((julian
? J_WEEK_LEN
: WEEK_LEN
) - len
) / 2, "", lineout
);
299 (void)sprintf(out
->s
[1],"%s",
300 julian
? j_day_headings
: day_headings
);
301 for (row
= 0; row
< 6; row
++) {
302 for (col
= 0, p
= lineout
; col
< 7; col
++,
303 p
+= julian
? J_DAY_LEN
: DAY_LEN
)
304 ascii_day(p
, days
[row
* 7 + col
]);
306 trim_trailing_spaces(lineout
);
307 (void)sprintf(out
->s
[row
+2],"%s", lineout
);
318 do_monthly(month
, year
, &out
);
319 for ( i
= 0; i
< FMT_ST_LINES
; i
++ )
321 printf("%s\n", out
.s
[i
]);
326 monthly3(month
, year
)
331 struct fmt_st out_prev
;
332 struct fmt_st out_curm
;
333 struct fmt_st out_next
;
334 int prev_month
, prev_year
;
335 int next_month
, next_year
;
340 prev_year
= year
- 1;
344 prev_month
= month
- 1;
350 next_year
= year
+ 1;
354 next_month
= month
+ 1;
358 do_monthly(prev_month
, prev_year
, &out_prev
);
359 do_monthly(month
, year
, &out_curm
);
360 do_monthly(next_month
, next_year
, &out_next
);
361 width
= (julian
? J_WEEK_LEN
: WEEK_LEN
);
362 for ( i
= 0; i
< FMT_ST_LINES
; i
++ )
364 printf("%-*.*s %-*.*s %-*.*s\n",
365 width
, width
, out_prev
.s
[i
],
366 width
, width
, out_curm
.s
[i
],
367 width
, width
, out_next
.s
[i
] );
375 int col
, *dp
, i
, month
, row
, which_cal
;
376 int days
[12][MAXDAYS
];
377 char *p
, lineout
[80];
379 (void)sprintf(lineout
, "%d", year
);
380 center(lineout
, J_WEEK_LEN
* 2 + J_HEAD_SEP
, 0);
381 (void)printf("\n\n");
382 for (i
= 0; i
< 12; i
++)
383 day_array(i
+ 1, year
, days
[i
]);
384 (void)memset(lineout
, ' ', sizeof(lineout
) - 1);
385 lineout
[sizeof(lineout
) - 1] = '\0';
386 for (month
= 0; month
< 12; month
+= 2) {
387 center(full_month
[month
], J_WEEK_LEN
, J_HEAD_SEP
);
388 center(full_month
[month
+ 1], J_WEEK_LEN
, 0);
389 (void)printf("\n%s%*s%s\n", j_day_headings
, J_HEAD_SEP
, "",
391 for (row
= 0; row
< 6; row
++) {
392 for (which_cal
= 0; which_cal
< 2; which_cal
++) {
393 p
= lineout
+ which_cal
* (J_WEEK_LEN
+ 2);
394 dp
= &days
[month
+ which_cal
][row
* 7];
395 for (col
= 0; col
< 7; col
++, p
+= J_DAY_LEN
)
399 trim_trailing_spaces(lineout
);
400 (void)printf("%s\n", lineout
);
410 int col
, *dp
, i
, month
, row
, which_cal
;
411 int days
[12][MAXDAYS
];
412 char *p
, lineout
[80];
414 (void)sprintf(lineout
, "%d", year
);
415 center(lineout
, WEEK_LEN
* 3 + HEAD_SEP
* 2, 0);
416 (void)printf("\n\n");
417 for (i
= 0; i
< 12; i
++)
418 day_array(i
+ 1, year
, days
[i
]);
419 (void)memset(lineout
, ' ', sizeof(lineout
) - 1);
420 lineout
[sizeof(lineout
) - 1] = '\0';
421 for (month
= 0; month
< 12; month
+= 3) {
422 center(full_month
[month
], WEEK_LEN
, HEAD_SEP
);
423 center(full_month
[month
+ 1], WEEK_LEN
, HEAD_SEP
);
424 center(full_month
[month
+ 2], WEEK_LEN
, 0);
425 (void)printf("\n%s%*s%s%*s%s\n", day_headings
, HEAD_SEP
,
426 "", day_headings
, HEAD_SEP
, "", day_headings
);
427 for (row
= 0; row
< 6; row
++) {
428 for (which_cal
= 0; which_cal
< 3; which_cal
++) {
429 p
= lineout
+ which_cal
* (WEEK_LEN
+ 2);
430 dp
= &days
[month
+ which_cal
][row
* 7];
431 for (col
= 0; col
< 7; col
++, p
+= DAY_LEN
)
435 trim_trailing_spaces(lineout
);
436 (void)printf("%s\n", lineout
);
444 * Fill in an array of 42 integers with a calendar. Assume for a moment
445 * that you took the (maximum) 6 rows in a calendar and stretched them
446 * out end to end. You would have 42 numbers or spaces. This routine
447 * builds that array for any month from Jan. 1 through Dec. 9999.
450 day_array(month
, year
, days
)
457 if (month
== 9 && year
== 1752) {
458 d_sep1752
= julian
? j_sep1752
: sep1752
;
459 memcpy(days
, d_sep1752
+ week1stday
, MAXDAYS
* sizeof(int));
462 memcpy(days
, empty
, MAXDAYS
* sizeof(int));
463 dm
= days_in_month
[leap_year(year
)][month
];
464 dw
= (day_in_week(1, month
, year
) - week1stday
+ 7) % 7;
465 day
= julian
? day_in_year(1, month
, year
) : 1;
472 * return the 1 based day number within the year
475 day_in_year(day
, month
, year
)
476 int day
, month
, year
;
480 leap
= leap_year(year
);
481 for (i
= 1; i
< month
; i
++)
482 day
+= days_in_month
[leap
][i
];
488 * return the 0 based day number for any date from 1 Jan. 1 to
489 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
490 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
494 day_in_week(day
, month
, year
)
495 int day
, month
, year
;
499 temp
= (long)(year
- 1) * 365 + leap_years_since_year_1(year
- 1)
500 + day_in_year(day
, month
, year
);
501 if (temp
< FIRST_MISSING_DAY
)
502 return ((temp
- 1 + SATURDAY
) % 7);
503 if (temp
>= (FIRST_MISSING_DAY
+ NUMBER_MISSING_DAYS
))
504 return (((temp
- 1 + SATURDAY
) - NUMBER_MISSING_DAYS
) % 7);
514 static char *aday
[] = {
516 " 1", " 2", " 3", " 4", " 5", " 6", " 7",
517 " 8", " 9", "10", "11", "12", "13", "14",
518 "15", "16", "17", "18", "19", "20", "21",
519 "22", "23", "24", "25", "26", "27", "28",
524 memset(p
, ' ', julian
? J_DAY_LEN
: DAY_LEN
);
528 if ((val
= day
/ 100)) {
541 *p
++ = day
% 10 + '0';
550 trim_trailing_spaces(s
)
557 while (p
> s
&& isspace(*--p
))
565 center(str
, len
, separate
)
572 (void)printf("%*s%s%*s", len
/ 2, "", str
, len
/ 2 + len
% 2, "");
574 (void)printf("%*s", separate
, "");
581 (void)fprintf(stderr
, _("usage: cal [-mjyV] [[month] year]\n"));