diff --git a/AK/LexicalPath.cpp b/AK/LexicalPath.cpp index 4f18a8f8d53..c50fc56b9e1 100644 --- a/AK/LexicalPath.cpp +++ b/AK/LexicalPath.cpp @@ -171,8 +171,25 @@ DeprecatedString LexicalPath::relative_path(StringView a_path, StringView a_pref return path.substring_view(prefix.length() + 1); } - // FIXME: It's still possible to generate a relative path in this case, it just needs some "..". - return path; + auto path_parts = path.split_view('/'); + auto prefix_parts = prefix.split_view('/'); + size_t index_of_first_part_that_differs = 0; + for (; index_of_first_part_that_differs < path_parts.size() && index_of_first_part_that_differs < prefix_parts.size(); index_of_first_part_that_differs++) { + if (path_parts[index_of_first_part_that_differs] != prefix_parts[index_of_first_part_that_differs]) + break; + } + + StringBuilder builder; + for (size_t part_index = index_of_first_part_that_differs; part_index < prefix_parts.size(); part_index++) { + builder.append("../"sv); + } + for (size_t part_index = index_of_first_part_that_differs; part_index < path_parts.size(); part_index++) { + builder.append(path_parts[part_index]); + if (part_index != path_parts.size() - 1) // We don't need a slash after the file name or the name of the last directory + builder.append('/'); + } + + return builder.to_deprecated_string(); } LexicalPath LexicalPath::append(StringView value) const diff --git a/Tests/AK/TestLexicalPath.cpp b/Tests/AK/TestLexicalPath.cpp index 29574049b2b..23875af3d9a 100644 --- a/Tests/AK/TestLexicalPath.cpp +++ b/Tests/AK/TestLexicalPath.cpp @@ -15,10 +15,12 @@ TEST_CASE(relative_path) EXPECT_EQ(LexicalPath::relative_path("/tmp/abc.txt"sv, "/tmp"sv), "abc.txt"sv); EXPECT_EQ(LexicalPath::relative_path("/tmp/abc.txt"sv, "/tmp/"sv), "abc.txt"sv); EXPECT_EQ(LexicalPath::relative_path("/tmp/abc.txt"sv, "/"sv), "tmp/abc.txt"sv); - EXPECT_EQ(LexicalPath::relative_path("/tmp/abc.txt"sv, "/usr"sv), "/tmp/abc.txt"sv); + EXPECT_EQ(LexicalPath::relative_path("/tmp/abc.txt"sv, "/usr"sv), "../tmp/abc.txt"sv); EXPECT_EQ(LexicalPath::relative_path("/tmp/foo.txt"sv, "tmp"sv), ""sv); EXPECT_EQ(LexicalPath::relative_path("tmp/foo.txt"sv, "/tmp"sv), ""sv); + + EXPECT_EQ(LexicalPath::relative_path("/tmp/foo/bar/baz.txt"sv, "/tmp/bar/foo/"sv), "../../foo/bar/baz.txt"sv); } TEST_CASE(regular_absolute_path)