LogIt++
Loading...
Searching...
No Matches
path_utils.hpp
Go to the documentation of this file.
1#ifndef _LOGIT_PATH_UTILS_HPP_INCLUDED
2#define _LOGIT_PATH_UTILS_HPP_INCLUDED
5
6#include <string>
7#if __cplusplus >= 201703L
8#include <filesystem>
9#else
10#include <vector>
11#include <cctype>
12#include <stdexcept>
13#endif
14
15#ifdef _WIN32
16// For Windows systems
17#include <direct.h>
18#include <windows.h>
19#include <locale>
20#include <codecvt>
21#else
22// For POSIX systems
23#include <unistd.h>
24#include <limits.h>
25#include <dirent.h>
26#include <sys/stat.h>
27#include <errno.h>
28#endif
29
30namespace logit {
31# if __cplusplus >= 201703L
32 namespace fs = std::filesystem;
33# endif
34
37 std::string get_exe_path() {
38# ifdef _WIN32
39 std::vector<wchar_t> buffer(MAX_PATH);
40 HMODULE hModule = GetModuleHandle(NULL);
41
42 // Пробуем получить путь
43 DWORD size = GetModuleFileNameW(hModule, buffer.data(), buffer.size());
44
45 // Если путь слишком длинный, увеличиваем буфер
46 while (size == buffer.size() && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
47 buffer.resize(buffer.size() * 2); // Увеличиваем буфер в два раза
48 size = GetModuleFileNameW(hModule, buffer.data(), buffer.size());
49 }
50
51 if (size == 0) {
52 throw std::runtime_error("Failed to get executable path.");
53 }
54
55 std::wstring exe_path(buffer.begin(), buffer.begin() + size);
56
57 // Обрезаем путь до директории (удаляем имя файла, оставляем только путь к папке)
58 size_t pos = exe_path.find_last_of(L"\\/");
59 if (pos != std::wstring::npos) {
60 exe_path = exe_path.substr(0, pos);
61 }
62
63 // Преобразуем из std::wstring (UTF-16) в std::string (UTF-8)
64 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
65 return converter.to_bytes(exe_path);
66# else
67 char result[PATH_MAX];
68 ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
69
70 if (count == -1) {
71 throw std::runtime_error("Failed to get executable path.");
72 }
73
74 std::string exe_path(result, count);
75
76 // Обрезаем путь до директории (удаляем имя файла, оставляем только путь к папке)
77 size_t pos = exe_path.find_last_of("\\/");
78 if (pos != std::string::npos) {
79 exe_path = exe_path.substr(0, pos);
80 }
81
82 return exe_path;
83# endif
84 }
85
89 std::vector<std::string> get_list_files(const std::string& path) {
90 std::vector<std::string> list_files;
91 std::string search_path = path;
92
93 // Handle empty path by using current working directory
94 if (search_path.empty()) {
95# ifdef _WIN32
96 char buffer[MAX_PATH];
97 GetCurrentDirectoryA(MAX_PATH, buffer);
98 search_path = buffer;
99# else
100 char buffer[PATH_MAX];
101 if (getcwd(buffer, PATH_MAX)) {
102 search_path = buffer;
103 }
104# endif
105 }
106
107 // Ensure the path ends with the appropriate separator
108# ifdef _WIN32
109 char path_separator = '\\';
110# else
111 char path_separator = '/';
112# endif
113
114 if (search_path.back() != '/' && search_path.back() != '\\') {
115 search_path += path_separator;
116 }
117
118# ifdef _WIN32
119 std::string pattern = search_path + "*";
120 WIN32_FIND_DATAA fd;
121 HANDLE hFind = FindFirstFileA(pattern.c_str(), &fd);
122
123 if (hFind != INVALID_HANDLE_VALUE) {
124 do {
125 std::string file_name = fd.cFileName;
126
127 if (file_name == "." || file_name == "..") {
128 continue;
129 }
130
131 std::string full_path = search_path + file_name;
132
133 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
134 // Recurse into subdirectories
135 std::vector<std::string> sub_files = get_list_files(full_path);
136 list_files.insert(list_files.end(), sub_files.begin(), sub_files.end());
137 } else {
138 // Add files to the list
139 list_files.push_back(full_path);
140 }
141 } while (FindNextFileA(hFind, &fd));
142 FindClose(hFind);
143 }
144# else
145 DIR* dir = opendir(search_path.c_str());
146 if (dir) {
147 struct dirent* entry;
148 while ((entry = readdir(dir)) != nullptr) {
149 std::string file_name = entry->d_name;
150
151 if (file_name == "." || file_name == "..") {
152 continue;
153 }
154
155 std::string full_path = search_path + file_name;
156 struct stat statbuf;
157
158 if (stat(full_path.c_str(), &statbuf) == 0) {
159 if (S_ISDIR(statbuf.st_mode)) {
160 // Recurse into subdirectories
161 std::vector<std::string> sub_files = get_list_files(full_path);
162 list_files.insert(list_files.end(), sub_files.begin(), sub_files.end());
163 } else if (S_ISREG(statbuf.st_mode)) {
164 // Add files to the list
165 list_files.push_back(full_path);
166 }
167 }
168 }
169 closedir(dir);
170 }
171# endif
172 return list_files;
173 }
174
178 std::string get_file_name(const std::string& file_path) {
179 size_t pos = file_path.find_last_of("/\\");
180 if (pos == std::string::npos) return file_path;
181 return file_path.substr(pos + 1);
182 }
183
184#if __cplusplus >= 201703L
185
190 inline std::string make_relative(const std::string& file_path, const std::string& base_path) {
191 if (base_path.empty()) return file_path;
192 std::filesystem::path fileP = std::filesystem::u8path(file_path);
193 std::filesystem::path baseP = std::filesystem::u8path(base_path);
194 std::error_code ec; // For exception-safe operation
195 std::filesystem::path relativeP = std::filesystem::relative(fileP, baseP, ec);
196 if (ec) {
197 // If there is an error, return the original file_path
198 return file_path;
199 } else {
200 return relativeP.u8string();
201 }
202 }
203
207 void create_directories(const std::string& path) {
208 std::filesystem::path dir(path);
209 if (!std::filesystem::exists(dir)) {
210 std::error_code ec;
211 if (!std::filesystem::create_directories(dir, ec)) {
212 throw std::runtime_error("Failed to create directories for path: " + path);
213 }
214 }
215 }
216
217#else
218
222 std::string root;
223 std::vector<std::string> components;
224 };
225
229 PathComponents split_path(const std::string& path) {
230 PathComponents result;
231 size_t i = 0;
232 size_t n = path.size();
233
234 // Handle root paths for Unix and Windows
235 if (n >= 1 && (path[0] == '/' || path[0] == '\\')) {
236 // Unix root "/"
237 result.root = "/";
238 ++i;
239 } else if (n >= 2 && std::isalpha(path[0]) && path[1] == ':') {
240 // Windows drive letter "C:"
241 result.root = path.substr(0, 2);
242 i = 2;
243 if (n >= 3 && (path[2] == '/' || path[2] == '\\')) {
244 // "C:/"
245 ++i;
246 }
247 }
248
249 // Split the path into components
250 while (i < n) {
251 // Skip path separators
252 while (i < n && (path[i] == '/' || path[i] == '\\')) {
253 ++i;
254 }
255 // Find the next separator
256 size_t j = i;
257 while (j < n && path[j] != '/' && path[j] != '\\') {
258 ++j;
259 }
260 if (i < j) {
261 result.components.push_back(path.substr(i, j - i));
262 i = j;
263 }
264 }
265
266 return result;
267 }
268
273 std::string make_relative(const std::string& file_path, const std::string& base_path) {
274 if (base_path.empty()) return file_path;
275 PathComponents file_pc = split_path(file_path);
276 PathComponents base_pc = split_path(base_path);
277
278 // If roots are different, return the original file_path
279 if (file_pc.root != base_pc.root) {
280 return file_path;
281 }
282
283 // Find the common prefix components
284 size_t common_size = 0;
285 while (common_size < file_pc.components.size() &&
286 common_size < base_pc.components.size() &&
287 file_pc.components[common_size] == base_pc.components[common_size]) {
288 ++common_size;
289 }
290
291 // Build the relative path components
292 std::vector<std::string> relative_components;
293
294 // Add ".." for each remaining component in base path
295 for (size_t i = common_size; i < base_pc.components.size(); ++i) {
296 relative_components.push_back("..");
297 }
298
299 // Add the remaining components from the file path
300 for (size_t i = common_size; i < file_pc.components.size(); ++i) {
301 relative_components.push_back(file_pc.components[i]);
302 }
303
304 // Join the components into a relative path string
305 std::string relative_path;
306 if (relative_components.empty()) {
307 relative_path = ".";
308 } else {
309 for (size_t i = 0; i < relative_components.size(); ++i) {
310 if (i > 0) {
311# ifdef _WIN32
312 relative_path += '\\'; // Windows
313# else
314 relative_path += '/';
315# endif
316 }
317 relative_path += relative_components[i];
318 }
319 }
320
321 return relative_path;
322 }
323
327 inline bool is_file(const std::string& path) {
328 size_t dot_pos = path.find_last_of('.');
329 size_t slash_pos = path.find_last_of("/\\");
330 return (dot_pos != std::string::npos && (slash_pos == std::string::npos || dot_pos > slash_pos));
331 }
332
336 void create_directories(const std::string& path) {
337 if (path.empty()) return;
338 PathComponents path_pc = split_path(path);
339 auto &components = path_pc.components;
340 size_t components_size = components.size();
341
342 // Check if the last component is a file
343 if (is_file(path)) {
344 --components_size;
345 }
346
347 // Build the path incrementally and create directories
348 std::string current_path = path_pc.root;
349 for (size_t i = 0; i < components_size; ++i) {
350 if (!current_path.empty() && current_path.back() != '/' && current_path.back() != '\\') {
351 current_path += '/';
352 }
353 current_path += components[i];
354
355 /*
356 if (i) current_path += components[i] + "/";
357 else if (i == 0 &&
358 (components[i] == "/" ||
359 components[i] == "~/")) {
360 current_path += components[i];
361 } else {
362 current_path += components[i] + "/";
363 }
364 */
365
366 // Skip special components
367 if (components[i] == ".." ||
368 components[i] == "/" ||
369 components[i] == "~/") continue;
370# ifdef _WIN32
371 int ret = _mkdir(current_path.c_str());
372# else
373 int ret = mkdir(current_path.c_str(), 0755);
374# endif
375 int errnum = errno;
376 if (ret != 0 && errnum != EEXIST) {
377 throw std::runtime_error("Failed to create directory: " + current_path);
378 }
379 }
380 }
381
382#endif // __cplusplus >= 201703L
383
384}; // namespace logit
385
386#endif // _LOGIT_PATH_UTILS_HPP_INCLUDED
The primary namespace for the LogIt++ library.
std::string get_exe_path()
Retrieves the directory of the executable file.
std::vector< std::string > get_list_files(const std::string &path)
Recursively retrieves a list of all files in a directory.
std::string make_relative(const std::string &file_path, const std::string &base_path)
Computes the relative path from base_path to file_path.
bool is_file(const std::string &path)
Checks if a path represents a file (by checking for an extension).
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_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::string root
The root part of the path (e.g., "/", "C:")
std::vector< std::string > components
The components of the path.