Support/PathV2: Implement reverse iteration and parent_path.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@120496 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/llvm/Support/PathV2.h b/include/llvm/Support/PathV2.h
index fcdeff9..e1b0a1d 100644
--- a/include/llvm/Support/PathV2.h
+++ b/include/llvm/Support/PathV2.h
@@ -81,16 +81,21 @@
   typedef value_type *pointer;
   typedef std::bidirectional_iterator_tag iterator_category;
 
-  reference operator*() const;
-  pointer   operator->() const;
+  reference operator*() const { return Component; }
+  pointer   operator->() const { return &Component; }
   const_iterator &operator++();    // preincrement
   const_iterator &operator++(int); // postincrement
   const_iterator &operator--();    // predecrement
   const_iterator &operator--(int); // postdecrement
   bool operator==(const const_iterator &RHS) const;
   bool operator!=(const const_iterator &RHS) const;
+
+  /// @brief Difference in bytes between this and RHS.
+  ptrdiff_t operator-(const const_iterator &RHS) const;
 };
 
+typedef std::reverse_iterator<const_iterator> reverse_iterator;
+
 /// @brief Get begin iterator over \a path.
 /// @param path Input path.
 /// @returns Iterator initialized with the first component of \a path.
@@ -101,6 +106,20 @@
 /// @returns Iterator initialized to the end of \a path.
 const_iterator end(const StringRef &path);
 
+/// @brief Get reverse begin iterator over \a path.
+/// @param path Input path.
+/// @returns Iterator initialized with the first reverse component of \a path.
+inline reverse_iterator rbegin(const StringRef &path) {
+  return reverse_iterator(end(path));
+}
+
+/// @brief Get reverse end iterator over \a path.
+/// @param path Input path.
+/// @returns Iterator initialized to the reverse end of \a path.
+inline reverse_iterator rend(const StringRef &path) {
+  return reverse_iterator(begin(path));
+}
+
 /// @}
 /// @name Lexical Modifiers
 /// @{
diff --git a/lib/Support/PathV2.cpp b/lib/Support/PathV2.cpp
index 115f294..a97f3ec 100644
--- a/lib/Support/PathV2.cpp
+++ b/lib/Support/PathV2.cpp
@@ -82,6 +82,78 @@
 
     return StringRef();
   }
+
+  size_t filename_pos(const StringRef &str) {
+    if (str.size() == 2 &&
+        is_separator(str[0]) &&
+        is_separator(str[1]))
+      return 0;
+
+    if (str.size() > 0 && is_separator(str[str.size() - 1]))
+      return str.size() - 1;
+
+    size_t pos = str.find_last_of(separators, str.size() - 1);
+
+#ifdef LLVM_ON_WIN32
+    if (pos == StringRef::npos)
+      pos = str.find_last_of(':', str.size() - 2);
+#endif
+
+    if (pos == StringRef::npos ||
+        (pos == 1 && is_separator(str[0])))
+      return 0;
+
+    return pos + 1;
+  }
+
+  size_t root_dir_start(const StringRef &str) {
+    // case "c:/"
+#ifdef LLVM_ON_WIN32
+    if (str.size() > 2 &&
+        str[1] == ':' &&
+        is_separator(str[2]))
+      return 2;
+#endif
+
+    // case "//"
+    if (str.size() == 2 &&
+        is_separator(str[0]) &&
+        str[0] == str[1])
+      return StringRef::npos;
+
+    // case "//net"
+    if (str.size() > 3 &&
+        is_separator(str[0]) &&
+        str[0] == str[1] &&
+        !is_separator(str[2])) {
+      return str.find_first_of(separators, 2);
+    }
+
+    // case "/"
+    if (str.size() > 0 && is_separator(str[0]))
+      return 0;
+
+    return StringRef::npos;
+  }
+
+  size_t parent_path_end(const StringRef &path) {
+    size_t end_pos = filename_pos(path);
+
+    bool filename_was_sep = path.size() > 0 && is_separator(path[end_pos]);
+
+    // Skip separators except for root dir.
+    size_t root_dir_pos = root_dir_start(StringRef(path.begin(), end_pos));
+
+    while(end_pos > 0 &&
+          (end_pos - 1) != root_dir_pos &&
+          is_separator(path[end_pos - 1]))
+      --end_pos;
+
+    if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep)
+      return StringRef::npos;
+
+    return end_pos;
+  }
 }
 
 namespace llvm {
@@ -103,14 +175,6 @@
   return i;
 }
 
