auto pwd_stat = Stat::stat(pwd);
auto cwd_stat = Stat::stat(actual_cwd);
- if (!pwd_stat || !cwd_stat || !pwd_stat.same_inode_as(cwd_stat)) {
- return actual_cwd;
- }
- std::string normalized_pwd = normalize_absolute_path(pwd);
- return normalized_pwd == pwd
- || Stat::stat(normalized_pwd).same_inode_as(pwd_stat)
- ? normalized_pwd
- : pwd;
+ return !pwd_stat || !cwd_stat || !pwd_stat.same_inode_as(cwd_stat)
+ ? actual_cwd
+ : normalize_concrete_absolute_path(pwd);
#endif
}
const auto real_path = Util::real_path(std::string(path));
const auto add_relpath_candidates = [&](auto path) {
- const std::string normalized_path = Util::normalize_absolute_path(path);
+ const std::string normalized_path =
+ Util::normalize_abstract_absolute_path(path);
relpath_candidates.push_back(
Util::get_relative_path(actual_cwd, normalized_path));
if (apparent_cwd != actual_cwd) {
}
std::string
-normalize_absolute_path(string_view path)
+normalize_abstract_absolute_path(string_view path)
{
if (!util::is_absolute_path(path)) {
return std::string(path);
if (path.find("\\") != string_view::npos) {
std::string new_path(path);
std::replace(new_path.begin(), new_path.end(), '\\', '/');
- return normalize_absolute_path(new_path);
+ return normalize_abstract_absolute_path(new_path);
}
std::string drive(path.substr(0, 2));
#endif
}
+std::string
+normalize_concrete_absolute_path(const std::string& path)
+{
+ const auto normalized_path = normalize_abstract_absolute_path(path);
+ return Stat::stat(normalized_path).same_inode_as(Stat::stat(path))
+ ? normalized_path
+ : path;
+}
+
uint64_t
parse_duration(const std::string& duration)
{
//
// Normalization here means syntactically removing redundant slashes and
// resolving "." and ".." parts. The algorithm does however *not* follow
-// symlinks, so the result may not actually resolve to `path`.
+// symlinks, so the result may not actually resolve to the same filesystem entry
+// as `path` (nor to any existing file system entry for that matter).
//
// On Windows: Backslashes are replaced with forward slashes.
-std::string normalize_absolute_path(nonstd::string_view path);
+std::string normalize_abstract_absolute_path(nonstd::string_view path);
+
+// Like normalize_abstract_absolute_path, but returns `path` unchanged if the
+// normalized result doesn't resolve to the same file system entry as `path`.
+std::string normalize_concrete_absolute_path(const std::string& path);
// Parse `duration`, an unsigned integer with d (days) or s (seconds) suffix,
// into seconds. Throws `core::Error` on error.
#endif
}
-TEST_CASE("Util::normalize_absolute_path")
+TEST_CASE("Util::normalize_abstract_absolute_path")
{
- CHECK(Util::normalize_absolute_path("") == "");
- CHECK(Util::normalize_absolute_path(".") == ".");
- CHECK(Util::normalize_absolute_path("..") == "..");
- CHECK(Util::normalize_absolute_path("...") == "...");
- CHECK(Util::normalize_absolute_path("x/./") == "x/./");
+ CHECK(Util::normalize_abstract_absolute_path("") == "");
+ CHECK(Util::normalize_abstract_absolute_path(".") == ".");
+ CHECK(Util::normalize_abstract_absolute_path("..") == "..");
+ CHECK(Util::normalize_abstract_absolute_path("...") == "...");
+ CHECK(Util::normalize_abstract_absolute_path("x/./") == "x/./");
#ifdef _WIN32
- CHECK(Util::normalize_absolute_path("c:/") == "c:/");
- CHECK(Util::normalize_absolute_path("c:\\") == "c:/");
- CHECK(Util::normalize_absolute_path("c:/.") == "c:/");
- CHECK(Util::normalize_absolute_path("c:\\..") == "c:/");
- CHECK(Util::normalize_absolute_path("c:\\x/..") == "c:/");
- CHECK(Util::normalize_absolute_path("c:\\x/./y\\..\\\\z") == "c:/x/z");
+ CHECK(Util::normalize_abstract_absolute_path("c:/") == "c:/");
+ CHECK(Util::normalize_abstract_absolute_path("c:\\") == "c:/");
+ CHECK(Util::normalize_abstract_absolute_path("c:/.") == "c:/");
+ CHECK(Util::normalize_abstract_absolute_path("c:\\..") == "c:/");
+ CHECK(Util::normalize_abstract_absolute_path("c:\\x/..") == "c:/");
+ CHECK(Util::normalize_abstract_absolute_path("c:\\x/./y\\..\\\\z")
+ == "c:/x/z");
#else
- CHECK(Util::normalize_absolute_path("/") == "/");
- CHECK(Util::normalize_absolute_path("/.") == "/");
- CHECK(Util::normalize_absolute_path("/..") == "/");
- CHECK(Util::normalize_absolute_path("/./") == "/");
- CHECK(Util::normalize_absolute_path("//") == "/");
- CHECK(Util::normalize_absolute_path("/../x") == "/x");
- CHECK(Util::normalize_absolute_path("/x/./y/z") == "/x/y/z");
- CHECK(Util::normalize_absolute_path("/x/../y/z/") == "/y/z");
- CHECK(Util::normalize_absolute_path("/x/.../y/z") == "/x/.../y/z");
- CHECK(Util::normalize_absolute_path("/x/yyy/../zz") == "/x/zz");
- CHECK(Util::normalize_absolute_path("//x/yyy///.././zz") == "/x/zz");
+ CHECK(Util::normalize_abstract_absolute_path("/") == "/");
+ CHECK(Util::normalize_abstract_absolute_path("/.") == "/");
+ CHECK(Util::normalize_abstract_absolute_path("/..") == "/");
+ CHECK(Util::normalize_abstract_absolute_path("/./") == "/");
+ CHECK(Util::normalize_abstract_absolute_path("//") == "/");
+ CHECK(Util::normalize_abstract_absolute_path("/../x") == "/x");
+ CHECK(Util::normalize_abstract_absolute_path("/x/./y/z") == "/x/y/z");
+ CHECK(Util::normalize_abstract_absolute_path("/x/../y/z/") == "/y/z");
+ CHECK(Util::normalize_abstract_absolute_path("/x/.../y/z") == "/x/.../y/z");
+ CHECK(Util::normalize_abstract_absolute_path("/x/yyy/../zz") == "/x/zz");
+ CHECK(Util::normalize_abstract_absolute_path("//x/yyy///.././zz") == "/x/zz");
+#endif
+}
+
+TEST_CASE("Util::normalize_concrete_absolute_path")
+{
+#ifndef _WIN32
+ TestContext test_context;
+
+ Util::write_file("file", "");
+ REQUIRE(Util::create_dir("dir1/dir2"));
+ REQUIRE(symlink("dir1/dir2", "symlink") == 0);
+ const auto cwd = Util::get_actual_cwd();
+
+ CHECK(Util::normalize_concrete_absolute_path(FMT("{}/file", cwd))
+ == FMT("{}/file", cwd));
+ CHECK(Util::normalize_concrete_absolute_path(FMT("{}/dir1/../file", cwd))
+ == FMT("{}/file", cwd));
+ CHECK(Util::normalize_concrete_absolute_path(FMT("{}/symlink/../file", cwd))
+ == FMT("{}/symlink/../file", cwd));
#endif
}