-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.cpp
193 lines (160 loc) · 6.93 KB
/
main.cpp
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <string>
#include <cassert>
#include <unordered_map>
namespace fs = std::filesystem;
using indentation_level_t = unsigned short;
std::string n_whitespaces(indentation_level_t n) {
std::string out;
out.reserve(n);
while (n--) {
out.push_back(' ');
}
return out;
}
void write_as_html_comment(std::string& b, const std::string& s) {
b.append("<!-- ");
b.append(s);
b.append(" -->");
}
void exit_if_file_doesnt_exist(const std::string& filename) noexcept {
std::FILE* file = std::fopen(filename.c_str(), "r");
if (file == nullptr) { // faster than using fstream
std::printf("Fatal error - file \"%s\" doesn't exist, exiting...\n", filename.c_str());
std::exit(EXIT_FAILURE);
}
std::fclose(file);
}
static std::unordered_map<std::string, std::string> temporaries;
template <bool _is_on_bottom_of_stack = true>
void rewrite_with_filled_templates(const fs::path& path, bool should_write_comments) {
// if _is_on_bottom_of_stack: will create a new file named t.<p>
// else (only called this way within this function, with template files): creates temporary buffers with handled
// templates within them (templates can have other templates inside), to be used in the creation of the main output file
exit_if_file_doesnt_exist(path.string());
std::ifstream ifs(path);
ifs >> std::noskipws;
char current_c;
std::string new_contents;
const std::string input_file_parent_path_str = path.parent_path().string() + "/";
constexpr auto OUTPUT_FILE_PREFIX = "t.";
constexpr auto TEMPORARY_PREFIX = "temp.";
indentation_level_t indentation_level{};
while (ifs >> current_c) {
if (current_c == '<') {
lessthan_encountered:
ifs >> current_c;
if (current_c == '@') {
std::string potential_filename;
constexpr std::uint8_t ITERS_MAX = 255; // max filename length
std::remove_const_t<decltype(ITERS_MAX)> iter_counter{};
bool reached_iters_max = false;
while (ifs >> current_c) {
if (current_c == '@') {
ifs >> current_c;
if (current_c == '>') {
break;
}
else {
potential_filename.push_back('@');
}
}
potential_filename.push_back(current_c);
++iter_counter;
if (iter_counter == ITERS_MAX) {
reached_iters_max = true;
break;
}
}
if (reached_iters_max) {
new_contents.append(potential_filename);
} else /* found the filename */ {
const std::string potential_filename_with_dir = input_file_parent_path_str + potential_filename;
exit_if_file_doesnt_exist(potential_filename_with_dir);
// handle templates recursively - if _is_on_bottom_of_stack, the new contents will be written to a new file,
// else - they will be written to the temporaries map
// ifs at the bottom of this function handle this behaviour
rewrite_with_filled_templates<false>(potential_filename_with_dir, should_write_comments);
std::istringstream stream_template_contents(temporaries.at(input_file_parent_path_str + TEMPORARY_PREFIX + potential_filename));
const std::string indentation_whitespaces = n_whitespaces(indentation_level);
std::string line;
if (should_write_comments) {
// the comment will be indented appropriately
write_as_html_comment(new_contents, potential_filename);
new_contents.append("\n");
} else {
// first line has to be handled separately if the comment isn't written, because the already written whitespace isnt used then
// so we will use it now, to indent the first line appropriately
std::getline(stream_template_contents, line);
new_contents.append(line);
new_contents.push_back('\n');
}
while (std::getline(stream_template_contents, line)) {
new_contents.append(indentation_whitespaces);
new_contents.append(line);
new_contents.push_back('\n');
}
if (should_write_comments) {
new_contents.append(indentation_whitespaces);
write_as_html_comment(new_contents, "END " + potential_filename);
}
}
}
else {
new_contents.push_back('<');
new_contents.push_back(current_c);
}
}
else if (current_c == '\n') {
indentation_level = 0;
while (ifs >> current_c) {
if (current_c != ' ') {
new_contents.push_back('\n');
for (decltype(indentation_level) i = 0; i < indentation_level; ++i) {
new_contents.push_back(' ');
}
if (current_c == '<') {
goto lessthan_encountered; // should be completely safe :)
}
new_contents.push_back(current_c);
break;
}
++indentation_level;
}
}
else {
new_contents.push_back(current_c);
}
}
if constexpr (_is_on_bottom_of_stack) {
// create the main file
std::ofstream(input_file_parent_path_str + OUTPUT_FILE_PREFIX + path.filename().string()) << new_contents;
} else {
// create buffers for templates inside templates, which are not meant to be written to the disk,
// we only want to write the main file to the disk
temporaries[input_file_parent_path_str + TEMPORARY_PREFIX + path.filename().string()] = new_contents;
}
}
int main(int argc, const char* const* argv) {
if (argc < 2) {
std::puts("At least one input file has to be specified, exiting...\n");
return EXIT_FAILURE;
}
bool should_write_comments = true;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "-nc") {
should_write_comments = false;
}
}
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "-nc") {
continue;
}
rewrite_with_filled_templates(arg, should_write_comments);
}
return EXIT_SUCCESS;
}