-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcal.cpp
More file actions
130 lines (111 loc) · 3.63 KB
/
cal.cpp
File metadata and controls
130 lines (111 loc) · 3.63 KB
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <cstdio>
#include <cstring>
#include <ctime>
#include <string>
#include <vector>
#include <cfbox/applet.hpp>
#include <cfbox/args.hpp>
#include <cfbox/help.hpp>
namespace {
constexpr cfbox::help::HelpEntry HELP = {
.name = "cal",
.version = CFBOX_VERSION_STRING,
.one_line = "display a calendar",
.usage = "cal [[MONTH] YEAR]",
.options = " -3 show prev/curr/next month\n"
" -y show whole year",
.extra = "",
};
auto days_in_month(int year, int month) -> int {
// month is 1-12
static const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month == 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)))
return 29;
return days[month];
}
auto day_of_week(int year, int month, int day) -> int {
// 0=Sunday, 1=Monday, ..., 6=Saturday (Zeller's congruence simplified)
struct tm tm{};
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
std::mktime(&tm);
return tm.tm_wday; // 0=Sunday
}
auto month_name(int month) -> const char* {
static const char* names[] = {
"", "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
return names[month];
}
auto print_month(int year, int month) -> void {
char header[32];
std::snprintf(header, sizeof(header), "%s %d", month_name(month), year);
auto header_len = static_cast<int>(std::strlen(header));
// Center the header over "Su Mo Tu We Th Fr Sa" (20 chars)
auto pad = (20 - header_len) / 2;
if (pad < 0) pad = 0;
std::printf("%*s%s\n", pad, "", header);
std::printf("Su Mo Tu We Th Fr Sa\n");
auto first_day = day_of_week(year, month, 1);
auto days = days_in_month(year, month);
// Leading spaces
for (int i = 0; i < first_day; ++i) {
std::printf(" ");
}
for (int d = 1; d <= days; ++d) {
std::printf("%2d ", d);
if ((d + first_day) % 7 == 0 || d == days) {
std::printf("\n");
}
}
}
} // anonymous namespace
auto cal_main(int argc, char* argv[]) -> int {
auto parsed = cfbox::args::parse(argc, argv, {
cfbox::args::OptSpec{'3', false, "three"},
cfbox::args::OptSpec{'y', false, "year"},
});
if (parsed.has_long("help")) { cfbox::help::print_help(HELP); return 0; }
if (parsed.has_long("version")) { cfbox::help::print_version(HELP); return 0; }
auto now = std::time(nullptr);
auto tm = std::localtime(&now);
int year = tm->tm_year + 1900;
int month = tm->tm_mon + 1;
const auto& pos = parsed.positional();
if (pos.size() == 1) {
auto val = std::stoi(std::string(pos[0]));
if (val >= 1 && val <= 12) {
month = val;
} else {
year = val;
month = 1;
}
} else if (pos.size() >= 2) {
month = std::stoi(std::string(pos[0]));
year = std::stoi(std::string(pos[1]));
}
bool three = parsed.has('3') || parsed.has_long("three");
bool whole_year = parsed.has('y') || parsed.has_long("year");
if (whole_year) {
for (int m = 1; m <= 12; ++m) {
print_month(year, m);
std::printf("\n");
}
} else if (three) {
// Previous month
int pm = month - 1, py = year;
if (pm < 1) { pm = 12; --py; }
int nm = month + 1, ny = year;
if (nm > 12) { nm = 1; ++ny; }
print_month(py, pm);
std::printf("\n");
print_month(year, month);
std::printf("\n");
print_month(ny, nm);
} else {
print_month(year, month);
}
return 0;
}