1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35#include "libbb.h"
36#include "unicode.h"
37
38
39
40#define THURSDAY 4
41#define SATURDAY 6
42
43#define FIRST_MISSING_DAY 639787
44#define NUMBER_MISSING_DAYS 11
45
46#define MAXDAYS 42
47#define SPACE -1
48
49static const unsigned char days_in_month[] ALIGN1 = {
50 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
51};
52
53static const unsigned char sep1752[] ALIGN1 = {
54 1, 2, 14, 15, 16,
55 17, 18, 19, 20, 21, 22, 23,
56 24, 25, 26, 27, 28, 29, 30
57};
58
59
60#define julian ((unsigned)option_mask32)
61
62
63static int leap_year(unsigned yr)
64{
65 if (yr <= 1752)
66 return !(yr % 4);
67 return (!(yr % 4) && (yr % 100)) || !(yr % 400);
68}
69
70
71#define centuries_since_1700(yr) \
72 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
73
74
75#define quad_centuries_since_1700(yr) \
76 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
77
78
79#define leap_years_since_year_1(yr) \
80 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
81
82static void center(char *, unsigned, unsigned);
83static void day_array(unsigned, unsigned, unsigned *);
84static void trim_trailing_spaces_and_print(char *);
85
86static void blank_string(char *buf, size_t buflen);
87static char *build_row(char *p, unsigned *dp);
88
89#define DAY_LEN 3
90#define J_DAY_LEN (DAY_LEN + 1)
91#define WEEK_LEN 20
92#define J_WEEK_LEN (WEEK_LEN + 7)
93#define HEAD_SEP 2
94
95int cal_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
96int cal_main(int argc UNUSED_PARAM, char **argv)
97{
98 struct tm zero_tm;
99 time_t now;
100 unsigned month, year, flags, i;
101 char *month_names[12];
102
103
104
105
106 char day_headings[ENABLE_UNICODE_SUPPORT ? 28 * 6 : 28];
107 IF_UNICODE_SUPPORT(char *hp = day_headings;)
108 char buf[40];
109
110 init_unicode();
111
112 flags = getopt32(argv, "jy");
113
114 option_mask32 &= 1;
115 month = 0;
116 argv += optind;
117
118 if (!argv[0]) {
119 struct tm *ptm;
120
121 time(&now);
122 ptm = localtime(&now);
123 year = ptm->tm_year + 1900;
124 if (!(flags & 2)) {
125 month = ptm->tm_mon + 1;
126 }
127 } else {
128 if (argv[1]) {
129 if (argv[2]) {
130 bb_show_usage();
131 }
132 if (!(flags & 2)) {
133 month = xatou_range(*argv, 1, 12);
134 }
135 argv++;
136 }
137 year = xatou_range(*argv, 1, 9999);
138 }
139
140 blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian);
141
142 i = 0;
143 do {
144 zero_tm.tm_mon = i;
145
146 strftime(buf, sizeof(buf), "%B", &zero_tm);
147 month_names[i] = xstrdup(buf);
148
149 if (i < 7) {
150 zero_tm.tm_wday = i;
151
152 strftime(buf, sizeof(buf), "%a", &zero_tm);
153#if ENABLE_UNICODE_SUPPORT
154 if (julian)
155 *hp++ = ' ';
156 {
157 char *two_wchars = unicode_conv_to_printable_fixedwidth( buf, 2);
158 strcpy(hp, two_wchars);
159 free(two_wchars);
160 }
161 hp += strlen(hp);
162 *hp++ = ' ';
163#else
164 strncpy(day_headings + i * (3+julian) + julian, buf, 2);
165#endif
166 }
167 } while (++i < 12);
168 IF_UNICODE_SUPPORT(hp[-1] = '\0';)
169
170 if (month) {
171 unsigned row, len, days[MAXDAYS];
172 unsigned *dp = days;
173 char lineout[30];
174
175 day_array(month, year, dp);
176 len = sprintf(lineout, "%s %u", month_names[month - 1], year);
177 printf("%*s%s\n%s\n",
178 ((7*julian + WEEK_LEN) - len) / 2, "",
179 lineout, day_headings);
180 for (row = 0; row < 6; row++) {
181 build_row(lineout, dp)[0] = '\0';
182 dp += 7;
183 trim_trailing_spaces_and_print(lineout);
184 }
185 } else {
186 unsigned row, which_cal, week_len, days[12][MAXDAYS];
187 unsigned *dp;
188 char lineout[80];
189
190 sprintf(lineout, "%u", year);
191 center(lineout,
192 (WEEK_LEN * 3 + HEAD_SEP * 2)
193 + julian * (J_WEEK_LEN * 2 + HEAD_SEP
194 - (WEEK_LEN * 3 + HEAD_SEP * 2)),
195 0
196 );
197 puts("\n");
198 for (i = 0; i < 12; i++) {
199 day_array(i + 1, year, days[i]);
200 }
201 blank_string(lineout, sizeof(lineout));
202 week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN);
203 for (month = 0; month < 12; month += 3-julian) {
204 center(month_names[month], week_len, HEAD_SEP);
205 if (!julian) {
206 center(month_names[month + 1], week_len, HEAD_SEP);
207 }
208 center(month_names[month + 2 - julian], week_len, 0);
209 printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings);
210 if (!julian) {
211 printf("%*s%s", HEAD_SEP, "", day_headings);
212 }
213 bb_putchar('\n');
214 for (row = 0; row < (6*7); row += 7) {
215 for (which_cal = 0; which_cal < 3-julian; which_cal++) {
216 dp = days[month + which_cal] + row;
217 build_row(lineout + which_cal * (week_len + 2), dp);
218 }
219
220 trim_trailing_spaces_and_print(lineout);
221 }
222 }
223 }
224
225 fflush_stdout_and_exit(EXIT_SUCCESS);
226}
227
228
229
230
231
232
233
234
235static void day_array(unsigned month, unsigned year, unsigned *days)
236{
237 unsigned long temp;
238 unsigned i;
239 unsigned day, dw, dm;
240
241 memset(days, SPACE, MAXDAYS * sizeof(int));
242
243 if ((month == 9) && (year == 1752)) {
244
245
246
247 unsigned j_offset = julian * 244;
248 size_t oday = 0;
249
250 do {
251 days[oday+2] = sep1752[oday] + j_offset;
252 } while (++oday < sizeof(sep1752));
253
254 return;
255 }
256
257
258
259
260 day = 1;
261 if ((month > 2) && leap_year(year)) {
262 ++day;
263 }
264
265 i = month;
266 while (i) {
267 day += days_in_month[--i];
268 }
269
270
271
272
273
274
275
276 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + day;
277 if (temp < FIRST_MISSING_DAY) {
278 dw = ((temp - 1 + SATURDAY) % 7);
279 } else {
280 dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
281 }
282
283 if (!julian) {
284 day = 1;
285 }
286
287 dm = days_in_month[month];
288 if ((month == 2) && leap_year(year)) {
289 ++dm;
290 }
291
292 do {
293 days[dw++] = day++;
294 } while (--dm);
295}
296
297static void trim_trailing_spaces_and_print(char *s)
298{
299 char *p = s;
300
301 while (*p) {
302 ++p;
303 }
304 while (p != s) {
305 --p;
306 if (!isspace(*p)) {
307 p[1] = '\0';
308 break;
309 }
310 }
311
312 puts(s);
313}
314
315static void center(char *str, unsigned len, unsigned separate)
316{
317 unsigned n = strlen(str);
318 len -= n;
319 printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, "");
320}
321
322static void blank_string(char *buf, size_t buflen)
323{
324 memset(buf, ' ', buflen);
325 buf[buflen-1] = '\0';
326}
327
328static char *build_row(char *p, unsigned *dp)
329{
330 unsigned col, val, day;
331
332 memset(p, ' ', (julian + DAY_LEN) * 7);
333
334 col = 0;
335 do {
336 day = *dp++;
337 if (day != SPACE) {
338 if (julian) {
339 ++p;
340 if (day >= 100) {
341 *p = '0';
342 p[-1] = (day / 100) + '0';
343 day %= 100;
344 }
345 }
346 val = day / 10;
347 if (val > 0) {
348 *p = val + '0';
349 }
350 *++p = day % 10 + '0';
351 p += 2;
352 } else {
353 p += DAY_LEN + julian;
354 }
355 } while (++col < 7);
356
357 return p;
358}
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391