/* Copyright <2018-2024> 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. */ #include "utils/path.hpp" #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/string.hpp" #include "utils/unix.hpp" namespace { static const std::string directory_seperator_str{ repertory::utils::path::directory_seperator, }; static const std::wstring directory_seperator_str_w{ repertory::utils::path::directory_seperator_w, }; [[nodiscard]] auto resolve(std::string path) -> std::string { #if defined(_WIN32) repertory::utils::string::replace(path, "~", "%USERPROFILE%"); std::string dest; dest.resize(::ExpandEnvironmentStringsA(path.c_str(), nullptr, 0)); ::ExpandEnvironmentStringsA(path.c_str(), dest.data(), static_cast(dest.size())); path = dest.c_str(); return path; #else // !defined (_WIN32) std::string home{}; repertory::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) { home = (pw->pw_dir ? pw->pw_dir : ""); if (home.empty() || ((home == "/") && (getuid() != 0))) { home = repertory::utils::path::combine("/home", {pw->pw_name}); } }); return repertory::utils::string::replace(path, "~", home); #endif // defined (_WIN32) } } // namespace namespace repertory::utils::path { auto absolute(std::string_view path) -> std::string { std::string abs_path{path}; #if defined(_WIN32) if (not abs_path.empty() && ::PathIsRelative(abs_path.c_str())) { std::string temp; temp.resize(MAX_PATH + 1); abs_path = _fullpath(temp.c_str(), abs_path.data(), MAX_PATH); } #else // !defined(_WIN32) abs_path = resolve(finalize(abs_path)); if (not abs_path.empty() && (abs_path.at(0U) != '/')) { auto found{false}; std::string tmp{abs_path}; do { auto *res = realpath(tmp.c_str(), nullptr); if (res) { abs_path = combine(res, {abs_path.substr(tmp.size())}); free(res); found = true; } else if (tmp == ".") { found = true; } else { tmp = dirname(tmp.data()); } } while (not found); } #endif // defined(_WIN32) return abs_path; } auto absolute(std::wstring_view path) -> std::wstring { return utils::string::from_utf8(absolute(utils::string::to_utf8(path))); } auto find_program_in_path(const std::string &name_without_extension) -> std::string { static std::mutex mtx{}; static std::unordered_map found_items{}; mutex_lock lock(mtx); if (found_items.contains(name_without_extension)) { return found_items.at(name_without_extension); } auto path = utils::get_environment_variable("PATH"); if (path.empty()) { return path; } #if defined(_WIN32) static constexpr const std::array extension_list{ ".bat", ".cmd", ".exe", ".ps1", }; static constexpr const auto split_char = ';'; #else // !defined(_WIN32) static constexpr const std::array extension_list{ "", ".sh", }; static constexpr const auto split_char = ':'; #endif // defined(_WIN32) const auto search_path_list = utils::string::split(path, split_char, false); for (auto &&search_path : search_path_list) { for (auto &&extension : extension_list) { auto exec_path = combine( search_path, {name_without_extension + std::string{extension}}); if (std::filesystem::exists(exec_path)) { found_items[name_without_extension] = exec_path; return exec_path; } } } return ""; } [[nodiscard]] auto find_program_in_path(std::wstring_view name_without_extension) -> std::wstring { return utils::string::from_utf8( find_program_in_path(utils::string::to_utf8(name_without_extension))); } auto get_parent_directory(std::string_view path) -> std::string { auto ret = std::filesystem::path{path}.parent_path().string(); #if !defined(_WIN32) if (ret == ".") { ret = "/"; } #endif // !defined(_WIN32) return absolute(ret); } auto get_parent_directory(std::wstring_view path) -> std::wstring { return utils::string::from_utf8( get_parent_directory(utils::string::to_utf8(path))); } auto is_trash_directory(std::string_view path) -> bool { auto trash_path = utils::string::to_lower(absolute(path)); return utils::string::begins_with(trash_path, directory_seperator_str + ".trash-") || utils::string::begins_with(trash_path, directory_seperator_str + ".trashes") || utils::string::begins_with(trash_path, directory_seperator_str + "$recycle.bin"); } auto is_trash_directory(std::wstring_view path) -> bool { return is_trash_directory(utils::string::to_utf8(path)); } auto make_file_uri(std::string_view path) -> std::string { auto abs_path = absolute(path); #if defined(_WIN32) utils::string::replace(abs_path, '\\', '/'); abs_path = '/' + abs_path; #endif // defined(_WIN32) return "file://" + abs_path; } auto make_file_uri(std::wstring_view path) -> std::wstring { return utils::string::from_utf8(make_file_uri(utils::string::to_utf8(path))); } auto remove_file_name(std::string_view path) -> std::string { auto abs_path = absolute(path); #if defined(_WIN32) ::PathRemoveFileSpec(abs_path.data()); abs_path = abs_path.c_str(); #else // !defined(_WIN32) if (abs_path != "/") { auto idx{abs_path.size() - 1U}; while ((idx != 0U) && (abs_path.at(idx) != '/')) { idx--; } abs_path = (idx > 0U) ? absolute(abs_path.substr(0U, idx)) : "/"; } #endif // defined(_WIN32) return abs_path; } auto strip_to_file_name(std::string path) -> std::string { #if defined(_WIN32) return ::PathFindFileName(path.c_str()); #else // !defined(_WIN32) return utils::string::contains(path, "/") ? basename(path.data()) : path; #endif // defined(_WIN32) } auto strip_to_file_name(std::wstring path) -> std::wstring { return utils::string::from_utf8( strip_to_file_name(utils::string::to_utf8(path))); } auto unmake_file_uri(std::string_view uri) -> std::string { static constexpr const std::array escape_characters = { { " ", "<", ">", "#", "%", "+", "{", "}", "|", "\\", "^", "~", "[", "]", "`", ";", "/", "?", ":", "@", "=", "&", "$", }}; static constexpr const std::array escape_sequences_lower = {{ "%20", "%3c", "%3e", "%23", "%25", "%2b", "%7b", "%7d", "%7c", "%5c", "%5e", "%7e", "%5b", "%5d", "%60", "%3b", "%2f", "%3f", "%3a", "%40", "%3d", "%26", "%24", }}; static constexpr const std::array escape_sequences_upper = {{ "%20", "%3C", "%3E", "%23", "%25", "%2B", "%7B", "%7D", "%7C", "%5C", "%5E", "%7E", "%5B", "%5D", "%60", "%3B", "%2F", "%3F", "%3A", "%40", "%3D", "%26", "%24", }}; std::string ret_uri{uri}; #if defined(_WIN32) ret_uri = ret_uri.substr(8U); #else ret_uri = ret_uri.substr(7U); #endif for (std::size_t idx = 0U; idx < escape_characters.size(); idx++) { utils::string::replace(ret_uri, escape_sequences_lower.at(idx), escape_characters.at(idx)); utils::string::replace(ret_uri, escape_sequences_upper.at(idx), escape_characters.at(idx)); } return absolute(ret_uri); } auto unmake_file_uri(std::wstring_view uri) -> std::wstring { return utils::string::from_utf8(unmake_file_uri(utils::string::to_utf8(uri))); } } // namespace repertory::utils::path