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