1 #include <algorithm>
2 #include <array>
3 #include <cassert>
4 #include <fstream>
5 #include <iostream>
6 #include <memory>
7 #include <numeric>
8 #include <regex>
9 #include <string>
10 #include <vector>
11
escape_arg(const std::string & arg)12 std::string escape_arg(const std::string& arg) {
13 if (arg.empty() == false &&
14 arg.find_first_of(" \t\n\v\"") == arg.npos) {
15 return arg;
16 }
17
18 std::string escaped;
19 escaped.push_back('"');
20 for (auto it = arg.begin(); ; ++it) {
21 int num_backslashes = 0;
22
23 while (it != arg.end() && *it == '\\') {
24 ++it;
25 ++num_backslashes;
26 }
27
28 if (it == arg.end()) {
29 escaped.append(num_backslashes * 2, '\\');
30 break;
31 } else if (*it == '"') {
32 escaped.append((num_backslashes + 1) * 2, '\\');
33 escaped.push_back('"');
34 escaped.push_back(*it);
35 } else {
36 escaped.append(num_backslashes, '\\');
37 escaped.push_back(*it);
38 }
39 }
40 escaped.push_back('"');
41
42 return escaped;
43 }
44
45
create_empty_file(std::string const & path)46 void create_empty_file(std::string const& path) {
47 std::ofstream ofs(path);
48 ofs << '\n';
49 }
50
51 const std::string separator = "--sep--";
52 const std::string logfile_prefix = "--log-file=";
53
starts_with(std::string const & str,std::string const & pref)54 bool starts_with(std::string const& str, std::string const& pref) {
55 return str.find(pref) == 0;
56 }
57
parse_log_file_arg(std::string const & arg)58 int parse_log_file_arg(std::string const& arg) {
59 assert(starts_with(arg, logfile_prefix) && "Attempting to parse incorrect arg!");
60 auto fname = arg.substr(logfile_prefix.size());
61 create_empty_file(fname);
62 std::regex regex("MemoryChecker\\.(\\d+)\\.log", std::regex::icase);
63 std::smatch match;
64 if (std::regex_search(fname, match, regex)) {
65 return std::stoi(match[1]);
66 } else {
67 throw std::domain_error("Couldn't find desired expression in string: " + fname);
68 }
69 }
70
catch_path(std::string path)71 std::string catch_path(std::string path) {
72 auto start = path.find("catch");
73 // try capitalized instead
74 if (start == std::string::npos) {
75 start = path.find("Catch");
76 }
77 if (start == std::string::npos) {
78 throw std::domain_error("Couldn't find Catch's base path");
79 }
80 auto end = path.find_first_of("\\/", start);
81 return path.substr(0, end);
82 }
83
windowsify_path(std::string path)84 std::string windowsify_path(std::string path) {
85 for (auto& c : path) {
86 if (c == '/') {
87 c = '\\';
88 }
89 }
90 return path;
91 }
92
exec_cmd(std::string const & cmd,int log_num,std::string const & path)93 int exec_cmd(std::string const& cmd, int log_num, std::string const& path) {
94 std::array<char, 128> buffer;
95
96 // cmd has already been escaped outside this function.
97 auto real_cmd = "OpenCppCoverage --export_type binary:cov-report" + std::to_string(log_num)
98 + ".bin --quiet " + "--sources " + escape_arg(path) + " --cover_children -- " + cmd;
99 std::cout << "=== Marker ===: Cmd: " << real_cmd << '\n';
100 auto pipe = _popen(real_cmd.c_str(), "r");
101
102 if (!pipe) {
103 throw std::runtime_error("popen() failed!");
104 }
105 while (!feof(pipe)) {
106 if (fgets(buffer.data(), 128, pipe) != nullptr) {
107 std::cout << buffer.data();
108 }
109 }
110
111 auto ret = _pclose(pipe);
112 if (ret == -1) {
113 throw std::runtime_error("underlying error in pclose()");
114 }
115
116 return ret;
117 }
118
119 // argv should be:
120 // [0]: our path
121 // [1]: "--log-file=<path>"
122 // [2]: "--sep--"
123 // [3]+: the actual command
124
main(int argc,char ** argv)125 int main(int argc, char** argv) {
126 std::vector<std::string> args(argv, argv + argc);
127 auto sep = std::find(begin(args), end(args), separator);
128 assert(sep - begin(args) == 2 && "Structure differs from expected!");
129
130 auto num = parse_log_file_arg(args[1]);
131
132 auto cmdline = std::accumulate(++sep, end(args), std::string{}, [] (const std::string& lhs, const std::string& rhs) {
133 return lhs + ' ' + escape_arg(rhs);
134 });
135
136 try {
137 return exec_cmd(cmdline, num, windowsify_path(catch_path(args[0])));
138 } catch (std::exception const& ex) {
139 std::cerr << "Helper failed with: '" << ex.what() << "'\n";
140 return 12;
141 }
142 }
143