From c3afe1009b3c7c24e3ab96e174e8068b4ea2f7a5 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 9 Jul 2021 19:11:51 +0200 Subject: [PATCH] Added filesystem::is_executable --- src/filesystem.cpp | 42 ++++++++++++++++++++++++--------------- src/filesystem.hpp | 21 +++++++++++--------- src/notebook.cpp | 14 ++----------- tests/filesystem_test.cpp | 22 ++++++++++++++++---- 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 5dcc6c6..902d76d 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -36,7 +36,7 @@ bool filesystem::write(const std::string &path, const std::string &new_content) return true; } -std::string filesystem::escape_argument(const std::string &argument) { +std::string filesystem::escape_argument(const std::string &argument) noexcept { auto escaped = argument; for(size_t pos = 0; pos < escaped.size(); ++pos) { if(escaped[pos] == ' ' || escaped[pos] == '(' || escaped[pos] == ')' || escaped[pos] == '\'' || escaped[pos] == '"') { @@ -47,7 +47,7 @@ std::string filesystem::escape_argument(const std::string &argument) { return escaped; } -std::string filesystem::unescape_argument(const std::string &argument) { +std::string filesystem::unescape_argument(const std::string &argument) noexcept { auto unescaped = argument; if(unescaped.size() >= 2) { @@ -204,13 +204,13 @@ boost::filesystem::path filesystem::get_long_path(const boost::filesystem::path #endif } -bool filesystem::file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) { +bool filesystem::file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) noexcept { if(std::distance(file_path.begin(), file_path.end()) < std::distance(path.begin(), path.end())) return false; return std::equal(path.begin(), path.end(), file_path.begin()); } -boost::filesystem::path filesystem::find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) { +boost::filesystem::path filesystem::find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) noexcept { auto current_path = path; boost::system::error_code ec; while(true) { @@ -283,7 +283,7 @@ boost::filesystem::path filesystem::get_executable(const boost::filesystem::path try { for(auto &path : bin_paths) { - if(boost::filesystem::exists(path / executable_name)) + if(is_executable(path / executable_name)) return executable_name; } @@ -317,7 +317,7 @@ boost::filesystem::path filesystem::get_executable(const boost::filesystem::path } // Based on https://stackoverflow.com/a/11295568 -const std::vector &filesystem::get_executable_search_paths() { +const std::vector &filesystem::get_executable_search_paths() noexcept { auto get_paths = [] { std::vector paths; @@ -347,21 +347,16 @@ const std::vector &filesystem::get_executable_search_pa return *executable_search_paths; } -boost::filesystem::path filesystem::find_executable(const std::string &executable_name) { +boost::filesystem::path filesystem::find_executable(const std::string &executable_name) noexcept { for(auto &path : get_executable_search_paths()) { - boost::system::error_code ec; -#ifdef _WIN32 - auto executable_path = path / (executable_name + ".exe"); -#else auto executable_path = path / executable_name; -#endif - if(boost::filesystem::exists(executable_path, ec)) + if(is_executable(executable_path)) return executable_path; } return boost::filesystem::path(); } -std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) { +std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) noexcept { std::string uri{"file://"}; static auto hex_chars = "0123456789ABCDEF"; @@ -378,7 +373,7 @@ std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) { return uri; } -boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) { +boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) noexcept { std::string encoded; if(starts_with(uri, "file://")) @@ -403,7 +398,7 @@ boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) { return unencoded; } -boost::filesystem::path filesystem::get_canonical_path(const boost::filesystem::path &path) { +boost::filesystem::path filesystem::get_canonical_path(const boost::filesystem::path &path) noexcept { try { return boost::filesystem::canonical(path); } @@ -411,3 +406,18 @@ boost::filesystem::path filesystem::get_canonical_path(const boost::filesystem:: return path; } } + +bool filesystem::is_executable(const boost::filesystem::path &path) noexcept { + if(path.empty()) + return false; + boost::system::error_code ec; +#ifdef _WIN32 + // Cannot for sure identify executable files in MSYS2 + if(boost::filesystem::exists(path, ec)) + return !boost::filesystem::is_directory(path, ec); + auto filename = path.filename().string() + ".exe"; + return boost::filesystem::exists(path.has_parent_path() ? path.parent_path() / filename : filename, ec); +#else + return boost::filesystem::exists(path, ec) && !boost::filesystem::is_directory(path, ec) && boost::filesystem::status(path, ec).permissions() & (boost::filesystem::perms::owner_exe | boost::filesystem::perms::group_exe | boost::filesystem::perms::others_exe); +#endif +} diff --git a/src/filesystem.hpp b/src/filesystem.hpp index dc39a2f..90d53d1 100644 --- a/src/filesystem.hpp +++ b/src/filesystem.hpp @@ -14,8 +14,8 @@ public: static bool write(const std::string &path) { return write(path, ""); }; static bool write(const boost::filesystem::path &path) { return write(path, ""); }; - static std::string escape_argument(const std::string &argument); - static std::string unescape_argument(const std::string &argument); + static std::string escape_argument(const std::string &argument) noexcept; + static std::string unescape_argument(const std::string &argument) noexcept; /// Does not resolve symbolic links. Returns empty path on failure. static const boost::filesystem::path &get_current_path() noexcept; @@ -34,8 +34,8 @@ public: /// Replaces ~ with home path (boost::filesystem does not recognize ~) static boost::filesystem::path get_long_path(const boost::filesystem::path &path) noexcept; - static bool file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path); - static boost::filesystem::path find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path); + static bool file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) noexcept; + static boost::filesystem::path find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) noexcept; /// Return path with dot, dot-dot and directory separator elements removed static boost::filesystem::path get_normal_path(const boost::filesystem::path &path) noexcept; @@ -47,18 +47,21 @@ public: /// Return executable with latest version in filename on systems that is lacking executable_name symbolic link static boost::filesystem::path get_executable(const boost::filesystem::path &executable_name) noexcept; - static const std::vector &get_executable_search_paths(); + static const std::vector &get_executable_search_paths() noexcept; /// Set to {} to reset get_executable_search_paths static boost::optional> executable_search_paths; /// Returns full executable path if found, or empty path otherwise. - static boost::filesystem::path find_executable(const std::string &executable_name); + static boost::filesystem::path find_executable(const std::string &executable_name) noexcept; /// Get uri from path - static std::string get_uri_from_path(const boost::filesystem::path &path); + static std::string get_uri_from_path(const boost::filesystem::path &path) noexcept; /// Get path from file uri - static boost::filesystem::path get_path_from_uri(const std::string &uri); + static boost::filesystem::path get_path_from_uri(const std::string &uri) noexcept; /// Returns path on error. Do not use boost::filesystem::canonical_path since it is bugged when current_folder() fails. - static boost::filesystem::path get_canonical_path(const boost::filesystem::path &path); + static boost::filesystem::path get_canonical_path(const boost::filesystem::path &path) noexcept; + + /// Platform independent check if path is executable + static bool is_executable(const boost::filesystem::path &path) noexcept; }; diff --git a/src/notebook.cpp b/src/notebook.cpp index d878929..3cbf489 100644 --- a/src/notebook.cpp +++ b/src/notebook.cpp @@ -208,25 +208,15 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position // Try find rust-analyzer installed with rustup auto sysroot = filesystem::get_rust_sysroot_path(); if(!sysroot.empty()) { -#ifdef _WIN32 - auto rust_analyzer = sysroot / "bin" / "rust-analyzer.exe"; -#else auto rust_analyzer = sysroot / "bin" / "rust-analyzer"; -#endif - boost::system::error_code ec; - if(boost::filesystem::exists(rust_analyzer, ec)) + if(filesystem::is_executable(rust_analyzer)) source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, filesystem::escape_argument(rust_analyzer.string()))); else { // Workaround while rust-analyzer is in nightly toolchain only auto nightly_sysroot = filesystem::get_rust_nightly_sysroot_path(); if(!nightly_sysroot.empty()) { -#ifdef _WIN32 - auto nightly_rust_analyzer = nightly_sysroot / "bin" / "rust-analyzer.exe"; -#else auto nightly_rust_analyzer = nightly_sysroot / "bin" / "rust-analyzer"; -#endif - boost::system::error_code ec; - if(boost::filesystem::exists(nightly_rust_analyzer, ec)) + if(filesystem::is_executable(nightly_rust_analyzer)) source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, filesystem::escape_argument(nightly_rust_analyzer.string()))); } } diff --git a/tests/filesystem_test.cpp b/tests/filesystem_test.cpp index ee9835e..695ef8c 100644 --- a/tests/filesystem_test.cpp +++ b/tests/filesystem_test.cpp @@ -20,10 +20,10 @@ int main() { g_assert(!paths.empty()); for(auto &path : paths) { g_assert(!path.empty()); -#ifndef _WIN32 - g_assert(boost::filesystem::exists(path)); - g_assert(boost::filesystem::is_directory(path)); -#endif + if(path.string() != "C:\\msys64\\usr\\local\\bin") { // Workaround for MSYS2 + g_assert(boost::filesystem::exists(path)); + g_assert(boost::filesystem::is_directory(path)); + } } } @@ -119,4 +119,18 @@ int main() { g_assert(uri == "file:///ro%20ot/te%20st%C3%A6%C3%B8%C3%A5.txt"); g_assert(path == filesystem::get_path_from_uri(uri)); } + + { + g_assert(!filesystem::is_executable(filesystem::get_home_path())); + g_assert(!filesystem::is_executable(filesystem::get_current_path())); + g_assert(!filesystem::is_executable(tests_path)); +#ifdef _WIN32 + g_assert(filesystem::is_executable(tests_path / ".." / "LICENSE")); +#else + g_assert(!filesystem::is_executable(tests_path / ".." / "LICENSE")); +#endif + auto ls = filesystem::find_executable("ls"); + g_assert(!ls.empty()); + g_assert(filesystem::is_executable(ls)); + } }