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