[fxl][files] add JoinPath
TESTED=`fx run-host-tests fxl_unittests -- --gtest_filter=Path.JoinPath`
Change-Id: I2da21e27e458a61fa678c217e44ada592654b134
diff --git a/public/lib/fxl/files/path.cc b/public/lib/fxl/files/path.cc
index 6912481..a217da5 100644
--- a/public/lib/fxl/files/path.cc
+++ b/public/lib/fxl/files/path.cc
@@ -238,4 +238,23 @@
return true;
}
+std::string JoinPath(const std::string& path1, const std::string& path2) {
+ if (path1.empty()) {
+ return path2;
+ }
+ if (path2.empty()) {
+ return path1;
+ }
+ if (path1.back() == '/') {
+ if (path2.front() == '/') {
+ return path1 + path2.substr(1);
+ }
+ } else {
+ if (path2.front() != '/') {
+ return path1 + "/" + path2;
+ }
+ }
+ return path1 + path2;
+}
+
} // namespace files
diff --git a/public/lib/fxl/files/path.h b/public/lib/fxl/files/path.h
index c4a0c89..38743bb 100644
--- a/public/lib/fxl/files/path.h
+++ b/public/lib/fxl/files/path.h
@@ -36,6 +36,26 @@
FXL_EXPORT bool DeletePathAt(int root_fd, const std::string& path,
bool recursive);
+// Joins two paths together.
+// Regardless if |path1| has a trailing '/' or |path2| has a leading '/', there
+// will be only one '/' in-between in the joined path.
+// Note that if either path is "" then the other path is returned unchanged.
+//
+// JoinPath("/foo", "bar") -> "/foo/bar"
+// JoinPath("/foo", "/bar") -> "/foo/bar"
+// JoinPath("/foo/", "bar") -> "/foo/bar"
+// JoinPath("/foo/", "/bar") -> "/foo/bar"
+//
+// JoinPath("", "") -> ""
+// JoinPath("", "/foo") -> "/foo"
+// JoinPath("", "foo") -> "foo"
+// JoinPath("/foo", "") -> "/foo"
+// JoinPath("foo", "") -> "foo"
+// JoinPath("/foo/", "") -> "/foo/"
+// JoinPath("foo/", "") -> "foo/"
+FXL_EXPORT std::string JoinPath(const std::string& path1,
+ const std::string& path2);
+
} // namespace files
#endif // LIB_FXL_FILES_PATH_H_
diff --git a/public/lib/fxl/files/path_unittest.cc b/public/lib/fxl/files/path_unittest.cc
index b0b5c45..ebff43f 100644
--- a/public/lib/fxl/files/path_unittest.cc
+++ b/public/lib/fxl/files/path_unittest.cc
@@ -241,5 +241,41 @@
EXPECT_FALSE(IsDirectoryAt(root.get(), sub_sub_dir1));
}
+TEST(Path, JoinPath) {
+ EXPECT_EQ(JoinPath("foo", ""), "foo");
+ EXPECT_EQ(JoinPath("foo", "bar"), "foo/bar");
+ EXPECT_EQ(JoinPath("foo", "bar/"), "foo/bar/");
+ EXPECT_EQ(JoinPath("foo", "/bar"), "foo/bar");
+ EXPECT_EQ(JoinPath("foo", "/bar/"), "foo/bar/");
+
+ EXPECT_EQ(JoinPath("foo/", ""), "foo/");
+ EXPECT_EQ(JoinPath("foo/", ""), "foo/");
+ EXPECT_EQ(JoinPath("foo/", "bar"), "foo/bar");
+ EXPECT_EQ(JoinPath("foo/", "bar/"), "foo/bar/");
+ EXPECT_EQ(JoinPath("foo/", "/bar"), "foo/bar");
+ EXPECT_EQ(JoinPath("foo/", "/bar/"), "foo/bar/");
+
+ EXPECT_EQ(JoinPath("/foo", ""), "/foo");
+ EXPECT_EQ(JoinPath("/foo", "bar"), "/foo/bar");
+ EXPECT_EQ(JoinPath("/foo", "bar/"), "/foo/bar/");
+ EXPECT_EQ(JoinPath("/foo", "/bar"), "/foo/bar");
+ EXPECT_EQ(JoinPath("/foo", "/bar/"), "/foo/bar/");
+
+ EXPECT_EQ(JoinPath("/foo/", ""), "/foo/");
+ EXPECT_EQ(JoinPath("/foo/", "bar"), "/foo/bar");
+ EXPECT_EQ(JoinPath("/foo/", "bar/"), "/foo/bar/");
+ EXPECT_EQ(JoinPath("/foo/", "/bar"), "/foo/bar");
+ EXPECT_EQ(JoinPath("/foo/", "/bar/"), "/foo/bar/");
+
+ EXPECT_EQ(JoinPath("", ""), "");
+ EXPECT_EQ(JoinPath("", "bar"), "bar");
+ EXPECT_EQ(JoinPath("", "bar/"), "bar/");
+ EXPECT_EQ(JoinPath("", "/bar"), "/bar");
+ EXPECT_EQ(JoinPath("", "/bar/"), "/bar/");
+
+ EXPECT_EQ(JoinPath("/foo/bar/baz/", "/blah/blink/biz"),
+ "/foo/bar/baz/blah/blink/biz");
+}
+
} // namespace
} // namespace files