1 /* vi: set sw=4 ts=4: */
3 * Calendar implementation for busybox
5 * See original copyright at the end of this file
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 /* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
11 /* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream
12 * BB_AUDIT BUG: version in util-linux seems to be broken as well. */
13 /* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */
15 /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
17 * Major size reduction... over 50% (>1.5k) on i386.
20 //usage:#define cal_trivial_usage
21 //usage: "[-jy] [[MONTH] YEAR]"
22 //usage:#define cal_full_usage "\n\n"
23 //usage: "Display a calendar\n"
24 //usage: "\n -j Use julian dates"
25 //usage: "\n -y Display the entire year"
30 /* We often use "unsigned" intead of "int", it's easier to div on most CPUs */
32 #define THURSDAY 4 /* for reformation */
33 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
35 #define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
36 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
38 #define MAXDAYS 42 /* max slots in a month array */
39 #define SPACE -1 /* used in day array */
41 static const unsigned char days_in_month
[] ALIGN1
= {
42 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
45 static const unsigned char sep1752
[] ALIGN1
= {
47 17, 18, 19, 20, 21, 22, 23,
48 24, 25, 26, 27, 28, 29, 30
51 /* Set to 0 or 1 in main */
52 #define julian ((unsigned)option_mask32)
54 /* leap year -- account for Gregorian reformation in 1752 */
55 static int leap_year(unsigned yr
)
59 return (!(yr
% 4) && (yr
% 100)) || !(yr
% 400);
62 /* number of centuries since 1700, not inclusive */
63 #define centuries_since_1700(yr) \
64 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
66 /* number of centuries since 1700 whose modulo of 400 is 0 */
67 #define quad_centuries_since_1700(yr) \
68 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
70 /* number of leap years between year 1 and this year, not inclusive */
71 #define leap_years_since_year_1(yr) \
72 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
74 static void center(char *, unsigned, unsigned);
75 static void day_array(unsigned, unsigned, unsigned *);
76 static void trim_trailing_spaces_and_print(char *);
78 static void blank_string(char *buf
, size_t buflen
);
79 static char *build_row(char *p
, unsigned *dp
);
81 #define DAY_LEN 3 /* 3 spaces per day */
82 #define J_DAY_LEN (DAY_LEN + 1)
83 #define WEEK_LEN 20 /* 7 * 3 - one space at the end */
84 #define J_WEEK_LEN (WEEK_LEN + 7)
85 #define HEAD_SEP 2 /* spaces between day headings */
87 int cal_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
88 int cal_main(int argc UNUSED_PARAM
, char **argv
)
92 unsigned month
, year
, flags
, i
;
93 char *month_names
[12];
95 /* "Su Mo Tu We Th Fr Sa" */
97 /* " Su Mo Tu We Th Fr Sa" */
98 char day_headings
[ENABLE_UNICODE_SUPPORT
? 28 * 6 : 28];
99 IF_UNICODE_SUPPORT(char *hp
= day_headings
;)
104 flags
= getopt32(argv
, "jy");
105 /* This sets julian = flags & 1: */
114 ptm
= localtime(&now
);
115 year
= ptm
->tm_year
+ 1900;
116 if (!(flags
& 2)) { /* no -y */
117 month
= ptm
->tm_mon
+ 1;
124 if (!(flags
& 2)) { /* no -y */
125 month
= xatou_range(*argv
, 1, 12);
129 year
= xatou_range(*argv
, 1, 9999);
132 blank_string(day_headings
, sizeof(day_headings
) - 7 + 7*julian
);
137 /* full month name according to locale */
138 strftime(buf
, sizeof(buf
), "%B", &zero_tm
);
139 month_names
[i
] = xstrdup(buf
);
143 /* abbreviated weekday name according to locale */
144 strftime(buf
, sizeof(buf
), "%a", &zero_tm
);
145 #if ENABLE_UNICODE_SUPPORT
149 char *two_wchars
= unicode_conv_to_printable_fixedwidth(/*NULL,*/ buf
, 2);
150 strcpy(hp
, two_wchars
);
156 strncpy(day_headings
+ i
* (3+julian
) + julian
, buf
, 2);
160 IF_UNICODE_SUPPORT(hp
[-1] = '\0';)
163 unsigned row
, len
, days
[MAXDAYS
];
167 day_array(month
, year
, dp
);
168 len
= sprintf(lineout
, "%s %d", month_names
[month
- 1], year
);
169 printf("%*s%s\n%s\n",
170 ((7*julian
+ WEEK_LEN
) - len
) / 2, "",
171 lineout
, day_headings
);
172 for (row
= 0; row
< 6; row
++) {
173 build_row(lineout
, dp
)[0] = '\0';
175 trim_trailing_spaces_and_print(lineout
);
178 unsigned row
, which_cal
, week_len
, days
[12][MAXDAYS
];
182 sprintf(lineout
, "%u", year
);
184 (WEEK_LEN
* 3 + HEAD_SEP
* 2)
185 + julian
* (J_WEEK_LEN
* 2 + HEAD_SEP
186 - (WEEK_LEN
* 3 + HEAD_SEP
* 2)),
189 puts("\n"); /* two \n's */
190 for (i
= 0; i
< 12; i
++) {
191 day_array(i
+ 1, year
, days
[i
]);
193 blank_string(lineout
, sizeof(lineout
));
194 week_len
= WEEK_LEN
+ julian
* (J_WEEK_LEN
- WEEK_LEN
);
195 for (month
= 0; month
< 12; month
+= 3-julian
) {
196 center(month_names
[month
], week_len
, HEAD_SEP
);
198 center(month_names
[month
+ 1], week_len
, HEAD_SEP
);
200 center(month_names
[month
+ 2 - julian
], week_len
, 0);
201 printf("\n%s%*s%s", day_headings
, HEAD_SEP
, "", day_headings
);
203 printf("%*s%s", HEAD_SEP
, "", day_headings
);
206 for (row
= 0; row
< (6*7); row
+= 7) {
207 for (which_cal
= 0; which_cal
< 3-julian
; which_cal
++) {
208 dp
= days
[month
+ which_cal
] + row
;
209 build_row(lineout
+ which_cal
* (week_len
+ 2), dp
);
211 /* blank_string took care of nul termination. */
212 trim_trailing_spaces_and_print(lineout
);
217 fflush_stdout_and_exit(EXIT_SUCCESS
);
222 * Fill in an array of 42 integers with a calendar. Assume for a moment
223 * that you took the (maximum) 6 rows in a calendar and stretched them
224 * out end to end. You would have 42 numbers or spaces. This routine
225 * builds that array for any month from Jan. 1 through Dec. 9999.
227 static void day_array(unsigned month
, unsigned year
, unsigned *days
)
231 unsigned day
, dw
, dm
;
233 memset(days
, SPACE
, MAXDAYS
* sizeof(int));
235 if ((month
== 9) && (year
== 1752)) {
236 /* Assumes the Gregorian reformation eliminates
237 * 3 Sep. 1752 through 13 Sep. 1752.
239 unsigned j_offset
= julian
* 244;
243 days
[oday
+2] = sep1752
[oday
] + j_offset
;
244 } while (++oday
< sizeof(sep1752
));
250 * return the 1 based day number within the year
253 if ((month
> 2) && leap_year(year
)) {
259 day
+= days_in_month
[--i
];
263 * return the 0 based day number for any date from 1 Jan. 1 to
264 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
265 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
268 temp
= (long)(year
- 1) * 365 + leap_years_since_year_1(year
- 1) + day
;
269 if (temp
< FIRST_MISSING_DAY
) {
270 dw
= ((temp
- 1 + SATURDAY
) % 7);
272 dw
= (((temp
- 1 + SATURDAY
) - NUMBER_MISSING_DAYS
) % 7);
279 dm
= days_in_month
[month
];
280 if ((month
== 2) && leap_year(year
)) {
289 static void trim_trailing_spaces_and_print(char *s
)
307 static void center(char *str
, unsigned len
, unsigned separate
)
309 unsigned n
= strlen(str
);
311 printf("%*s%*s", (len
/2) + n
, str
, (len
/2) + (len
% 2) + separate
, "");
314 static void blank_string(char *buf
, size_t buflen
)
316 memset(buf
, ' ', buflen
);
317 buf
[buflen
-1] = '\0';
320 static char *build_row(char *p
, unsigned *dp
)
322 unsigned col
, val
, day
;
324 memset(p
, ' ', (julian
+ DAY_LEN
) * 7);
334 p
[-1] = (day
/ 100) + '0';
342 *++p
= day
% 10 + '0';
345 p
+= DAY_LEN
+ julian
;
353 * Copyright (c) 1989, 1993, 1994
354 * The Regents of the University of California. All rights reserved.
356 * This code is derived from software contributed to Berkeley by
359 * Redistribution and use in source and binary forms, with or without
360 * modification, are permitted provided that the following conditions
362 * 1. Redistributions of source code must retain the above copyright
363 * notice, this list of conditions and the following disclaimer.
364 * 2. Redistributions in binary form must reproduce the above copyright
365 * notice, this list of conditions and the following disclaimer in the
366 * documentation and/or other materials provided with the distribution.
367 * 3. Neither the name of the University nor the names of its contributors
368 * may be used to endorse or promote products derived from this software
369 * without specific prior written permission.
371 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
372 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
373 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
374 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
375 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
376 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
377 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
378 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
379 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
380 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF