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.

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