/* Copyright (c) 2017-2026 Hans-Kristian Arntzen * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define NOMINMAX #include "path_utils.hpp" #include "string_helpers.hpp" #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #ifdef __linux__ #include #endif #endif namespace Granite { namespace Path { std::string enforce_protocol(const std::string &path) { if (path.empty()) return ""; auto index = path.find("://"); if (index == std::string::npos) return std::string("file://") + path; else return path; } std::string canonicalize_path(const std::string &path) { std::string transformed; transformed.resize(path.size()); transform(begin(path), end(path), begin(transformed), [](char c) -> char { return c == '\\' ? '/' : c; }); auto data = Util::split_no_empty(transformed, "/"); std::vector result; for (auto &i : data) { if (i == "..") { if (!result.empty()) result.pop_back(); } else if (i != ".") result.push_back(std::move(i)); } std::string res; for (auto &i : result) { if (&i != result.data()) res += "/"; res += i; } return res; } static size_t find_last_slash(const std::string &str) { #ifdef _WIN32 auto index = str.find_last_of("/\\"); #else auto index = str.find_last_of('/'); #endif return index; } bool is_abspath(const std::string &path) { if (path.empty()) return false; if (path.front() == '/') return true; #ifdef _WIN32 { auto index = std::min(path.find(":/"), path.find(":\\")); if (index != std::string::npos) return true; } #endif return path.find("://") != std::string::npos; } bool is_root_path(const std::string &path) { if (path.empty()) return false; if (path.front() == '/' && path.size() == 1) return true; #ifdef _WIN32 { auto index = std::min(path.find(":/"), path.find(":\\")); if (index != std::string::npos && (index + 2) == path.size()) return true; } #endif auto index = path.find("://"); return index != std::string::npos && (index + 3) == path.size(); } std::string join(const std::string &base, const std::string &path) { if (base.empty()) return path; if (path.empty()) return base; if (is_abspath(path)) return path; auto index = find_last_slash(base); bool need_slash = index != base.size() - 1; return Util::join(base, need_slash ? "/" : "", path); } std::string basedir(const std::string &path) { if (path.empty()) return ""; if (is_root_path(path)) return path; auto index = find_last_slash(path); if (index == std::string::npos) return "."; // Preserve the first slash. if (index == 0 && is_abspath(path)) index++; auto ret = path.substr(0, index + 1); if (!is_root_path(ret)) ret.pop_back(); return ret; } std::string basename(const std::string &path) { if (path.empty()) return ""; auto index = find_last_slash(path); if (index == std::string::npos) return path; auto base = path.substr(index + 1, std::string::npos); return base; } std::string relpath(const std::string &base, const std::string &path) { return Path::join(basedir(base), path); } std::string ext(const std::string &path) { auto index = path.find_last_of('.'); if (index == std::string::npos) return ""; else return path.substr(index + 1, std::string::npos); } std::pair split(const std::string &path) { if (path.empty()) return make_pair(std::string("."), std::string(".")); auto index = find_last_slash(path); if (index == std::string::npos) return make_pair(std::string("."), path); auto base = path.substr(index + 1, std::string::npos); return make_pair(path.substr(0, index), base); } std::pair protocol_split(const std::string &path) { if (path.empty()) return make_pair(std::string(""), std::string("")); auto index = path.find("://"); if (index == std::string::npos) return make_pair(std::string(""), path); return make_pair(path.substr(0, index), path.substr(index + 3, std::string::npos)); } std::string get_executable_path() { #ifdef _WIN32 wchar_t target[4096]; DWORD ret = GetModuleFileNameW(GetModuleHandle(nullptr), target, sizeof(target) / sizeof(wchar_t)); return canonicalize_path(Path::to_utf8(target, ret)); #else pid_t pid = getpid(); static const char *exts[] = { "exe", "file", "a.out" }; char link_path[PATH_MAX]; char target[PATH_MAX]; for (auto *ext : exts) { snprintf(link_path, sizeof(link_path), "/proc/%u/%s", unsigned(pid), ext); ssize_t ret = readlink(link_path, target, sizeof(target) - 1); if (ret >= 0) { target[ret] = '\0'; return std::string(target); } } return ""; #endif } #ifdef _WIN32 std::string to_utf8(const wchar_t *wstr, size_t len) { std::vector char_buffer; auto ret = WideCharToMultiByte(CP_UTF8, 0, wstr, len, nullptr, 0, nullptr, nullptr); if (ret < 0) return ""; char_buffer.resize(ret); WideCharToMultiByte(CP_UTF8, 0, wstr, len, char_buffer.data(), char_buffer.size(), nullptr, nullptr); return std::string(char_buffer.data(), char_buffer.size()); } std::wstring to_utf16(const char *str, size_t len) { std::vector wchar_buffer; auto ret = MultiByteToWideChar(CP_UTF8, 0, str, len, nullptr, 0); if (ret < 0) return L""; wchar_buffer.resize(ret); MultiByteToWideChar(CP_UTF8, 0, str, len, wchar_buffer.data(), wchar_buffer.size()); return std::wstring(wchar_buffer.data(), wchar_buffer.size()); } std::string to_utf8(const std::wstring &wstr) { return to_utf8(wstr.data(), wstr.size()); } std::wstring to_utf16(const std::string &str) { return to_utf16(str.data(), str.size()); } #endif } }