Mercurial > irccd
diff common/util.cpp @ 0:1158cffe5a5e
Initial import
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 08 Feb 2016 16:43:14 +0100 |
parents | |
children | 03068f5ed79d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/util.cpp Mon Feb 08 16:43:14 2016 +0100 @@ -0,0 +1,357 @@ +/* + * util.cpp -- some utilities + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> +#include <cctype> +#include <cstdlib> +#include <ctime> +#include <iomanip> +#include <sstream> +#include <stdexcept> + +#include <irccd-config.h> + +#include "util.h" +#include "unicode.h" + +using namespace std::string_literals; + +namespace irccd { + +namespace util { + +namespace { + +const std::unordered_map<std::string, int> colorTable{ + { "white", 0 }, + { "black", 1 }, + { "blue", 2 }, + { "green", 3 }, + { "red", 4 }, + { "brown", 5 }, + { "purple", 6 }, + { "orange", 7 }, + { "yellow", 8 }, + { "lightgreen", 9 }, + { "cyan", 10 }, + { "lightcyan", 11 }, + { "lightblue", 12 }, + { "pink", 13 }, + { "grey", 14 }, + { "lightgrey", 15 } +}; + +const std::unordered_map<std::string, char> attributesTable{ + { "bold", '\x02' }, + { "italic", '\x09' }, + { "strike", '\x13' }, + { "reset", '\x0f' }, + { "underline", '\x15' }, + { "underline2", '\x1f' }, + { "reverse", '\x16' } +}; + +std::string substituteDate(const std::string &text, const Substitution ¶ms) +{ + std::ostringstream oss; + +#if defined(HAVE_STD_PUT_TIME) + oss << std::put_time(std::localtime(¶ms.time), text.c_str()); +#else + /* + * Quick and dirty hack because GCC does not have this function. + */ + char buffer[4096]; + + std::strftime(buffer, sizeof (buffer) - 1, text.c_str(), std::localtime(¶ms.time)); + + oss << buffer; +#endif + + return oss.str(); +} + +std::string substituteKeywords(const std::string &content, const Substitution ¶ms) +{ + auto value = params.keywords.find(content); + + if (value != params.keywords.end()) + return value->second; + + return ""; +} + +std::string substituteEnv(const std::string &content) +{ + auto value = std::getenv(content.c_str()); + + if (value != nullptr) + return value; + + return ""; +} + +std::string substituteAttributes(const std::string &content) +{ + std::stringstream oss; + std::vector<std::string> list = split(content, ","); + + /* @{} means reset */ + if (list.empty()) { + oss << attributesTable.at("reset"); + } else { + /* Remove useless spaces */ + for (auto &a : list) + a = strip(a); + + /* + * 0: foreground + * 1: background + * 2-n: attributes + */ + auto foreground = list[0]; + if (!foreground.empty() || list.size() >= 2) { + /* Color sequence */ + oss << '\x03'; + + /* Foreground */ + auto it = colorTable.find(foreground); + if (it != colorTable.end()) + oss << it->second; + + /* Background */ + if (list.size() >= 2 && (it = colorTable.find(list[1])) != colorTable.end()) + oss << "," << it->second; + + /* Attributes */ + for (std::size_t i = 2; i < list.size(); ++i) { + auto attribute = attributesTable.find(list[i]); + + if (attribute != attributesTable.end()) + oss << attribute->second; + } + } + } + + return oss.str(); +} + +std::string substitute(std::string::const_iterator &it, std::string::const_iterator &end, char token, const Substitution ¶ms) +{ + std::string content, value; + + if (it == end) + return ""; + + while (it != end && *it != '}') + content += *it++; + + if (*it != '}' || it == end) + throw std::invalid_argument("unclosed "s + token + " construct"s); + + it++; + + switch (token) { + case '#': + value = substituteKeywords(content, params); + break; + case '$': + value = substituteEnv(content); + break; + case '@': + value = substituteAttributes(content); + break; + default: + throw std::invalid_argument("unknown "s + token + " construct"); + } + + return value; +} + +} // !namespace + +std::string format(std::string text, const Substitution ¶ms) +{ + std::ostringstream oss; + + /* + * Change the date format before anything else to avoid interpolation with keywords and + * user input. + */ + text = substituteDate(text, params); + + std::string::const_iterator it = text.begin(); + std::string::const_iterator end = text.end(); + + while (it != end) { + auto token = *it; + + if (token == '#' || token == '@' || token == '$') { + ++ it; + + if (it == end) { + oss << token; + continue; + } + + if (*it == '{') { + /* Do we have a variable? */ + oss << substitute(++it, end, token, params); + } else if (*it == token) { + /* Need one for sure */ + oss << token; + + /* Do we have a double token followed by a { for escaping? */ + if (++it == end) + continue; + + if (*it != '{') + oss << token; + } else { + oss << *it++; + } + } else { + oss << *it++; + } + } + + return oss.str(); +} + +std::string strip(std::string str) +{ + auto test = [] (char c) { return !std::isspace(c); }; + + str.erase(str.begin(), std::find_if(str.begin(), str.end(), test)); + str.erase(std::find_if(str.rbegin(), str.rend(), test).base(), str.end()); + + return str; +} + +std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max) +{ + std::vector<std::string> result; + size_t next = -1, current; + int count = 1; + bool finished = false; + + if (list.empty()) + return result; + + do { + std::string val; + + current = next + 1; + next = list.find_first_of(delimiters, current); + + // split max, get until the end + if (max >= 0 && count++ >= max) { + val = list.substr(current, std::string::npos); + finished = true; + } else { + val = list.substr(current, next - current); + finished = next == std::string::npos; + } + + result.push_back(val); + } while (!finished); + + return result; +} + +MessagePair parseMessage(std::string message, const std::string &cc, const std::string &name) +{ + std::string result = message; + bool iscommand = false; + + // handle special commands "!<plugin> command" + if (cc.length() > 0) { + auto pos = result.find_first_of(" \t"); + auto fullcommand = cc + name; + + /* + * If the message that comes is "!foo" without spaces we + * compare the command char + the plugin name. If there + * is a space, we check until we find a space, if not + * typing "!foo123123" will trigger foo plugin. + */ + if (pos == std::string::npos) + iscommand = result == fullcommand; + else + iscommand = result.length() >= fullcommand.length() && result.compare(0, pos, fullcommand) == 0; + + if (iscommand) { + /* + * If no space is found we just set the message to "" otherwise + * the plugin name will be passed through onCommand + */ + if (pos == std::string::npos) + result = ""; + else + result = message.substr(pos + 1); + } + } + + return MessagePair{result, ((iscommand) ? MessageType::Command : MessageType::Message)}; +} + +bool isBoolean(std::string value) noexcept +{ + return (value = unicode::toupper(value)) == "1" || value == "YES" || value == "TRUE" || value == "ON"; +} + +bool isInt(const std::string &str, int base) noexcept +{ + if (str.empty()) + return false; + + char *ptr; + + std::strtol(str.c_str(), &ptr, base); + + return *ptr == 0; +} + +bool isReal(const std::string &str) noexcept +{ + if (str.empty()) + return false; + + char *ptr; + + std::strtod(str.c_str(), &ptr); + + return *ptr == 0; +} + +std::string nextNetwork(std::string &input) +{ + std::string result; + std::string::size_type pos = input.find("\r\n\r\n"); + + if ((pos = input.find("\r\n\r\n")) != std::string::npos) { + result = input.substr(0, pos); + input.erase(input.begin(), input.begin() + pos + 4); + } + + return result; +} + +} // util + +} // !irccd