#include "Protocol.h" #include #include #include namespace protocol { namespace { std::mutex writing; std::string escape(const std::string& field) { std::string escaped; for (char c : field) { switch (c) { case '\\': escaped += "\\\\"; break; case '\t': escaped += "\\t"; break; case '\r': escaped += "\\r"; break; case '\n': escaped += "\\n"; break; default: escaped += c; } } return escaped; } std::string unescape(const std::string& field) { std::string plain; for (size_t i = 0; i < field.size(); i++) { if (field[i] != '\\' || i + 1 == field.size()) { plain += field[i]; continue; } switch (field[++i]) { case 't': plain += '\t'; break; case 'r': plain += '\r'; break; case 'n': plain += '\n'; break; default: plain += field[i]; } } return plain; } } // namespace std::string Line::field(size_t index) const { return index < fields.size() ? fields[index] : std::string(); } long Line::number(size_t index) const { return std::strtol(field(index).c_str(), nullptr, 10); } Line read(const std::string& text) { Line line; size_t start = 0; while (true) { size_t tab = text.find('\t', start); std::string part = text.substr(start, tab == std::string::npos ? tab : tab - start); if (start == 0) { line.verb = part; } else { line.fields.push_back(unescape(part)); } if (tab == std::string::npos) { return line; } start = tab + 1; } } void write(const std::string& verb, const std::vector& fields) { std::string line = verb; for (const std::string& field : fields) { line += '\t' + escape(field); } line += '\n'; std::lock_guard held(writing); std::fwrite(line.data(), 1, line.size(), stdout); std::fflush(stdout); } void write(const std::string& verb, long value) { write(verb, {std::to_string(value)}); } } // namespace protocol