diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2023-03-30 15:48:39 +0200 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2023-03-31 17:15:03 +0200 |
commit | 7ebaf0252a9de5f77f4d44491fff5299cd5fc5b0 (patch) | |
tree | 5d28d3a498fb5b501ffbe78253f0121e38d5e6cd /src | |
parent | 1829e7ccacc3fa7ee0474b4af3679f96430da3eb (diff) |
Add CanonPath::makeRelative()
Diffstat (limited to 'src')
-rw-r--r-- | src/libutil/canon-path.cc | 26 | ||||
-rw-r--r-- | src/libutil/canon-path.hh | 7 | ||||
-rw-r--r-- | src/libutil/tests/canon-path.cc | 11 |
3 files changed, 44 insertions, 0 deletions
diff --git a/src/libutil/canon-path.cc b/src/libutil/canon-path.cc index b132b4262..ddf6db6d1 100644 --- a/src/libutil/canon-path.cc +++ b/src/libutil/canon-path.cc @@ -100,4 +100,30 @@ std::ostream & operator << (std::ostream & stream, const CanonPath & path) return stream; } +std::string CanonPath::makeRelative(const CanonPath & path) const +{ + auto p1 = begin(); + auto p2 = path.begin(); + + for (; p1 != end() && p2 != path.end() && *p1 == *p2; ++p1, ++p2) ; + + if (p1 == end() && p2 == path.end()) + return "."; + else if (p1 == end()) + return std::string(p2.remaining); + else { + std::string res; + while (p1 != end()) { + ++p1; + if (!res.empty()) res += '/'; + res += ".."; + } + if (p2 != path.end()) { + if (!res.empty()) res += '/'; + res += p2.remaining; + } + return res; + } +} + } diff --git a/src/libutil/canon-path.hh b/src/libutil/canon-path.hh index 9d5984584..77f98775c 100644 --- a/src/libutil/canon-path.hh +++ b/src/libutil/canon-path.hh @@ -85,6 +85,9 @@ public: bool operator != (const Iterator & x) const { return remaining.data() != x.remaining.data(); } + bool operator == (const Iterator & x) const + { return !(*this != x); } + const std::string_view operator * () const { return remaining.substr(0, slash); } @@ -166,6 +169,10 @@ public: the `allowed` paths are within `this`. (The latter condition ensures access to the parents of allowed paths.) */ bool isAllowed(const std::set<CanonPath> & allowed) const; + + /* Return a representation `x` of `path` relative to `this`, i.e. + `CanonPath(this.makeRelative(x), this) == path`. */ + std::string makeRelative(const CanonPath & path) const; }; std::ostream & operator << (std::ostream & stream, const CanonPath & path); diff --git a/src/libutil/tests/canon-path.cc b/src/libutil/tests/canon-path.cc index c1c5adadf..8fbcf0ddf 100644 --- a/src/libutil/tests/canon-path.cc +++ b/src/libutil/tests/canon-path.cc @@ -152,4 +152,15 @@ namespace nix { ASSERT_TRUE (CanonPath("/").isAllowed(allowed)); } } + + TEST(CanonPath, makeRelative) { + CanonPath d("/foo/bar"); + ASSERT_EQ(d.makeRelative(CanonPath("/foo/bar")), "."); + ASSERT_EQ(d.makeRelative(CanonPath("/foo")), ".."); + ASSERT_EQ(d.makeRelative(CanonPath("/")), "../.."); + ASSERT_EQ(d.makeRelative(CanonPath("/foo/bar/xyzzy")), "xyzzy"); + ASSERT_EQ(d.makeRelative(CanonPath("/foo/bar/xyzzy/bla")), "xyzzy/bla"); + ASSERT_EQ(d.makeRelative(CanonPath("/foo/xyzzy/bla")), "../xyzzy/bla"); + ASSERT_EQ(d.makeRelative(CanonPath("/xyzzy/bla")), "../../xyzzy/bla"); + } } |