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