Revert "Revert "Revert "Drop all path-related methods from android::String8"""

This reverts commit 4b6a7c3940bc6d5e89e15179d8d49600e6dd4857.

Reason for revert: b/299695302, b/299694769

Change-Id: I89c6719d23446f2f9ce24e5b75d321538b580431
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 4301f0e..2b72847 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -430,6 +430,31 @@
 // ---------------------------------------------------------------------------
 // Path functions
 
+static void setPathName(String8& s, const char* name) {
+    size_t len = strlen(name);
+    char* buf = s.lockBuffer(len);
+
+    memcpy(buf, name, len);
+
+    // remove trailing path separator, if present
+    if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
+    buf[len] = '\0';
+
+    s.unlockBuffer(len);
+}
+
+String8 String8::getPathLeaf(void) const
+{
+    const char* cp;
+    const char*const buf = mString;
+
+    cp = strrchr(buf, OS_PATH_SEPARATOR);
+    if (cp == nullptr)
+        return String8(*this);
+    else
+        return String8(cp+1);
+}
+
 String8 String8::getPathDir(void) const
 {
     const char* cp;
@@ -442,14 +467,40 @@
         return String8(str, cp - str);
 }
 
+String8 String8::walkPath(String8* outRemains) const
+{
+    const char* cp;
+    const char*const str = mString;
+    const char* buf = str;
+
+    cp = strchr(buf, OS_PATH_SEPARATOR);
+    if (cp == buf) {
+        // don't include a leading '/'.
+        buf = buf+1;
+        cp = strchr(buf, OS_PATH_SEPARATOR);
+    }
+
+    if (cp == nullptr) {
+        String8 res = buf != str ? String8(buf) : *this;
+        if (outRemains) *outRemains = String8("");
+        return res;
+    }
+
+    String8 res(buf, cp-buf);
+    if (outRemains) *outRemains = String8(cp+1);
+    return res;
+}
+
 /*
  * Helper function for finding the start of an extension in a pathname.
  *
  * Returns a pointer inside mString, or NULL if no extension was found.
  */
-static const char* find_extension(const char* str) {
+char* String8::find_extension(void) const
+{
     const char* lastSlash;
     const char* lastDot;
+    const char* const str = mString;
 
     // only look at the filename
     lastSlash = strrchr(str, OS_PATH_SEPARATOR);
@@ -464,16 +515,67 @@
         return nullptr;
 
     // looks good, ship it
-    return lastDot;
+    return const_cast<char*>(lastDot);
 }
 
 String8 String8::getPathExtension(void) const
 {
-    auto ext = find_extension(mString);
+    char* ext;
+
+    ext = find_extension();
     if (ext != nullptr)
         return String8(ext);
     else
         return String8("");
 }
 
+String8 String8::getBasePath(void) const
+{
+    char* ext;
+    const char* const str = mString;
+
+    ext = find_extension();
+    if (ext == nullptr)
+        return String8(*this);
+    else
+        return String8(str, ext - str);
+}
+
+String8& String8::appendPath(const char* name)
+{
+    // TODO: The test below will fail for Win32 paths. Fix later or ignore.
+    if (name[0] != OS_PATH_SEPARATOR) {
+        if (*name == '\0') {
+            // nothing to do
+            return *this;
+        }
+
+        size_t len = length();
+        if (len == 0) {
+            // no existing filename, just use the new one
+            setPathName(*this, name);
+            return *this;
+        }
+
+        // make room for oldPath + '/' + newPath
+        int newlen = strlen(name);
+
+        char* buf = lockBuffer(len+1+newlen);
+
+        // insert a '/' if needed
+        if (buf[len-1] != OS_PATH_SEPARATOR)
+            buf[len++] = OS_PATH_SEPARATOR;
+
+        memcpy(buf+len, name, newlen+1);
+        len += newlen;
+
+        unlockBuffer(len);
+
+        return *this;
+    } else {
+        setPathName(*this, name);
+        return *this;
+    }
+}
+
 }; // namespace android
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index cbce050..6f7a54f 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -68,6 +68,30 @@
                     int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
                     str1->find(str2->c_str(), start_index);
                 },
