Move adb_dirname and adb_basename to libbase
adb already provides an implementation of dirname and basename that
take and produce std::strings, move it into libbase so it can be
used in the implementation of GetExecutableDirectory.
Test: out/host/linux-x86/nativetest64/adb_test/adb_test
Test: out/host/linux-x86/nativetest64/libbase_test/libbase_test
Test: adb shell /data/nativetest64/libbase_test/libbase_test64
Change-Id: Ideb1627607b14562121316d4ed27fa6fb0930684
diff --git a/base/file.cpp b/base/file.cpp
index 6284b04..32c2439 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -18,11 +18,13 @@
#include <errno.h>
#include <fcntl.h>
+#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
+#include <mutex>
#include <string>
#include <vector>
@@ -236,5 +238,56 @@
#endif
}
+std::string Basename(const std::string& path) {
+
+ // Copy path because basename may modify the string passed in.
+ std::string result(path);
+
+#if !defined(__BIONIC__)
+ // Use lock because basename() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to basename in the process also grab this same lock, but its
+ // better than nothing. Bionic's basename returns a thread-local buffer.
+ static std::mutex& basename_lock = *new std::mutex();
+ std::lock_guard<std::mutex> lock(basename_lock);
+#endif
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* name = basename(&result[0]);
+
+ // In case basename returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(name);
+
+ return result;
+}
+
+std::string Dirname(const std::string& path) {
+ // Copy path because dirname may modify the string passed in.
+ std::string result(path);
+
+#if !defined(__BIONIC__)
+ // Use lock because dirname() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to dirname in the process also grab this same lock, but its
+ // better than nothing. Bionic's dirname returns a thread-local buffer.
+ static std::mutex& dirname_lock = *new std::mutex();
+ std::lock_guard<std::mutex> lock(dirname_lock);
+#endif
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* parent = dirname(&result[0]);
+
+ // In case dirname returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(parent);
+
+ return result;
+}
+
} // namespace base
} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index ed39ce9..bbd5037 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -162,3 +162,15 @@
TEST(file, GetExecutablePath) {
ASSERT_NE("", android::base::GetExecutablePath());
}
+
+TEST(file, Basename) {
+ EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
+ EXPECT_EQ("sh", android::base::Basename("sh"));
+ EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
+}
+
+TEST(file, Dirname) {
+ EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
+ EXPECT_EQ(".", android::base::Dirname("sh"));
+ EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
+}
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index cd64262..c7e094e 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -52,6 +52,11 @@
std::string GetExecutablePath();
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string Basename(const std::string& path);
+std::string Dirname(const std::string& path);
+
} // namespace base
} // namespace android