#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
{
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;
+}