-const_iterator::reference const_iterator::operator*() const {
-  return Component;
-}
-
-const_iterator::pointer const_iterator::operator->() const {
-  return &Component;
-}
-
 const_iterator &const_iterator::operator++() {
   assert(Position < Path.size() && "Tried to increment past end!");
 
@@ -166,6 +230,36 @@
   return *this;
 }
 
+const_iterator &const_iterator::operator--() {
+  // If we're at the end and the previous char was a '/', return '.'.
+  if (Position == Path.size() &&
+      Path.size() > 1 &&
+      is_separator(Path[Position - 1])
+#ifdef LLVM_ON_WIN32
+      && Path[Position - 2] != ':'
+#endif
+      ) {
+    --Position;
+    Component = ".";
+    return *this;
+  }
+
+  // Skip separators unless it's the root directory.
+  size_t root_dir_pos = root_dir_start(Path);
+  size_t end_pos = Position;
+
+  while(end_pos > 0 &&
+        (end_pos - 1) != root_dir_pos &&
+        is_separator(Path[end_pos - 1]))
+    --end_pos;
+
+  // Find next separator.
+  size_t start_pos = filename_pos(StringRef(Path.begin(), end_pos));
+  Component = StringRef(Path.begin() + start_pos, end_pos - start_pos);
+  Position = start_pos;
+  return *this;
+}
+
 bool const_iterator::operator==(const const_iterator &RHS) const {
   return Path.begin() == RHS.Path.begin() &&
          Position == RHS.Position;
@@ -175,6 +269,10 @@
   return !(*this == RHS);
 }
 
+ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const {
+  return Position - RHS.Position;
+}
+
 error_code root_path(const StringRef &path, StringRef &result) {
   const_iterator b = begin(path),
                  pos = b,
@@ -396,6 +494,15 @@
                    "occurred above!");
 }
 
+error_code parent_path(const StringRef &path, StringRef &result) {
+  size_t end_pos = parent_path_end(path);
+  if (end_pos == StringRef::npos)
+    result = StringRef();
+  else
+    result = StringRef(path.data(), end_pos);
+  return make_error_code(errc::success);
+}
+
 }
 }
 }
diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp
index 2c7c3cb..b8f818f 100644
--- a/unittests/Support/Path.cpp
+++ b/unittests/Support/Path.cpp
@@ -71,6 +71,15 @@
     }
     outs() << "]\n";
 
+    outs() << "    Reverse Iteration: [";
+    for (sys::path::reverse_iterator ci = sys::path::rbegin(*i),
+                                     ce = sys::path::rend(*i);
+                                     ci != ce;
+                                     ++ci) {
+      outs() << *ci << ',';
+    }
+    outs() << "]\n";
+
     StringRef res;
     SmallString<16> temp_store;
     if (error_code ec = sys::path::root_path(*i, res))
@@ -82,6 +91,9 @@
     if (error_code ec = sys::path::root_directory(*i, res))
       ASSERT_FALSE(ec.message().c_str());
     outs() << "    root_directory: " << res << '\n';
+    if (error_code ec = sys::path::parent_path(*i, res))
+      ASSERT_FALSE(ec.message().c_str());
+    outs() << "    parent_path: " << res << '\n';
 
     temp_store = *i;
     if (error_code ec = sys::path::make_absolute(temp_store))