Title
path::lexically_relative is confused by trailing slashes
Status
c++20
Section
[fs.path.gen]
Submitter
Jonathan Wakely

Created on 2018-04-04.00:00:00 last changed 45 months ago

Messages

Date: 2018-11-12.04:39:29

Proposed resolution:

This wording is relative to N4727.

  1. Edit [fs.path.gen] as indicated:

    path lexically_relative(const path& base) const;
    

    -3- Returns: *this made relative to base. Does not resolve ([fs.class.path]) symlinks. Does not first normalize ([fs.path.generic]) *this or base.

    -4- Effects: If root_name() != base.root_name() is true or is_absolute() != base.is_absolute() is true or !has_root_directory() && base.has_root_directory() is true, returns path(). Determines the first mismatched element of *this and base as if by:

    auto [a, b] = mismatch(begin(), end(), base.begin(), base.end());
    

    Then,

    1. (4.1) — if a == end() and b == base.end(), returns path("."); otherwise

    2. (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. If n<0, returns path(); otherwise

    3. (4.?) — if n == 0 and (a == end() || a->empty()), returns path("."); otherwise

    4. (4.3) — returns an object of class path that is default-constructed, followed by […]

Date: 2018-11-12.04:39:29

[ 2018-11, Adopted in San Diego ]

Date: 2018-08-23.00:00:00

[ 2018-08-23 Batavia Issues processing ]

Status to Tentatively Ready

Date: 2018-06-18.00:00:00

[ 2018-06-18 after reflector discussion ]

Priority set to 2

Date: 2018-04-15.00:00:00

[ 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:

  1. 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

  2. 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.

Date: 2018-04-04.00:00:00

filesystem::proximate("/dir", "/dir/") returns "." when "/dir" exists, and ".." otherwise. It should always be "." because whether it exists shouldn't matter.

The problem is in 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.

History
Date User Action Args
2021-02-25 10:48:01adminsetstatus: wp -> c++20
2018-11-12 04:39:29adminsetmessages: + msg10193
2018-11-12 04:39:29adminsetstatus: voting -> wp
2018-10-08 05:13:59adminsetstatus: ready -> voting
2018-08-24 13:31:33adminsetmessages: + msg10132
2018-08-24 13:31:33adminsetstatus: new -> ready
2018-06-19 05:49:11adminsetmessages: + msg9941
2018-04-10 18:27:01adminsetmessages: + msg9813
2018-04-08 15:09:22adminsetmessages: + msg9809
2018-04-04 00:00:00admincreate