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