MDBX Containers
Loading...
Searching...
No Matches
path_utils.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _MDBX_CONTAINERS_PATH_UTILS_HPP_INCLUDED
3#define _MDBX_CONTAINERS_PATH_UTILS_HPP_INCLUDED
4
8
9#include <string>
10#if __cplusplus >= 201703L
11#include <filesystem>
12#else
13#include <vector>
14#include <cctype>
15#include <stdexcept>
16#endif
17
18#ifdef _WIN32
19// For Windows systems
20#include <direct.h>
21#include <windows.h>
22#if __cplusplus < 202002L
23#include <locale>
24#include <codecvt>
25#endif
26#else
27// For POSIX systems
28#include <unistd.h>
29#include <limits.h>
30#include <dirent.h>
31#include <sys/stat.h>
32#include <errno.h>
33#endif
34
35namespace mdbxc {
36# if __cplusplus >= 201703L
37 namespace fs = std::filesystem;
38# endif
39
43 inline bool is_absolute_path(const std::string& path) {
44# if __cplusplus >= 201703L
45 return fs::u8path(path).is_absolute();
46# else
47# ifdef _WIN32
48 // On Windows: absolute path starts with drive letter or UNC path (\\‍)
49 return (path.size() >= 2 && std::isalpha(path[0]) && path[1] == ':') ||
50 (path.size() >= 2 && path[0] == '\\' && path[1] == '\\') ||
51 (path.size() >= 2 && path[0] == '/' && path[1] == '/');
52# else
53 // On POSIX: absolute path starts with /
54 return !path.empty() && path[0] == '/';
55# endif
56# endif
57 }
58
62 inline std::string get_parent_path(const std::string& file_path) {
63# if __cplusplus >= 201703L
64 return fs::u8path(file_path).parent_path().u8string();
65# else
66 size_t pos = file_path.find_last_of("/\\");
67 if (pos == std::string::npos)
68 return "."; // current dir
69 return file_path.substr(0, pos);
70# endif
71 }
72
75 std::string get_exec_dir() {
76# ifdef _WIN32
77 std::vector<wchar_t> buffer(MAX_PATH);
78 HMODULE hModule = GetModuleHandle(NULL);
79
80 // Try to get the path
81 std::size_t size = static_cast<std::size_t>(GetModuleFileNameW(hModule, buffer.data(), static_cast<DWORD>(buffer.size())));
82
83 // If the path is too long, increase the buffer size
84 while (size == buffer.size() && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
85 buffer.resize(buffer.size() * 2); // Double the buffer size
86 size = static_cast<std::size_t>(GetModuleFileNameW(hModule, buffer.data(), static_cast<DWORD>(buffer.size())));
87 }
88
89 if (size == 0) {
90 throw std::runtime_error("Failed to get executable path.");
91 }
92
93 std::wstring exe_path(buffer.begin(), buffer.begin() + size);
94
95 // Trim the path to the directory (remove the file name, keep only the folder path)
96 size_t pos = exe_path.find_last_of(L"\\/");
97 if (pos != std::wstring::npos) {
98 exe_path = exe_path.substr(0, pos);
99 }
100
101# if __cplusplus >= 202002L
102 fs::path path_wide = exe_path;
103 return path_wide.u8string();
104# else
105 // Convert from std::wstring (UTF-16) to std::string (UTF-8)
106 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
107 return converter.to_bytes(exe_path);
108# endif
109
110# else
111 char result[PATH_MAX];
112 ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
113
114 if (count == -1) {
115 throw std::runtime_error("Failed to get executable path.");
116 }
117
118 std::string exe_path(result, count);
119
120 // Trim the path to the directory (remove the file name, keep only the folder path)
121 size_t pos = exe_path.find_last_of("\\/");
122 if (pos != std::string::npos) {
123 exe_path = exe_path.substr(0, pos);
124 }
125
126 return exe_path;
127# endif
128 }
129
133 std::string get_file_name(const std::string& file_path) {
134# if __cplusplus >= 201703L
135 return fs::u8path(file_path).filename().u8string();
136# else
137 size_t pos = file_path.find_last_of("/\\");
138 if (pos == std::string::npos) return file_path;
139 return file_path.substr(pos + 1);
140# endif
141 }
142
143#if __cplusplus >= 201703L
144
149 inline std::string make_relative(const std::string& file_path, const std::string& base_path) {
150 if (base_path.empty()) return file_path;
151 fs::path fileP = fs::u8path(file_path);
152 fs::path baseP = fs::u8path(base_path);
153 std::error_code ec; // For exception-safe operation
154 fs::path relativeP = fs::relative(fileP, baseP, ec);
155 if (ec) {
156 // If there is an error, return the original file_path
157 return file_path;
158 } else {
159 return relativeP.u8string();
160 }
161 }
162
166 void create_directories(const std::string& path) {
167# ifdef _WIN32
168# if __cplusplus >= 202002L
169 fs::path parent_dir = fs::u8path(get_parent_path(path));
170# else
171 // Convert UTF-8 string to wide string for Windows
172 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
173 std::wstring wide_path = converter.from_bytes(get_parent_path(path));
174 fs::path parent_dir = fs::path(wide_path);
175# endif
176# else
177 fs::path parent_dir = fs::u8path(get_parent_path(path));
178# endif
179 if (parent_dir.empty()) parent_dir = fs::current_path();
180 if (!fs::exists(parent_dir)) {
181 std::error_code ec;
182 if (!std::filesystem::create_directories(parent_dir, ec)) {
183 throw std::runtime_error("Failed to create directories for path: " + parent_dir.u8string());
184 }
185 }
186 }
187
188#else
189
193 std::string root;
194 std::vector<std::string> components;
195 };
196
200 PathComponents split_path(const std::string& path) {
201 PathComponents result;
202 size_t i = 0;
203 size_t n = path.size();
204
205 // Handle root paths for Unix and Windows
206 if (n >= 1 && (path[0] == '/' || path[0] == '\\')) {
207 // Unix root "/"
208 result.root = "/";
209 ++i;
210 } else if (n >= 2 && std::isalpha(path[0]) && path[1] == ':') {
211 // Windows drive letter "C:"
212 result.root = path.substr(0, 2);
213 i = 2;
214 if (n >= 3 && (path[2] == '/' || path[2] == '\\')) {
215 // "C:/"
216 ++i;
217 }
218 }
219
220 // Split the path into components
221 while (i < n) {
222 // Skip path separators
223 while (i < n && (path[i] == '/' || path[i] == '\\')) {
224 ++i;
225 }
226 // Find the next separator
227 size_t j = i;
228 while (j < n && path[j] != '/' && path[j] != '\\') {
229 ++j;
230 }
231 if (i < j) {
232 result.components.push_back(path.substr(i, j - i));
233 i = j;
234 }
235 }
236
237 return result;
238 }
239
243 std::string utf8_to_ansi(const std::string& utf8) noexcept {
244 int n_len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
245 if (n_len == 0) return {};
246
247 std::wstring wide_string(n_len + 1, L'\0');
248 MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, &wide_string[0], n_len);
249
250 n_len = WideCharToMultiByte(CP_ACP, 0, wide_string.c_str(), -1, NULL, 0, NULL, NULL);
251 if (n_len == 0) return {};
252
253 std::string ansi_string(n_len - 1, '\0');
254 WideCharToMultiByte(CP_ACP, 0, wide_string.c_str(), -1, &ansi_string[0], n_len, NULL, NULL);
255 return ansi_string;
256 }
257
261 void create_directories(const std::string& path) {
262 if (path.empty()) return;
264 auto &components = path_pc.components;
265 size_t components_size = components.size();
266
267 // Build the path incrementally and create directories
268 std::string current_path = path_pc.root;
269 for (size_t i = 0; i < components_size; ++i) {
270 if (!current_path.empty() && current_path.back() != '/' && current_path.back() != '\\') {
271 current_path += '/';
272 }
273 current_path += components[i];
274
275 // Skip special components
276 if (components[i] == ".." ||
277 components[i] == "/" ||
278 components[i] == "~/") continue;
279# ifdef _WIN32
280 int ret = _mkdir(utf8_to_ansi(current_path).c_str());
281# else
282 int ret = mkdir(current_path.c_str(), 0755);
283# endif
284 int errnum = errno;
285 if (ret != 0 && errnum != EEXIST) {
286 throw std::runtime_error("Failed to create directory: " + current_path);
287 }
288 }
289 }
290
291#endif // __cplusplus >= 201703L
292
293}; // namespace mdbxc
294
295#endif // _MDBX_CONTAINERS_PATH_UTILS_HPP_INCLUDED
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.
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_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.
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:")