C++11 library providing functionality common to all adapters.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

396 lines
12 KiB

  1. // This file is under GNU General Public License 3.0
  2. // see LICENSE.txt
  3. #include "std_utils.hh"
  4. #include <iostream>
  5. #include <iterator>
  6. #include <fstream>
  7. #include <cstdio>
  8. #include <cerrno>
  9. #include <cmath>
  10. #include <algorithm>
  11. #include <thread>
  12. #include <random>
  13. #include <cassert>
  14. #include <cstring>
  15. #include <iomanip>
  16. #include "pEpLog.hh"
  17. #ifndef WIN32
  18. #include <dirent.h>
  19. #include <sys/stat.h>
  20. #include <unistd.h>
  21. #endif
  22. using namespace std;
  23. using namespace pEp;
  24. namespace pEp {
  25. namespace Utils {
  26. bool is_c_str_empty(const char *str)
  27. {
  28. if (str == nullptr) {
  29. return true;
  30. }
  31. string tmp{ str };
  32. if (tmp.empty()) {
  33. return true;
  34. }
  35. return false;
  36. }
  37. string nested_exception_to_string(const exception &e, int level, string src)
  38. {
  39. src += string(level, ' ') + "exception: " + e.what() + "\n";
  40. try {
  41. rethrow_if_nested(e);
  42. } catch (const exception &e) {
  43. src = nested_exception_to_string(e, level + 1, src);
  44. } catch (...) {
  45. }
  46. return src;
  47. }
  48. // File utils
  49. bool path_exists(const string &filename)
  50. {
  51. ifstream ifile(filename.c_str());
  52. return static_cast<bool>(ifile);
  53. }
  54. void path_delete(const string &filename)
  55. {
  56. int status = remove(filename.c_str());
  57. if (status) {
  58. runtime_error e{ string("path_delete(\"" + filename + "\") - " + strerror(errno)) };
  59. throw(e);
  60. }
  61. }
  62. #ifndef WIN32
  63. std::string dir_get_cwd()
  64. {
  65. std::string ret;
  66. char cwd[2048];
  67. char const *res = getcwd(cwd, sizeof(cwd));
  68. if (res == nullptr) {
  69. throw std::runtime_error(
  70. "failed to get current working directory: " + std::string(strerror(errno)));
  71. }
  72. ret = std::string(res);
  73. return ret;
  74. }
  75. void dir_set_cwd(const std::string &dir)
  76. {
  77. int err = chdir(dir.c_str());
  78. if (err != 0) {
  79. throw std::runtime_error("dir_set_cwd(" + dir + ")" + std::string(strerror(errno)));
  80. }
  81. }
  82. bool path_is_dir(const string &path)
  83. {
  84. bool ret = false;
  85. struct stat statbuf;
  86. if (stat(path.c_str(), &statbuf) != 0) {
  87. runtime_error e{ "path_is_dir(\"" + path + "\") - " + strerror(errno) };
  88. throw(e);
  89. }
  90. if (S_ISDIR(statbuf.st_mode)) {
  91. ret = true;
  92. }
  93. return ret;
  94. }
  95. void path_delete_all(const string &path)
  96. {
  97. try {
  98. if (!path_is_dir(path)) {
  99. path_delete(path);
  100. } else {
  101. vector<string> dirlist = dir_list_all(path);
  102. if (dirlist.empty()) {
  103. path_delete(path);
  104. } else {
  105. for (const string &filename : dirlist) {
  106. string newpath = path + "/" + filename;
  107. path_delete_all(newpath);
  108. }
  109. path_delete(path);
  110. }
  111. }
  112. } catch (...) {
  113. runtime_error e{ "path_delete_all(\"" + path + "\")" };
  114. throw_with_nested(e);
  115. }
  116. }
  117. std::string path_dirname(std::string const &f)
  118. {
  119. if (f.empty())
  120. return f;
  121. if (f == "/")
  122. return "";
  123. auto len = f.size();
  124. // if the last character is / or \ ignore it
  125. if (f[len - 1] == '/' || f[len - 1] == '\\')
  126. --len;
  127. while (len > 0) {
  128. --len;
  129. if (f[len] == '/' || f[len] == '\\')
  130. break;
  131. }
  132. if (f[len] == '/' || f[len] == '\\')
  133. ++len;
  134. return std::string(f.c_str(), len);
  135. }
  136. std::string path_get_abs(const std::string &path)
  137. {
  138. std::string ret{ path };
  139. if (path[0] != '/') {
  140. ret = dir_get_cwd() + "/" + path;
  141. }
  142. return ret;
  143. }
  144. #endif
  145. ofstream file_create(const string &filename)
  146. {
  147. ofstream outfile{ filename };
  148. return outfile;
  149. }
  150. std::string file_read(const std::string &filename)
  151. {
  152. auto ss = ostringstream{};
  153. ifstream input_file(filename);
  154. if (!input_file.is_open()) {
  155. runtime_error e{ "Could not open the file: " + filename };
  156. exit(EXIT_FAILURE);
  157. }
  158. ss << input_file.rdbuf();
  159. return ss.str();
  160. }
  161. #ifndef WIN32
  162. void path_ensure_not_existing(const string &path)
  163. {
  164. while (path_exists(path)) {
  165. path_delete_all(path);
  166. }
  167. }
  168. void dir_create(const string &dirname, const mode_t mode)
  169. {
  170. if (mkdir(dirname.c_str(), mode) != 0) {
  171. runtime_error e{ string("dir_create(\"" + dirname + "\") - " + strerror(errno)) };
  172. throw(e);
  173. }
  174. }
  175. void dir_ensure(const std::string &path)
  176. {
  177. if (!Utils::path_exists(path)) {
  178. Utils::dir_create(path);
  179. }
  180. }
  181. void dir_recreate(const std::string &path)
  182. {
  183. Utils::path_ensure_not_existing(path);
  184. Utils::dir_ensure(path);
  185. }
  186. vector<string> dir_list_all(const std::string &path, const bool incl_dot_and_dotdot)
  187. {
  188. vector<string> ret;
  189. if (!path_exists(path)) {
  190. runtime_error e{ "dir_list_all(\"" + path + "\") - Error: does not exist" };
  191. throw(e);
  192. }
  193. if (!path_is_dir(path)) {
  194. runtime_error e{ "dir_list_all(\"" + path + "\") - Error: is not a directory" };
  195. throw(e);
  196. }
  197. DIR *dirp = opendir(path.c_str());
  198. if (dirp == nullptr) {
  199. runtime_error e{ "dir_list_all(\"" + path + "\") - Error opening dir" };
  200. throw e;
  201. }
  202. struct dirent *dp;
  203. while ((dp = readdir(dirp)) != NULL) {
  204. ret.push_back(string(dp->d_name));
  205. }
  206. if (!incl_dot_and_dotdot) {
  207. ret.erase(
  208. remove_if(
  209. ret.begin(),
  210. ret.end(),
  211. [](string elem) { return (elem == "." || elem == ".."); }),
  212. ret.end());
  213. }
  214. closedir(dirp);
  215. return ret;
  216. }
  217. vector<string> dir_list_dirs(const string &dirname, const bool incl_dot_and_dotdot)
  218. {
  219. vector<string> ret = dir_list_all(dirname, incl_dot_and_dotdot);
  220. ret.erase(
  221. remove_if(
  222. ret.begin(),
  223. ret.end(),
  224. [dirname](string elem) { return !path_is_dir(dirname + "/" + elem); }),
  225. ret.end());
  226. return ret;
  227. }
  228. vector<string> dir_list_files(const string &dirname)
  229. {
  230. vector<string> ret = dir_list_all(dirname);
  231. ret.erase(
  232. remove_if(
  233. ret.begin(),
  234. ret.end(),
  235. [dirname](string elem) { return path_is_dir(dirname + "/" + elem); }),
  236. ret.end());
  237. return ret;
  238. }
  239. #endif
  240. // Attention, it pads left...
  241. string padTo(const string &str, const size_t num, const char paddingChar)
  242. {
  243. string ret{ str };
  244. if (num > ret.size()) {
  245. ret.insert(0, num - ret.size(), paddingChar);
  246. }
  247. return ret;
  248. }
  249. std::string clip(const std::string &str, const size_t len)
  250. {
  251. std::string ret{ str };
  252. if (str.length() > len) {
  253. ret = str.substr(0, len) + "...";
  254. }
  255. return ret;
  256. }
  257. // prints the beginning and the end of the string and clips out
  258. // content in the middle replaced by "... tldr ..."
  259. std::string tldr(const std::string &str, const size_t len)
  260. {
  261. std::string decoration = "...";
  262. int trunclen = len - decoration.length();
  263. std::string ret{ str };
  264. if (str.length() > len) {
  265. ret = "\n" + str.substr(0, (int)floor(trunclen / 2.0)) + "\n... tldr ...\n";
  266. ret += str.substr(str.length() - (int)floor(trunclen / 2.0), str.length());
  267. }
  268. return ret;
  269. }
  270. std::string to_lower(const std::string &data)
  271. {
  272. std::string ret{ data };
  273. std::transform(ret.begin(), ret.end(), ret.begin(), [](unsigned char c) {
  274. return std::tolower(c);
  275. });
  276. return ret;
  277. }
  278. string to_termcol(const Color &col)
  279. {
  280. switch (col) {
  281. case Color::RESET:
  282. return "\033[0m";
  283. case Color::BLACK:
  284. return "\033[30m";
  285. case Color::RED:
  286. return "\033[31m";
  287. case Color::GREEN:
  288. return "\033[32m";
  289. case Color::YELLOW:
  290. return "\033[33m";
  291. case Color::BLUE:
  292. return "\033[34m";
  293. case Color::MAGENTA:
  294. return "\033[35m";
  295. case Color::CYAN:
  296. return "\033[36m";
  297. case Color::WHITE:
  298. return "\033[37m";
  299. default:
  300. return "\033[0m";
  301. }
  302. }
  303. void sleep_millis(int milis)
  304. {
  305. std::chrono::milliseconds timespan(milis);
  306. std::this_thread::sleep_for(timespan);
  307. }
  308. // Random --------------------------------------------------------------------------------
  309. unsigned g_seed = gen_seed();
  310. std::mt19937 gen{ g_seed };
  311. uint gen_seed()
  312. {
  313. uint ret{};
  314. if (std::random_device{}.entropy() > 0) {
  315. ret = std::random_device{}();
  316. } else {
  317. pEpLog("gen_seed(): No real random device present, falling back to epoch-time");
  318. ret = std::chrono::system_clock::now().time_since_epoch().count();
  319. }
  320. assert(ret != 0);
  321. return ret;
  322. }
  323. unsigned int random_fast(int max)
  324. {
  325. g_seed = (214013 * g_seed + 2531011);
  326. return ((g_seed >> 16) & 0x7FFF) % max + 1;
  327. }
  328. unsigned char random_char(unsigned char min, unsigned char max)
  329. {
  330. std::uniform_int_distribution<unsigned char> dis(min, max);
  331. return dis(gen);
  332. }
  333. std::string random_string(unsigned char min, unsigned char max, int len)
  334. {
  335. std::stringstream ret;
  336. for (int i = 0; i < len; i++) {
  337. ret << random_char(min, max);
  338. }
  339. return ret.str();
  340. }
  341. std::string random_string_fast(unsigned char min, unsigned char max, int len)
  342. {
  343. int range = std::max(max - min, 0);
  344. std::stringstream ret;
  345. for (int i = 0; i < len; i++) {
  346. ret << static_cast<unsigned char>(random_fast(range) + min);
  347. }
  348. return ret.str();
  349. }
  350. } // namespace Utils
  351. } // namespace pEp