diff --git a/src/std_utils.cc b/src/std_utils.cc new file mode 100644 index 0000000..cb98e7d --- /dev/null +++ b/src/std_utils.cc @@ -0,0 +1,396 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#include "std_utils.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pEpLog.hh" + +#ifndef WIN32 + #include + #include + #include +#endif + +using namespace std; +using namespace pEp; + +namespace pEp { + namespace Utils { + bool is_c_str_empty(const char *str) + { + if (str == nullptr) { + return true; + } + string tmp{ str }; + if (tmp.empty()) { + return true; + } + return false; + } + + string nested_exception_to_string(const exception &e, int level, string src) + { + src += string(level, ' ') + "exception: " + e.what() + "\n"; + try { + rethrow_if_nested(e); + } catch (const exception &e) { + src = nested_exception_to_string(e, level + 1, src); + } catch (...) { + } + return src; + } + + // File utils + bool path_exists(const string &filename) + { + ifstream ifile(filename.c_str()); + return static_cast(ifile); + } + + void path_delete(const string &filename) + { + int status = remove(filename.c_str()); + if (status) { + runtime_error e{ string("path_delete(\"" + filename + "\") - " + strerror(errno)) }; + throw(e); + } + } + +#ifndef WIN32 + std::string dir_get_cwd() + { + std::string ret; + char cwd[2048]; + char const *res = getcwd(cwd, sizeof(cwd)); + if (res == nullptr) { + throw std::runtime_error( + "failed to get current working directory: " + std::string(strerror(errno))); + } + ret = std::string(res); + return ret; + } + + void dir_set_cwd(const std::string &dir) + { + int err = chdir(dir.c_str()); + if (err != 0) { + throw std::runtime_error("dir_set_cwd(" + dir + ")" + std::string(strerror(errno))); + } + } + + bool path_is_dir(const string &path) + { + bool ret = false; + struct stat statbuf; + if (stat(path.c_str(), &statbuf) != 0) { + runtime_error e{ "path_is_dir(\"" + path + "\") - " + strerror(errno) }; + throw(e); + } + if (S_ISDIR(statbuf.st_mode)) { + ret = true; + } + return ret; + } + + void path_delete_all(const string &path) + { + try { + if (!path_is_dir(path)) { + path_delete(path); + } else { + vector dirlist = dir_list_all(path); + if (dirlist.empty()) { + path_delete(path); + } else { + for (const string &filename : dirlist) { + string newpath = path + "/" + filename; + path_delete_all(newpath); + } + path_delete(path); + } + } + } catch (...) { + runtime_error e{ "path_delete_all(\"" + path + "\")" }; + throw_with_nested(e); + } + } + + std::string path_dirname(std::string const &f) + { + if (f.empty()) + return f; + if (f == "/") + return ""; + + auto len = f.size(); + // if the last character is / or \ ignore it + if (f[len - 1] == '/' || f[len - 1] == '\\') + --len; + while (len > 0) { + --len; + if (f[len] == '/' || f[len] == '\\') + break; + } + + if (f[len] == '/' || f[len] == '\\') + ++len; + return std::string(f.c_str(), len); + } + + std::string path_get_abs(const std::string &path) + { + std::string ret{ path }; + if (path[0] != '/') { + ret = dir_get_cwd() + "/" + path; + } + return ret; + } + +#endif + + ofstream file_create(const string &filename) + { + ofstream outfile{ filename }; + return outfile; + } + + std::string file_read(const std::string &filename) + { + auto ss = ostringstream{}; + ifstream input_file(filename); + if (!input_file.is_open()) { + runtime_error e{ "Could not open the file: " + filename }; + exit(EXIT_FAILURE); + } + ss << input_file.rdbuf(); + return ss.str(); + } + +#ifndef WIN32 + void path_ensure_not_existing(const string &path) + { + while (path_exists(path)) { + path_delete_all(path); + } + } + + + void dir_create(const string &dirname, const mode_t mode) + { + if (mkdir(dirname.c_str(), mode) != 0) { + runtime_error e{ string("dir_create(\"" + dirname + "\") - " + strerror(errno)) }; + throw(e); + } + } + + void dir_ensure(const std::string &path) + { + if (!Utils::path_exists(path)) { + Utils::dir_create(path); + } + } + + void dir_recreate(const std::string &path) + { + Utils::path_ensure_not_existing(path); + Utils::dir_ensure(path); + } + + vector dir_list_all(const std::string &path, const bool incl_dot_and_dotdot) + { + vector ret; + if (!path_exists(path)) { + runtime_error e{ "dir_list_all(\"" + path + "\") - Error: does not exist" }; + throw(e); + } + + if (!path_is_dir(path)) { + runtime_error e{ "dir_list_all(\"" + path + "\") - Error: is not a directory" }; + throw(e); + } + + DIR *dirp = opendir(path.c_str()); + if (dirp == nullptr) { + runtime_error e{ "dir_list_all(\"" + path + "\") - Error opening dir" }; + throw e; + } + + struct dirent *dp; + while ((dp = readdir(dirp)) != NULL) { + ret.push_back(string(dp->d_name)); + } + + if (!incl_dot_and_dotdot) { + ret.erase( + remove_if( + ret.begin(), + ret.end(), + [](string elem) { return (elem == "." || elem == ".."); }), + ret.end()); + } + + closedir(dirp); + return ret; + } + + vector dir_list_dirs(const string &dirname, const bool incl_dot_and_dotdot) + { + vector ret = dir_list_all(dirname, incl_dot_and_dotdot); + ret.erase( + remove_if( + ret.begin(), + ret.end(), + [dirname](string elem) { return !path_is_dir(dirname + "/" + elem); }), + ret.end()); + + return ret; + } + + vector dir_list_files(const string &dirname) + { + vector ret = dir_list_all(dirname); + ret.erase( + remove_if( + ret.begin(), + ret.end(), + [dirname](string elem) { return path_is_dir(dirname + "/" + elem); }), + ret.end()); + return ret; + } +#endif + + // Attention, it pads left... + string padTo(const string &str, const size_t num, const char paddingChar) + { + string ret{ str }; + if (num > ret.size()) { + ret.insert(0, num - ret.size(), paddingChar); + } + return ret; + } + + std::string clip(const std::string &str, const size_t len) + { + std::string ret{ str }; + if (str.length() > len) { + ret = str.substr(0, len) + "..."; + } + return ret; + } + + // prints the beginning and the end of the string and clips out + // content in the middle replaced by "... tldr ..." + std::string tldr(const std::string &str, const size_t len) + { + std::string decoration = "..."; + int trunclen = len - decoration.length(); + + std::string ret{ str }; + if (str.length() > len) { + ret = "\n" + str.substr(0, (int)floor(trunclen / 2.0)) + "\n... tldr ...\n"; + ret += str.substr(str.length() - (int)floor(trunclen / 2.0), str.length()); + } + return ret; + } + + std::string to_lower(const std::string &data) + { + std::string ret{ data }; + std::transform(ret.begin(), ret.end(), ret.begin(), [](unsigned char c) { + return std::tolower(c); + }); + return ret; + } + + string to_termcol(const Color &col) + { + switch (col) { + case Color::RESET: + return "\033[0m"; + case Color::BLACK: + return "\033[30m"; + case Color::RED: + return "\033[31m"; + case Color::GREEN: + return "\033[32m"; + case Color::YELLOW: + return "\033[33m"; + case Color::BLUE: + return "\033[34m"; + case Color::MAGENTA: + return "\033[35m"; + case Color::CYAN: + return "\033[36m"; + case Color::WHITE: + return "\033[37m"; + default: + return "\033[0m"; + } + } + + void sleep_millis(int milis) + { + std::chrono::milliseconds timespan(milis); + std::this_thread::sleep_for(timespan); + } + + // Random -------------------------------------------------------------------------------- + unsigned g_seed = gen_seed(); + std::mt19937 gen{ g_seed }; + + uint gen_seed() + { + uint ret{}; + if (std::random_device{}.entropy() > 0) { + ret = std::random_device{}(); + } else { + pEpLog("gen_seed(): No real random device present, falling back to epoch-time"); + ret = std::chrono::system_clock::now().time_since_epoch().count(); + } + assert(ret != 0); + return ret; + } + + unsigned int random_fast(int max) + { + g_seed = (214013 * g_seed + 2531011); + return ((g_seed >> 16) & 0x7FFF) % max + 1; + } + + unsigned char random_char(unsigned char min, unsigned char max) + { + std::uniform_int_distribution dis(min, max); + return dis(gen); + } + + std::string random_string(unsigned char min, unsigned char max, int len) + { + std::stringstream ret; + for (int i = 0; i < len; i++) { + ret << random_char(min, max); + } + return ret.str(); + } + + std::string random_string_fast(unsigned char min, unsigned char max, int len) + { + int range = std::max(max - min, 0); + std::stringstream ret; + for (int i = 0; i < len; i++) { + ret << static_cast(random_fast(range) + min); + } + return ret.str(); + } + } // namespace Utils +} // namespace pEp diff --git a/src/std_utils.hh b/src/std_utils.hh new file mode 100644 index 0000000..ff226b1 --- /dev/null +++ b/src/std_utils.hh @@ -0,0 +1,114 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef LIBPEPADAPTER_STD_UTILS_HH +#define LIBPEPADAPTER_STD_UTILS_HH + +#include +#include +#include + +namespace pEp { + namespace Utils { + // C-types helpers + bool is_c_str_empty(const char *str); + + // C++/STL data types to string + template + std::string to_string(const std::vector &v); + + // exception utils + std::string nested_exception_to_string( + const std::exception &e, + int level = 0, + std::string src = ""); + void print_exception(const std::exception &e, int level = 0); + + // File utils + // ---------- + // path (file & dir) + bool path_exists(const std::string &filename); + void path_delete(const std::string &filename); + +#ifndef WIN32 + std::string dir_get_cwd(); + void dir_set_cwd(const std::string& dir); + bool path_is_dir(const std::string &path); + void path_delete_all(const std::string &path); + std::string path_dirname(std::string const& f); + std::string path_get_abs(const std::string& path); +#endif + void path_ensure_not_existing(const std::string &path); + + // file + std::ofstream file_create(const std::string &filename); + std::string file_read(const std::string &filename); + + template + std::vector file_read_bin(const std::string& filename); + + template + void file_write_bin(const std::string& filename, std::vector& data); + + // dir +#ifndef WIN32 + void dir_create(const std::string &dirname, const mode_t mode = 0775); + void dir_ensure(const std::string &path); + void dir_recreate(const std::string &path); + + std::vector dir_list_all( + const std::string &path, + const bool incl_dot_and_dotdot = false); + + std::vector dir_list_dirs( + const std::string &dirname, + const bool incl_dot_and_dotdot = false); + + std::vector dir_list_files(const std::string &dirname); +#endif + + //String tools + std::string padTo(const std::string &str, const size_t num, const char paddingChar); + std::string clip(const std::string &str, const size_t len); + std::string tldr(const std::string &str, const size_t len); + std::string to_lower(const std::string& data); + + enum class Color + { + RESET, + BLACK, + RED, + GREEN, + YELLOW, + BLUE, + MAGENTA, + CYAN, + WHITE, + }; + + std::string to_termcol(const Color &col); + + + // Time + void sleep_millis(int milis); + + // Random + // Attention: calling gen_seed() often will exhaust your entropy pool. + // If no random devices present, will return epoch-time. + unsigned int gen_seed(); + unsigned int random_fast(int max); + unsigned char random_char(unsigned char min, unsigned char max); + std::string random_string(unsigned char min, unsigned char max, int len); + std::string random_string_fast(unsigned char min, unsigned char max, int len); + + // conversion + template + std::string bin2hex(const T &bin); + template + std::vector hex2bin(const std::string &hex_str); + } // namespace Utils +} // namespace pEp + +#include "std_utils.hxx" + +#endif // LIBPEPADAPTER_STD_UTILS_HH \ No newline at end of file diff --git a/src/std_utils.hxx b/src/std_utils.hxx new file mode 100644 index 0000000..de6779c --- /dev/null +++ b/src/std_utils.hxx @@ -0,0 +1,97 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef LIBPEPADAPTER_STD_UTILS_HXX +#define LIBPEPADAPTER_STD_UTILS_HXX + +#include +#include +#include +#include + +namespace pEp { + namespace Utils { + template + std::string to_string(const std::vector& v) + { + std::stringstream ss; + for (const T& elem : v) { + ss << elem << std::endl; + } + return ss.str(); + } + + template + std::vector file_read_bin(const std::string &filename) + { + std::vector ret{}; + if (pEp::Utils::path_exists(filename)) { + std::ifstream ifs(filename, std::ios_base::binary); + ifs.unsetf(std::ios_base::skipws); + + if (ifs.bad()) { + throw std::runtime_error("failed to read file: '" + filename + "'"); + } + ret = { std::istream_iterator(ifs), std::istream_iterator() }; + } else { + throw std::runtime_error("File does not exist: '" + filename + "'"); + } + return ret; + } + + template + void file_write_bin(const std::string &filename, std::vector &data) + { + std::fstream f(filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + f.write(data.data(), static_cast(data.size())); + if (f.bad()) { + throw std::runtime_error("failed to write file: '" + filename + "'"); + } + } + + template + std::string bin2hex(const T &bin) + { + std::string ret{}; + std::stringstream ss{}; + for (const auto &i : bin) { + ss << std::hex << std::setfill('0') << std::setw(2) << (int)i; + } + ret = ss.str(); + return ret; + } + + template + std::vector hex2bin(const std::string &hex_str) + { + std::vector ret{}; + if ((hex_str.size() % 2) != 0) { + throw std::runtime_error("hex2bin: Invalid hex string: must be even length"); + } + for (size_t i = 0; i < hex_str.size(); i += 2) { + std::ostringstream val_hex{}; + val_hex << hex_str.at(i); + val_hex << hex_str.at(i + 1); + + int val_int; + std::istringstream conv_ss{ val_hex.str() }; + conv_ss >> std::hex >> val_int; + if (conv_ss.fail()) { + throw std::runtime_error("hex2bin: invalid hex string" + hex_str); + } + ret.push_back(static_cast(val_int)); + } + return ret; + + // alternative way + // std::string extract; + // for (std::string::const_iterator pos = hex_str.begin(); pos < hex_str.end(); pos += 2) { + // extract.assign(pos, pos + 2); + // ret.push_back(std::stoi(extract, nullptr, 16)); + // } + // return ret; + } + + } // namespace Utils +} // namespace pEp +#endif // LIBPEPADAPTER_STD_UTILS_HXX