]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/tools/build/src/engine/pathsys.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / tools / build / src / engine / pathsys.cpp
index 61bacd6b1f26f4b3c1a3b91b4ca878a5db62b75d..00687d159086580347083157c341ca6fa4946835 100644 (file)
@@ -35,6 +35,8 @@
 #include <stdlib.h>
 #include <time.h>
 
+#include <algorithm>
+
 
 /* Internal OS specific implementation details - have names ending with an
  * underscore and are expected to be implemented in an OS specific pathXXX.c
@@ -300,3 +302,65 @@ int path_translate_to_os( char const * f, string * file )
 {
   return path_translate_to_os_( f, file );
 }
+
+
+std::string b2::paths::normalize(const std::string &p)
+{
+    // We root the path as a sentinel. But we need to remember that we did so
+    // to un-root afterwards.
+    std::string result{"/"};
+    bool is_rooted = p[0] == '/' || p[0] == '\\';
+    result += p;
+
+    // Convert \ into /. On Windows, paths using / and \ are equivalent, and we
+    // want this function to obtain a canonic representation.
+    std::replace(result.begin(), result.end(), '\\', '/');
+
+    size_t ellipsis = 0;
+    for (auto end_pos = result.length(); end_pos > 0; )
+    {
+        auto path_pos = result.rfind('/', end_pos-1);
+        if (path_pos == std::string::npos) break;
+        if (path_pos == end_pos-1)
+        {
+            /* Found a trailing or duplicate '/'. Remove it. */
+            result.erase(path_pos, 1);
+        }
+        else if ((end_pos-path_pos == 2) && result[path_pos+1] == '.')
+        {
+            /* Found '/.'. Remove them all. */
+            result.erase(path_pos, 2);
+        }
+        else if ((end_pos-path_pos == 3) && result[path_pos+1] == '.' && result[path_pos+2] == '.')
+        {
+            /* Found '/..'. Remove them all. */
+            result.erase(path_pos, 3);
+            ellipsis += 1;
+        }
+        else if (ellipsis > 0)
+        {
+            /* An elided parent path. Remove it. */
+            result.erase(path_pos, end_pos-path_pos);
+            ellipsis -= 1;
+        }
+        end_pos = path_pos;
+    }
+
+    // Now we know that we need to add exactly ellipsis '..' path elements to the
+    // front and that our string is either empty or has a '/' as its first
+    // significant character. If we have any ellipsis remaining then the passed
+    // path must not have been rooted or else it is invalid we return empty.
+    if (ellipsis > 0)
+    {
+        if (is_rooted) return "";
+        do result.insert(0, "/.."); while (--ellipsis > 0);
+    }
+
+    // If we reduced to nothing we return a valid path depending on wether
+    // the input was rooted or not.
+    if (result.empty()) return is_rooted ? "/" : ".";
+    // Return the result without the sentinel if it's not rooted.
+    if (!is_rooted) return result.substr(1);
+
+    return result;
+}