path::lexically_relative
is confused by trailing slashesSection: 31.12.6.5.11 [fs.path.gen] Status: C++20 Submitter: Jonathan Wakely Opened: 2018-04-04 Last modified: 2021-02-25
Priority: 2
View all other issues in [fs.path.gen].
View all issues with C++20 status.
Discussion:
filesystem::proximate("/dir", "/dir/")
returns "."
when "/dir"
exists, and ".."
otherwise. It should always be "."
because whether it exists shouldn't matter.
filesystem::path::lexically_relative
, as shown by:
path("/dir").lexically_relative("/dir/."); // yields "" path("/dir").lexically_relative("/dir/"); // yields ".."
The two calls should yield the same result, and when iteration of a path
with a trailing slash gave "." as the final
element they did yield the same result. In the final C++17 spec the trailing slash produces an empty filename in the
iteration sequence, and lexically_relative
doesn't handle that correctly.
[2018-04-10, Jonathan comments]
There are more inconsistencies with paths that are "obviously" equivalent to the human reader:
path("a/b/c").lexically_relative("a/b/c") // yields "." path("a/b/c").lexically_relative("a/b/c/") // yields ".." path("a/b/c").lexically_relative("a/b/c/.") // yields "" path("a/b/c/").lexically_relative("a/b/c") // yields "" path("a/b/c/.").lexically_relative("a/b/c") // yields "." path("a/b/c/.").lexically_relative("a/b/c/") // yields "../."
I think the right solution is:
when counting [b, base.end())
in bullet (4.2) handle empty filename elements (which can only occur as the last
element, due to a trailing slash) equivalently to dot elements; and
add a new condition for the case where n == 0
and [a, end())
contains no non-empty elements, i.e. the
paths are equivalent except for final dots or a final slash, which don't introduce any relative difference between the paths.
[2018-06-18 after reflector discussion]
Priority set to 2
[2018-08-23 Batavia Issues processing]
Status to Tentatively Ready
[2018-11, Adopted in San Diego]
Proposed resolution:
This wording is relative to N4727.
Edit 31.12.6.5.11 [fs.path.gen] as indicated:
path lexically_relative(const path& base) const;-3- Returns:
-4- Effects: If*this
made relative tobase
. Does not resolve (31.12.6 [fs.class.path]) symlinks. Does not first normalize (31.12.6.2 [fs.path.generic])*this
orbase
.root_name() != base.root_name()
istrue
oris_absolute() != base.is_absolute()
istrue
or!has_root_directory() && base.has_root_directory()
istrue
, returnspath()
. Determines the first mismatched element of*this
andbase
as if by:auto [a, b] = mismatch(begin(), end(), base.begin(), base.end());Then,
(4.1) — if
a == end()
andb == base.end()
, returnspath(".")
; otherwise(4.2) — let
n
be the number of filename elements in[b, base.end())
that are not dot or dot-dot or empty, minus the number that are dot-dot. Ifn<0
, returnspath()
; otherwise(4.?) — if
n == 0
and(a == end() || a->empty())
, returnspath(".")
; otherwise(4.3) — returns an object of class
path
that is default-constructed, followed by […]