2#ifndef _MDBX_CONTAINERS_PATH_UTILS_HPP_INCLUDED
3#define _MDBX_CONTAINERS_PATH_UTILS_HPP_INCLUDED
12#if __cplusplus >= 201703L
22#if __cplusplus < 202002L
37# if __cplusplus >= 201703L
38 namespace fs = std::filesystem;
41#if __cplusplus >= 202002L
43 inline std::string u8string_to_string(
const std::u8string& s) {
44 return std::string(s.begin(), s.end());
52 auto sw = [&](
const char* p){
return s.rfind(p, 0) == 0; };
53 return sw(
"./") || sw(
"../") || sw(
".\\") || sw(
"..\\");
60# if __cplusplus >= 201703L
61 return fs::u8path(path).is_absolute();
65 return (path.size() >= 2 && std::isalpha(path[0]) && path[1] ==
':') ||
66 (path.size() >= 2 && path[0] ==
'\\' && path[1] ==
'\\') ||
67 (path.size() >= 2 && path[0] ==
'/' && path[1] ==
'/');
70 return !path.empty() && path[0] ==
'/';
79# if __cplusplus >= 201703L
80# if __cplusplus >= 202002L
81 auto parent = fs::u8path(file_path).parent_path().u8string();
82 return u8string_to_string(parent);
84 return fs::u8path(file_path).parent_path().u8string();
87 size_t pos = file_path.find_last_of(
"/\\");
88 if (pos == std::string::npos)
90 return file_path.substr(0, pos);
98 std::vector<wchar_t> buffer(MAX_PATH);
99 HMODULE hModule = GetModuleHandle(NULL);
102 std::size_t size =
static_cast<std::size_t
>(GetModuleFileNameW(hModule, buffer.data(),
static_cast<DWORD
>(buffer.size())));
105 while (size == buffer.size() && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
106 buffer.resize(buffer.size() * 2);
107 size =
static_cast<std::size_t
>(GetModuleFileNameW(hModule, buffer.data(),
static_cast<DWORD
>(buffer.size())));
111 throw std::runtime_error(
"Failed to get executable path.");
114 std::wstring exe_path(buffer.begin(), buffer.begin() + size);
117 size_t pos = exe_path.find_last_of(L
"\\/");
118 if (pos != std::wstring::npos) {
119 exe_path = exe_path.substr(0, pos);
122# if __cplusplus >= 202002L
123 fs::path path_wide = exe_path;
124 auto tmp = path_wide.u8string();
125 return u8string_to_string(tmp);
128 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
129 return converter.to_bytes(exe_path);
133 char result[PATH_MAX];
134 ssize_t count = readlink(
"/proc/self/exe", result, PATH_MAX);
137 throw std::runtime_error(
"Failed to get executable path.");
140 std::string exe_path(result, count);
143 size_t pos = exe_path.find_last_of(
"\\/");
144 if (pos != std::string::npos) {
145 exe_path = exe_path.substr(0, pos);
156# if __cplusplus >= 201703L
157# if __cplusplus >= 202002L
158 auto name = fs::u8path(file_path).filename().u8string();
159 return u8string_to_string(name);
161 return fs::u8path(file_path).filename().u8string();
164 size_t pos = file_path.find_last_of(
"/\\");
165 if (pos == std::string::npos)
return file_path;
166 return file_path.substr(pos + 1);
175 int n_len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
176 if (n_len == 0)
return {};
178 std::wstring wide_string(n_len + 1, L
'\0');
179 MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, &wide_string[0], n_len);
181 n_len = WideCharToMultiByte(CP_ACP, 0, wide_string.c_str(), -1, NULL, 0, NULL, NULL);
182 if (n_len == 0)
return {};
184 std::string ansi_string(n_len - 1,
'\0');
185 WideCharToMultiByte(CP_ACP, 0, wide_string.c_str(), -1, &ansi_string[0], n_len, NULL, NULL);
192#if __cplusplus >= 201703L
198 inline std::string make_relative(
const std::string& file_path,
const std::string& base_path) {
199 if (base_path.empty())
return file_path;
200 fs::path fileP = fs::u8path(file_path);
201 fs::path baseP = fs::u8path(base_path);
203 fs::path relativeP = fs::relative(fileP, baseP, ec);
208# if __cplusplus >= 202002L
209 return u8string_to_string(relativeP.u8string());
211 return relativeP.u8string();
221# if __cplusplus >= 202002L
225 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
227 fs::path parent_dir = fs::path(wide_path);
232 if (parent_dir.empty()) parent_dir = fs::current_path();
233 if (!fs::exists(parent_dir)) {
235 if (!std::filesystem::create_directories(parent_dir, ec)) {
236# if __cplusplus >= 202002L
237 auto p = parent_dir.u8string();
238 throw std::runtime_error(
"Failed to create directories for path: " + u8string_to_string(p));
240 throw std::runtime_error(
"Failed to create directories for path: " + parent_dir.u8string());
253 return c ==
'/' || c ==
'\\';
261 const char SEP =
'\\';
263 const char SEP =
'/';
265 const char* s = in.c_str();
266 const size_t n = in.size();
273 std::vector<std::string> root_parts;
282 if (i > start) root_parts.push_back(in.substr(start, i - start));
287 if (i > start) root_parts.push_back(in.substr(start, i - start));
292 else if (n >= 2 && std::isalpha(
static_cast<unsigned char>(s[0])) && s[1] ==
':') {
293 drive.assign(in, 0, 2);
298 const bool is_abs_root = is_unc || (!drive.empty() && (n >= 3 &&
is_path_sep(s[2]))) ||
301 const bool is_abs_root = (n >= 1 && s[0] ==
'/');
302 if (is_abs_root) i = 1;
306 std::vector<std::string> comps;
308 auto push_component = [&](
const std::string& token) {
309 if (token.empty() || token ==
".")
return;
313 size_t protected_root = 0;
322 if (!comps.empty() && comps.back() !=
"..") {
327 comps.push_back(
"..");
330 comps.push_back(token);
336 size_t protected_count = 0;
338 for (
size_t k = 0; k < root_parts.size(); ++k) {
339 comps.push_back(root_parts[k]);
341 protected_count = comps.size();
351 std::string token = in.substr(start, i - start);
355 if (!comps.empty() && comps.size() > protected_count && comps.back() !=
"..") {
357 }
else if (!is_abs_root || (is_unc && comps.size() > protected_count)) {
358 comps.push_back(
"..");
362 if (!comps.empty() && comps.back() !=
"..") comps.pop_back();
364 comps.push_back(
"..");
367 }
else if (token !=
".") {
368 comps.push_back(token);
378 for (
size_t k = 0; k < comps.size(); ++k) {
384 if (!drive.empty()) {
386 bool drive_absolute = (n >= 3 && std::isalpha(
static_cast<unsigned char>(s[0])) && s[1]==
':' &&
is_path_sep(s[2]));
388 if (drive_absolute) out += SEP;
389 }
else if (is_abs_root) {
393 if (is_abs_root) out.push_back(SEP);
396 for (
size_t k = 0; k < comps.size(); ++k) {
397 if (!out.empty() && out.back() != SEP) out.push_back(SEP);
403 out = !drive.empty() ? drive : std::string(
".");
405 out = is_abs_root ? std::string(1, SEP) : std::string(
".");
424 size_t n = path.size();
427 if (n >= 1 && (path[0] ==
'/' || path[0] ==
'\\')) {
431 }
else if (n >= 2 && std::isalpha(path[0]) && path[1] ==
':') {
433 result.
root = path.substr(0, 2);
435 if (n >= 3 && (path[2] ==
'/' || path[2] ==
'\\')) {
444 while (i < n && (path[i] ==
'/' || path[i] ==
'\\')) {
449 while (j < n && path[j] !=
'/' && path[j] !=
'\\') {
453 result.
components.push_back(path.substr(i, j - i));
465 if (path.empty())
return;
468 size_t components_size = components.size();
471 std::string current_path = path_pc.
root;
472 for (
size_t i = 0; i < components_size; ++i) {
473 if (!current_path.empty() && current_path.back() !=
'/' && current_path.back() !=
'\\') {
476 current_path += components[i];
479 if (components[i] ==
".." ||
480 components[i] ==
"/" ||
481 components[i] ==
"~/")
continue;
485 int ret = mkdir(current_path.c_str(), 0755);
488 if (ret != 0 && errnum != EEXIST) {
489 throw std::runtime_error(
"Failed to create directory: " + current_path);
std::string get_parent_path(const std::string &file_path)
Extracts the parent directory from a full file path.
PathComponents split_path(const std::string &path)
Splits a path into its root and components.
bool is_explicitly_relative(const std::string &s) noexcept
Check if path starts with explicit relative prefix.
void create_directories(const std::string &path)
Creates directories recursively for the given path.
std::string get_exec_dir()
Retrieves the directory of the executable file.
bool is_path_sep(char c)
Check if character is a path separator.
bool is_absolute_path(const std::string &path)
Checks whether the given path is absolute (cross-platform).
std::string utf8_to_ansi(const std::string &utf8) noexcept
Converts a UTF-8 string to an ANSI string (Windows-specific).
std::string get_file_name(const std::string &file_path)
Extracts the file name from a full file path.
std::string lexically_normal_compat(const std::string &in)
Normalize path removing '.
Structure to hold the root and components of a path.
std::vector< std::string > components
The components of the path.
std::string root
The root part of the path (e.g., "/", "C:")