diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 48b6e1020e..04061da85f 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -387,24 +387,53 @@ shared_ptr fs::set_virtual_device(const std::string& name, shar std::string fs::get_parent_dir(std::string_view path, u32 parent_level) { - std::string normalized_path = std::filesystem::path(path).lexically_normal().string(); + std::string_view result = path; -#ifdef _WIN32 - std::replace(normalized_path.begin(), normalized_path.end(), '\\', '/'); -#endif + // Number of path components to remove + usz to_remove = parent_level; - if (normalized_path.ends_with('/')) - normalized_path.pop_back(); - - while (parent_level--) + while (to_remove--) { - if (const auto pos = normalized_path.find_last_of('/'); pos != umax) - normalized_path = normalized_path.substr(0, pos); + // Trim contiguous delimiters at the end + if (usz sz = result.find_last_not_of(delim) + 1) + { + result = result.substr(0, sz); + } else + { + return "/"; + } + + const auto elem = result.substr(result.find_last_of(delim) + 1); + + if (elem.empty() || elem.size() == result.size()) + { break; + } + + if (elem == ".") + { + to_remove += 1; + } + + if (elem == "..") + { + to_remove += 2; + } + + result.remove_suffix(elem.size()); } - return normalized_path.empty() ? "/" : normalized_path; + if (usz sz = result.find_last_not_of(delim) + 1) + { + result = result.substr(0, sz); + } + else + { + return "/"; + } + + return std::string{result}; } bool fs::stat(const std::string& path, stat_t& info) diff --git a/Utilities/File.h b/Utilities/File.h index caea341446..55fbd05998 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -165,7 +165,7 @@ namespace fs // Set virtual device with specified name (nullptr for deletion) shared_ptr set_virtual_device(const std::string& name, shared_ptr device); - // Try to get normalized parent directory + // Try to get parent directory std::string get_parent_dir(std::string_view path, u32 parent_level = 1); // Get file information