diff --git a/Documentation/developers.rst b/Documentation/developers.rst index 1938a8984..c6f071c1c 100644 --- a/Documentation/developers.rst +++ b/Documentation/developers.rst @@ -155,7 +155,6 @@ Two special format specifiers are available: Please note the the custom specifiers spec the data to be in network byte order while standard specifiers expects it to be in host by order. Current limitations: - - The whole call to `pcn_log` should be on the same line - Cannot be used inside a macro - Maximum 4 arguments are allowed diff --git a/src/polycubed/src/datapath_log.cpp b/src/polycubed/src/datapath_log.cpp index 8fb4b9fb5..a59b0e55d 100644 --- a/src/polycubed/src/datapath_log.cpp +++ b/src/polycubed/src/datapath_log.cpp @@ -199,20 +199,47 @@ void DatapathLog::stop() { } } +std::string DatapathLog::replace_string(std::string& subject, const std::string& search, const std::string& replace) { + size_t start_pos = 0; + while((start_pos = subject.find(search, start_pos)) != std::string::npos) { + subject.replace(start_pos, search.length(), replace); + start_pos += replace.length(); // Handles case where 'to' is a substring of 'from' + } + return subject; +} + // matches all C++ comments // https://stackoverflow.com/questions/36454069/how-to-remove-c-style-comments-from-code static std::regex regComments(R"***((?:\/\/(?:\\\n|[^\n])*)|(?:\/\*[\s\S]*?\*\/)|((?:R"([^(\\\s]{0,16})\([^)]*\)\2")|(?:@"[^"]*?")|(?:"(?:\?\?'|\\\\|\\"|\\\n|[^"])*?")|(?:'(?:\\\\|\\'|\\\n|[^'])*?')))***"); // matches all calls to pcn_log(ctx, ...) -static std::regex regN(R"***(pcn_log\(([\s\S]+?)\)\s*;)***"); +static std::regex regN(R"***(pcn_log\s*\(([\s\S]+?)\)\s*;)***"); +static std::regex regNewLine(R"***(/(\r\n)+|\r+|\n+|\t+/i)***"); +static std::regex regAddSpaces(R"***( +)***"); + +static std::regex regNPkt(R"***(pcn_pkt_log\s*\(([\s\S]+?)\)\s*;)***"); + +std::string DatapathLog::dp_callback(const std::smatch& m) { + std::string match = std::regex_replace(m.str(1), regNewLine, ""); + match = std::regex_replace(match, regAddSpaces, " "); + std::string new_string = std::string(REPLACE_BASE); + new_string = DatapathLog::replace_string(new_string, "$1", match); + return new_string; +} -static std::regex regNPkt(R"***(pcn_pkt_log\(([\s\S]+?)\)\s*;)***"); +std::string DatapathLog::dp_callback_pkt(const std::smatch& m) { + std::string match = std::regex_replace(m.str(1), regNewLine, ""); + match = std::regex_replace(match, regAddSpaces, " "); + std::string new_string = std::string(REPLACE_BASE_PKT); + new_string = DatapathLog::replace_string(new_string, "$1", match); + return new_string; +} std::string DatapathLog::parse_log(const std::string &code) { // remove all comments from the code before going on - auto code1 = regex_replace(code, regComments, "$1"); - auto code2 = std::regex_replace(code1, regN, REPLACE_BASE); - auto code3 = std::regex_replace(code2, regNPkt, REPLACE_BASE_PKT); + auto code1 = std::regex_replace(code, regComments, "$1"); + auto code2 = std::regex_replace_cb(code1, regN, DatapathLog::dp_callback); + auto code3 = std::regex_replace_cb(code2, regNPkt, DatapathLog::dp_callback_pkt); return BASE_CODE + code3; } diff --git a/src/polycubed/src/datapath_log.h b/src/polycubed/src/datapath_log.h index a67212091..dee55e566 100644 --- a/src/polycubed/src/datapath_log.h +++ b/src/polycubed/src/datapath_log.h @@ -19,6 +19,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -42,6 +47,9 @@ class DatapathLog { void stop(); static void call_back_proxy(void *cb_cookie, void *data, int data_size); + static std::string replace_string(std::string& subject, const std::string& search, const std::string& replace); + static std::string dp_callback(const std::smatch& m); + static std::string dp_callback_pkt(const std::smatch& m); // replaces all the log calls to the code that does it std::string parse_log(const std::string &code); @@ -60,3 +68,53 @@ class DatapathLog { } // namespace polycubed } // namespace polycube + +// Based on solution proposed in: https://stackoverflow.com/questions/22617209/regex-replace-with-callback-in-c11 +namespace std +{ + +template +std::basic_string regex_replace_cb(BidirIt first, BidirIt last, + const std::basic_regex& re, UnaryFunction f) +{ + std::basic_string s; + + typename std::match_results::difference_type + positionOfLastMatch = 0; + auto endOfLastMatch = first; + + auto callback = [&](const std::match_results& match) + { + auto positionOfThisMatch = match.position(0); + auto diff = positionOfThisMatch - positionOfLastMatch; + + auto startOfThisMatch = endOfLastMatch; + std::advance(startOfThisMatch, diff); + + s.append(endOfLastMatch, startOfThisMatch); + s.append(f(match)); + + auto lengthOfMatch = match.length(0); + + positionOfLastMatch = positionOfThisMatch + lengthOfMatch; + + endOfLastMatch = startOfThisMatch; + std::advance(endOfLastMatch, lengthOfMatch); + }; + + std::regex_iterator begin(first, last, re), end; + std::for_each(begin, end, callback); + + s.append(endOfLastMatch, last); + + return s; +} + +template +std::string regex_replace_cb(const std::string& s, + const std::basic_regex& re, UnaryFunction f) +{ + return regex_replace_cb(s.cbegin(), s.cend(), re, f); +} + +} // namespace std