generated from steve02081504/empty
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHeader_File_constructor.cpp
395 lines (379 loc) · 15.2 KB
/
Header_File_constructor.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
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
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
384
385
386
387
388
389
390
391
392
393
394
395
// Header_File_constructor.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <regex>
#include <filesystem>
#include <map>
#if defined(_WIN32)
#include <Windows.h>
#endif
std::regex include_reg("^#[ \t]*include[ \t]+\"([^\"]+)\"[ \t]*");
std::regex define_with_value_reg("^#[ \t]*define[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]+(.*)[ \t]*");
std::regex define_reg("^#[ \t]*define[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*");
std::regex undef_reg("^#[ \t]*undef[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*");
std::regex ifndef_reg("^#[ \t]*ifndef[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*");
//#if !defined(
std::regex ifndef_reg2("^#[ \t]*if[ \t]+!defined[ \t]*\\([ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*\\)[ \t]*");
std::regex endif_reg("^#[ \t]*endif[ \t]*");
std::regex simple_block_comment_begin_reg("^[ \t]*/\\*[ \t]*$");
std::regex simple_block_comment_end_reg("^[ \t]*\\*/[ \t]*$");
std::map<std::string, std::string> define_map;
class NullStream: public std::ostream {
class NullBuffer: public std::streambuf {
public:
int overflow(int c) { return c; }
} m_nb;
public:
NullStream():
std::ostream(&m_nb) {}
};
NullStream nullstream;
template<class char_t>
inline std::basic_string<char_t>& replace_all(std::basic_string<char_t>& a, const std::basic_string_view<char_t> b, const std::basic_string_view<char_t> c) {
auto i = a.find(b);
while(i != std::basic_string<char_t>::npos) {
a.replace(i, b.size(), c);
i = a.find(b, i + c.size());
}
return a;
};
template<class char_t, class T1, class T2>
inline std::basic_string<char_t>& replace_all(std::basic_string<char_t>& a, T1&& b, T2&& c) {
return replace_all(a, std::basic_string_view<char_t>(b), std::basic_string_view<char_t>(c));
};
namespace arg_info {
std::filesystem::path in_path;
std::filesystem::path in_path_dir;
std::filesystem::path out_path;
bool is_full_mode = false; //-f or --full : Even under folder handling each file is guaranteed to be included individually without error
bool open_help = false; //-h or --help : Display help
std::string relocate_path; //-r or --relocate : Relocate the input file path in "#line" to the specified path
bool skip_simple_block_comment = false; //-b or --skip-simple-block-comment : Skip simple block comment
bool relocate_path_was_an_url = false;
bool format_line_beginning = false; //-f or --format : Format the line beginning
bool using_std_out = false; //-s or --std-out : Output to standard output
} // namespace arg_info
//路径转义为合法的C++字符串
inline std::string path_to_string(std::filesystem::path path) {
std::string aret;
if(!arg_info::relocate_path.empty()) {
//get relative path of path to arg_info::in_path_dir
auto relative_path = std::filesystem::relative(path, arg_info::in_path_dir);
aret = arg_info::relocate_path + relative_path.string();
}
else
aret = path.string();
if(arg_info::relocate_path_was_an_url)
replace_all(aret, "\\", "/");
replace_all(aret, "//", "/");
replace_all(aret, "\\\\", "\\");
if(arg_info::relocate_path_was_an_url) {
replace_all(aret, ":/", "://");
replace_all(aret, " ", "%20");
}
return replace_all(aret, "\\", "\\\\");
}
void process_file(std::filesystem::path in_file, std::istream& in, std::ostream& out, std::string line_begin, std::filesystem::path include_path, std::filesystem::path root_path_for_skip) {
std::string line;
size_t line_num = 0;
//write #line
out << line_begin << "#line 1 \"" << path_to_string(in_file) << "\"" << std::endl;
//process file
while(std::getline(in, line)) {
line_num++;
//get line begin of this line
std::string line_begin_of_this_line;
auto update_line_begin = [&]() {
line_begin_of_this_line.clear();
if(arg_info::format_line_beginning)
line_begin_of_this_line = line_begin;
{
auto pos = line.find_first_not_of(" \t");
if(pos != std::string::npos) {
line_begin_of_this_line += line.substr(0, pos);
line = line.substr(pos);
}
}
};
update_line_begin();
std::smatch result;
//skip_simple_block_comment
if(arg_info::skip_simple_block_comment && std::regex_match(line, result, simple_block_comment_begin_reg)) {
//skip this line and all lines until */
re_inblock:
while(std::getline(in, line)) {
line_num++;
if(std::regex_match(line, result, simple_block_comment_end_reg)) {
while(std::getline(in, line)) {
line_num++;
if(std::regex_match(line, result, simple_block_comment_begin_reg))
goto re_inblock;
else if(!line.empty())
break;
}
break;
}
}
//write #line
out << line_begin_of_this_line << "#line " << line_num << " \"" << path_to_string(in_file) << "\"" << std::endl;
//update line_begin_of_this_line
update_line_begin();
}
//match include
if(std::regex_search(line, result, include_reg)) {
std::string file_name = result[1];
std::filesystem::path file_base_path = file_name;
std::filesystem::path file_path = include_path / file_base_path;
//get content after "#include"
std::string content_after_include = line.substr(result.position() + result.length());
//content_after_include must be empty or end with "//"
if(content_after_include.empty() || content_after_include.find("//") == 0) {
auto file_open_path = std::filesystem::absolute(file_path);
std::ifstream include_file(file_open_path);
if(include_file.is_open()) {
//if file_path's parent is root_path_for_skip, use NullStream
//not skip the processing of the contents of the file as it may have definitions.
if(!arg_info::is_full_mode && std::filesystem::equivalent(file_path.parent_path(), root_path_for_skip)) {
out << line_begin_of_this_line << "#include \"" << file_path.filename().string() << "\"" << content_after_include << std::endl;
process_file(file_path, include_file, nullstream, line_begin_of_this_line, file_path.parent_path(), root_path_for_skip);
}
else {
if(!content_after_include.empty()) //has comment
out << line_begin_of_this_line << content_after_include << std::endl;
process_file(file_path, include_file, out, line_begin_of_this_line, file_path.parent_path(), root_path_for_skip);
//write #line to reset
out << line_begin_of_this_line << "#line " << line_num << " \"" << path_to_string(in_file) << "\"" << std::endl;
}
include_file.close();
}
else {
out << line_begin_of_this_line << line << std::endl;
std::cerr << "can't open file: " << file_path << std::endl;
}
}
else {
out << line_begin_of_this_line << line << std::endl;
}
}
//match define
else if(std::regex_search(line, result, define_reg)) {
std::string define_name = result[1];
define_map[define_name] = "";
out << line_begin_of_this_line << line << std::endl;
}
//match define_with_value
else if(std::regex_search(line, result, define_with_value_reg)) {
std::string define_name = result[1];
std::string define_value = result[2];
//get content after "#define"
std::string content_after_define = line.substr(result.position() + result.length());
//content_after_define must be empty or end with "//"
if(content_after_define.empty() || content_after_define.find("//") == 0) {
define_map[define_name] = define_value;
}
out << line_begin_of_this_line << line << std::endl;
}
//match undef
else if(std::regex_search(line, result, undef_reg)) {
std::string define_name = result[1];
//get content after "#undef"
std::string content_after_undef = line.substr(result.position() + result.length());
//content_after_undef must be empty or end with "//"
if(content_after_undef.empty() || content_after_undef.find("//") == 0) {
define_map.erase(define_name);
}
out << line_begin_of_this_line << line << std::endl;
}
//match ifndef
else if(std::regex_search(line, result, ifndef_reg)) {
ifndef_reg_process:
std::string define_name = result[1];
//get content after "#ifndef"
std::string content_after_ifndef = line.substr(result.position() + result.length());
//content_after_ifndef must be empty or end with "//"
if(content_after_ifndef.empty() || content_after_ifndef.find("//") == 0) {
//if "skip define check" in content_after_ifndef, skip define check
if(content_after_ifndef.find("skip define check") != std::string::npos) {
out << line_begin_of_this_line << line << std::endl;
}
else if(define_map.find(define_name) != define_map.end()) {
//skip this line and all lines until #endif
while(std::getline(in, line)) {
line_num++;
if(std::regex_search(line, result, endif_reg)) {
//get content after "#endif"
std::string content_after_endif = line.substr(result.position() + result.length());
//content_after_endif must be empty or end with "//"
if(content_after_endif.empty() || content_after_endif.find("//") == 0) {
//write #line
out << line_begin_of_this_line << "#line " << line_num << " \"" << path_to_string(in_file) << "\"" << std::endl;
break;
}
}
}
}
else {
out << line_begin_of_this_line << line << std::endl;
}
}
else {
out << line_begin_of_this_line << line << std::endl;
}
}
//match ifndef_reg2
else if(std::regex_search(line, result, ifndef_reg2)) {
goto ifndef_reg_process;
}
else {
if(line.empty())
out << std::endl;
else
out << line_begin_of_this_line << line << std::endl;
}
}
}
void process_file(std::string in_file_name, std::string out_file_name, std::filesystem::path root_path_for_skip) {
if(!arg_info::using_std_out)
std::cout << "process file: " << in_file_name << std::endl;
std::ifstream in_file(in_file_name);
std::ofstream out_file;
std::ostream* out_stream = &out_file;
if(arg_info::using_std_out)
out_stream = &std::cout;
else
out_file.open(out_file_name, std::ios_base::binary);
std::filesystem::path include_path = std::filesystem::path(in_file_name).parent_path();
if(in_file.is_open() && (arg_info::using_std_out || out_file.is_open())) {
process_file(in_file_name, in_file, *out_stream, "", include_path, root_path_for_skip);
in_file.close();
if(!arg_info::using_std_out)
out_file.close();
}
else {
std::cerr << "can't open file" << std::endl;
}
if(!arg_info::using_std_out) {
for(auto& [key, value]: define_map) {
std::cout << "warning: define " << key << " is not undef" << std::endl;
}
std::cout << "process file: " << in_file_name << " done\n\n"
<< std::endl;
}
define_map.clear();
}
void print_help() {
std::cout << "Usage: Header_File_constructor [options] in_file out_file" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -f, --full" << std::endl;
std::cout << " Even under folder handling each file is guaranteed to be included individually without error" << std::endl;
std::cout << " -h, --help" << std::endl;
std::cout << " Display help" << std::endl;
std::cout << " -r, --relocate" << std::endl;
std::cout << " Relocate the input file path in \"#line\" to the specified path" << std::endl;
std::cout << " -b, --skip-simple-block-comment" << std::endl;
std::cout << " Skip simple block comment" << std::endl;
std::cout << " -f, --format" << std::endl;
std::cout << " Format the line beginning" << std::endl;
std::cout << " This will result in a better looking output file," << std::endl;
std::cout << " but the number of columns in the compilation warning will not match the source file." << std::endl;
std::cout << " -s, --std-out" << std::endl;
std::cout << " Output to standard output" << std::endl;
std::cout << "if in_file is a directory, out_file must be a directory or not exist," << std::endl;
std::cout << "and all superficial files in in_file will be processed." << std::endl;
}
int main(size_t argc, char* _argv[]) {
#if defined(_WIN32)
// Set console code page to UTF-8 so console known how to interpret string data
SetConsoleOutputCP(CP_UTF8);
// Enable buffering to prevent VS from chopping up UTF-8 byte sequences
setvbuf(stdout, nullptr, _IOFBF, 1000);
#endif
//build argv
std::vector<std::string> argv;
for(size_t i = 0; i < argc; i++) {
argv.push_back(_argv[i]);
}
//process argv
for(size_t i = 1; i < argv.size(); i++) {
std::string arg = argv[i];
if(arg == "-f" || arg == "--full") {
arg_info::is_full_mode = true;
}
else if(arg == "-h" || arg == "--help") {
arg_info::open_help = true;
}
else if(arg == "-f" || arg == "--format") {
arg_info::format_line_beginning = true;
}
else if(arg == "-s" || arg == "--std-out") {
arg_info::using_std_out = true;
}
else if(arg == "-r" || arg == "--relocate") {
if(i + 1 < argv.size()) {
arg_info::relocate_path = argv[i + 1];
//if arg_info::relocate_path is an url, add a "/" at the end and replace all "\" with "/"
if(arg_info::relocate_path.find("://") != std::string::npos) {
arg_info::relocate_path_was_an_url = true;
if(arg_info::relocate_path.back() != '/')
arg_info::relocate_path += '/';
}
i++;
}
else {
std::cerr << "error: -r or --relocate must be followed by a path" << std::endl;
return 1;
}
}
else if(arg == "-b" || arg == "--skip-simple-block-comment") {
arg_info::skip_simple_block_comment = true;
}
else {
if(arg_info::in_path.empty()) {
arg_info::in_path = arg;
}
else if(arg_info::out_path.empty()) {
arg_info::out_path = arg;
}
else {
std::cerr << "error: too many arguments" << std::endl;
return 1;
}
}
}
if(arg_info::out_path.empty())
arg_info::out_path = arg_info::in_path / ".out";
if(arg_info::open_help || arg_info::in_path.empty()) {
print_help();
return 0;
}
arg_info::in_path = std::filesystem::absolute(arg_info::in_path);
arg_info::out_path = std::filesystem::absolute(arg_info::out_path);
if(std::filesystem::is_regular_file(arg_info::in_path)) {
arg_info::is_full_mode = true;
if(!arg_info::using_std_out)
std::filesystem::create_directories(arg_info::out_path.parent_path());
arg_info::in_path_dir = arg_info::in_path.parent_path();
process_file(arg_info::in_path.string(), arg_info::out_path.string(), arg_info::in_path_dir);
}
else if(std::filesystem::is_directory(arg_info::in_path)) {
if(!arg_info::using_std_out)
std::filesystem::create_directories(arg_info::out_path);
arg_info::in_path_dir = arg_info::in_path;
//process just superficial files
for(auto& p: std::filesystem::directory_iterator(arg_info::in_path)) {
if(std::filesystem::is_regular_file(p)) {
std::filesystem::path in_file_path = p;
std::filesystem::path out_file_path = arg_info::out_path / in_file_path.filename();
process_file(in_file_path.string(), out_file_path.string(), arg_info::in_path_dir);
}
}
}
else {
std::cerr << "error: " << arg_info::in_path << " is not a file or directory" << std::endl;
return 1;
}
return 0;
}