+
+                // Path handling
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getBasePath();
+                },
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getPathExtension();
+                },
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getPathLeaf();
+                },
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    str1->getPathDir();
+                },
+                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+                    std::shared_ptr<android::String8> path_out_str =
+                            std::make_shared<android::String8>();
+                    str1->walkPath(path_out_str.get());
+                    path_out_str->clear();
+                },
+                [](FuzzedDataProvider* dataProvider, android::String8* str1,
+                   android::String8*) -> void {
+                    str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
+                },
 };
 
 void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
diff --git a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
index 8881b44..46badde 100644
--- a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -704,6 +704,9 @@
    "name" : "_ZN7android7RefBaseD2Ev"
   },
   {
+   "name" : "_ZN7android7String810appendPathEPKc"
+  },
+  {
    "name" : "_ZN7android7String810lockBufferEm"
   },
   {
@@ -1142,6 +1145,15 @@
    "name" : "_ZNK7android7String810getPathDirEv"
   },
   {
+   "name" : "_ZNK7android7String811getBasePathEv"
+  },
+  {
+   "name" : "_ZNK7android7String811getPathLeafEv"
+  },
+  {
+   "name" : "_ZNK7android7String814find_extensionEv"
+  },
+  {
    "name" : "_ZNK7android7String816getPathExtensionEv"
   },
   {
@@ -1151,6 +1163,9 @@
    "name" : "_ZNK7android7String86lengthEv"
   },
   {
+   "name" : "_ZNK7android7String88walkPathEPS0_"
+  },
+  {
    "name" : "_ZNK7android8String1610startsWithEPKDs"
   },
   {
@@ -6794,6 +6809,22 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "function_name" : "android::String8::appendPath",
+   "linker_set_key" : "_ZN7android7String810appendPathEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIRN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "function_name" : "android::String8::lockBuffer",
    "linker_set_key" : "_ZN7android7String810lockBufferEm",
    "parameters" :
@@ -9073,7 +9104,6 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
-   "access" : "private",
    "function_name" : "android::String8::getPathDir",
    "linker_set_key" : "_ZNK7android7String810getPathDirEv",
    "parameters" :
@@ -9087,7 +9117,46 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
+   "function_name" : "android::String8::getBasePath",
+   "linker_set_key" : "_ZNK7android7String811getBasePathEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::getPathLeaf",
+   "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "access" : "private",
+   "function_name" : "android::String8::find_extension",
+   "linker_set_key" : "_ZNK7android7String814find_extensionEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIPc",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "function_name" : "android::String8::getPathExtension",
    "linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
    "parameters" :
@@ -9134,6 +9203,23 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
+   "function_name" : "android::String8::walkPath",
+   "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "function_name" : "android::String16::startsWith",
    "linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
    "parameters" :
diff --git a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
index e8236ea..219c766 100644
--- a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -704,6 +704,9 @@
    "name" : "_ZN7android7RefBaseD2Ev"
   },
   {
+   "name" : "_ZN7android7String810appendPathEPKc"
+  },
+  {
    "name" : "_ZN7android7String810lockBufferEj"
   },
   {
@@ -1142,6 +1145,15 @@
    "name" : "_ZNK7android7String810getPathDirEv"
   },
   {
+   "name" : "_ZNK7android7String811getBasePathEv"
+  },
+  {
+   "name" : "_ZNK7android7String811getPathLeafEv"
+  },
+  {
+   "name" : "_ZNK7android7String814find_extensionEv"
+  },
+  {
    "name" : "_ZNK7android7String816getPathExtensionEv"
   },
   {
@@ -1151,6 +1163,9 @@
    "name" : "_ZNK7android7String86lengthEv"
   },
   {
+   "name" : "_ZNK7android7String88walkPathEPS0_"
+  },
+  {
    "name" : "_ZNK7android8String1610startsWithEPKDs"
   },
   {
@@ -6790,6 +6805,22 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "function_name" : "android::String8::appendPath",
+   "linker_set_key" : "_ZN7android7String810appendPathEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIRN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "function_name" : "android::String8::lockBuffer",
    "linker_set_key" : "_ZN7android7String810lockBufferEj",
    "parameters" :
@@ -9069,7 +9100,6 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
-   "access" : "private",
    "function_name" : "android::String8::getPathDir",
    "linker_set_key" : "_ZNK7android7String810getPathDirEv",
    "parameters" :
@@ -9083,7 +9113,46 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
+   "function_name" : "android::String8::getBasePath",
+   "linker_set_key" : "_ZNK7android7String811getBasePathEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::getPathLeaf",
+   "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "access" : "private",
+   "function_name" : "android::String8::find_extension",
+   "linker_set_key" : "_ZNK7android7String814find_extensionEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIPc",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "function_name" : "android::String8::getPathExtension",
    "linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
    "parameters" :
@@ -9130,6 +9199,23 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
+   "function_name" : "android::String8::walkPath",
+   "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
    "function_name" : "android::String16::startsWith",
    "linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
    "parameters" :
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index fbb0a0a..ea25c6a 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -130,11 +130,80 @@
 
             void                toLower();
 
-private:
-            String8 getPathDir(void) const;
-            String8 getPathExtension(void) const;
 
+    /*
+     * These methods operate on the string as if it were a path name.
+     */
+
+    /*
+     * Get just the filename component.
+     *
+     * "/tmp/foo/bar.c" --> "bar.c"
+     */
+    String8 getPathLeaf(void) const;
+
+    /*
+     * Remove the last (file name) component, leaving just the directory
+     * name.
+     *
+     * "/tmp/foo/bar.c" --> "/tmp/foo"
+     * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
+     * "bar.c" --> ""
+     */
+    String8 getPathDir(void) const;
+
+    /*
+     * Retrieve the front (root dir) component.  Optionally also return the
+     * remaining components.
+     *
+     * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
+     * "/tmp" --> "tmp" (remain = "")
+     * "bar.c" --> "bar.c" (remain = "")
+     */
+    String8 walkPath(String8* outRemains = nullptr) const;
+
+    /*
+     * Return the filename extension.  This is the last '.' and any number
+     * of characters that follow it.  The '.' is included in case we
+     * decide to expand our definition of what constitutes an extension.
+     *
+     * "/tmp/foo/bar.c" --> ".c"
+     * "/tmp" --> ""
+     * "/tmp/foo.bar/baz" --> ""
+     * "foo.jpeg" --> ".jpeg"
+     * "foo." --> ""
+     */
+    String8 getPathExtension(void) const;
+
+    /*
+     * Return the path without the extension.  Rules for what constitutes
+     * an extension are described in the comment for getPathExtension().
+     *
+     * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
+     */
+    String8 getBasePath(void) const;
+
+    /*
+     * Add a component to the pathname.  We guarantee that there is
+     * exactly one path separator between the old path and the new.
+     * If there is no existing name, we just copy the new name in.
+     *
+     * If leaf is a fully qualified path (i.e. starts with '/', it
+     * replaces whatever was there before.
+     */
+    String8& appendPath(const char* leaf);
+    String8& appendPath(const String8& leaf) { return appendPath(leaf.c_str()); }
+
+    /*
+     * Like appendPath(), but does not affect this string.  Returns a new one instead.
+     */
+    String8 appendPathCopy(const char* leaf) const
+                                             { String8 p(*this); p.appendPath(leaf); return p; }
+    String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.c_str()); }
+
+private:
             status_t            real_append(const char* other, size_t numChars);
+            char*               find_extension(void) const;
 
             const char* mString;
 };