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)),
188 puts("\n"); /* two \n's */
189 for (i
= 0; i
< 12; i
++) {
190 day_array(i
+ 1, year
, days
[i
]);
192 blank_string(lineout
, sizeof(lineout
));
193 week_len
= WEEK_LEN
+ julian
* (J_WEEK_LEN
- WEEK_LEN
);
194 for (month
= 0; month
< 12; month
+= 3-julian
) {
195 center(month_names
[month
], week_len
, HEAD_SEP
);
197 center(month_names
[month
+ 1], week_len
, HEAD_SEP
);
199 center(month_names
[month
+ 2 - julian
], week_len
, 0);
200 printf("\n%s%*s%s", day_headings
, HEAD_SEP
, "", day_headings
);
202 printf("%*s%s", HEAD_SEP
, "", day_headings
);
205 for (row
= 0; row
< (6*7); row
+= 7) {
206 for (which_cal
= 0; which_cal
< 3-julian
; which_cal
++) {
207 dp
= days
[month
+ which_cal
] + row
;
208 build_row(lineout
+ which_cal
* (week_len
+ 2), dp
);
210 /* blank_string took care of nul termination. */
211 trim_trailing_spaces_and_print(lineout
);
216 fflush_stdout_and_exit(EXIT_SUCCESS
);
221 * Fill in an array of 42 integers with a calendar. Assume for a moment
222 * that you took the (maximum) 6 rows in a calendar and stretched them
223 * out end to end. You would have 42 numbers or spaces. This routine
224 * builds that array for any month from Jan. 1 through Dec. 9999.
226 static void day_array(unsigned month
, unsigned year
, unsigned *days
)
230 unsigned day
, dw
, dm
;
232 memset(days
, SPACE
, MAXDAYS
* sizeof(int));
234 if ((month
== 9) && (year
== 1752)) {
235 /* Assumes the Gregorian reformation eliminates
236 * 3 Sep. 1752 through 13 Sep. 1752.
238 unsigned j_offset
= julian
* 244;
242 days
[oday
+2] = sep1752
[oday
] + j_offset
;
243 } while (++oday
< sizeof(sep1752
));
249 * return the 1 based day number within the year
252 if ((month
> 2) && leap_year(year
)) {
258 day
+= days_in_month
[--i
];
262 * return the 0 based day number for any date from 1 Jan. 1 to
263 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
264 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
267 temp
= (long)(year
- 1) * 365 + leap_years_since_year_1(year
- 1) + day
;
268 if (temp
< FIRST_MISSING_DAY
) {
269 dw
= ((temp
- 1 + SATURDAY
) % 7);
271 dw
= (((temp
- 1 + SATURDAY
) - NUMBER_MISSING_DAYS
) % 7);
278 dm
= days_in_month
[month
];
279 if ((month
== 2) && leap_year(year
)) {
288 static void trim_trailing_spaces_and_print(char *s
)
306 static void center(char *str
, unsigned len
, unsigned separate
)
308 unsigned n
= strlen(str
);
310 printf("%*s%*s", (len
/2) + n
, str
, (len
/2) + (len
% 2) + separate
, "");
313 static void blank_string(char *buf
, size_t buflen
)
315 memset(buf
, ' ', buflen
);
316 buf
[buflen
-1] = '\0';
319 static char *build_row(char *p
, unsigned *dp
)
321 unsigned col
, val
, day
;
323 memset(p
, ' ', (julian
+ DAY_LEN
) * 7);
333 p
[-1] = (day
/ 100) + '0';
341 *++p
= day
% 10 + '0';
344 p
+= DAY_LEN
+ julian
;
352 * Copyright (c) 1989, 1993, 1994
353 * The Regents of the University of California. All rights reserved.
355 * This code is derived from software contributed to Berkeley by
358 * Redistribution and use in source and binary forms, with or without
359 * modification, are permitted provided that the following conditions
361 * 1. Redistributions of source code must retain the above copyright
362 * notice, this list of conditions and the following disclaimer.
363 * 2. Redistributions in binary form must reproduce the above copyright
364 * notice, this list of conditions and the following disclaimer in the
365 * documentation and/or other materials provided with the distribution.
366 * 3. Neither the name of the University nor the names of its contributors
367 * may be used to endorse or promote products derived from this software
368 * without specific prior written permission.
370 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
371 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
372 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
373 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
374 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
375 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
376 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
377 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
378 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
379 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF