commit 6d9059656c08bff714b51d4ed0b56893d17b5d36
parent 1b2d79ede5f7001e98607dd35f440eef487e16e2
Author: cfillion <cfillion@users.noreply.github.com>
Date: Tue, 26 Nov 2019 15:34:02 -0800
win32: add support for UNC paths [p=2207357]
Diffstat:
3 files changed, 94 insertions(+), 59 deletions(-)
diff --git a/src/path.cpp b/src/path.cpp
@@ -40,50 +40,54 @@ const Path Path::REGISTRY = Path::DATA + "registry.db";
Path Path::s_root;
-static std::vector<std::string> Split(const std::string &input, bool *absolute)
+static std::vector<std::string> Split(const std::string &input, int *attributes)
{
std::vector<std::string> list;
+ *attributes = 0;
- const auto append = [&list, absolute] (const std::string &part) {
- if(part.empty() || part == DOT)
- return;
+ size_t last = 0;
-#ifdef _WIN32
- if(list.empty() && part.size() == 2 && isalpha(part[0]) && part[1] == ':')
- *absolute = true;
-#else
- (void)absolute;
-#endif
-
- list.push_back(part);
- };
-
- size_t last = 0, size = input.size();
-
- while(last < size) {
+ while(last < input.size()) {
const size_t pos = input.find_first_of("\\/", last);
- if(pos == std::string::npos) {
- append(input.substr(last));
- break;
- }
- else if(last + pos == 0) {
+ if(last + pos == 0) {
#ifndef _WIN32
- *absolute = true;
+ *attributes |= Path::Absolute;
#endif
last++;
continue;
}
+ else if(last == pos) {
+#ifdef _WIN32
+ if(pos == 1)
+ *attributes |= Path::UNC | Path::Absolute;
+#endif
+ last++;
+ continue;
+ }
+
+ std::string part;
- append(input.substr(last, pos - last));
+ if(pos == std::string::npos)
+ part = input.substr(last);
+ else
+ part = input.substr(last, pos - last);
+
+#ifdef _WIN32
+ if(list.empty() && part.size() == 2 && part[1] == ':' && isalpha(part[0]))
+ *attributes |= Path::Absolute;
+#endif
- last = pos + 1;
+ if(part != DOT)
+ list.push_back(part);
+
+ last += part.size() + 1;
}
return list;
}
-Path::Path(const std::string &path) : m_absolute(false)
+Path::Path(const std::string &path) : m_attributes{}
{
append(path);
}
@@ -93,11 +97,11 @@ void Path::append(const std::string &input, const bool traversal)
if(input.empty())
return;
- bool absolute = false;
- const auto &parts = Split(input, &absolute);
+ int attributes;
+ const auto &parts = Split(input, &attributes);
- if(m_parts.empty() && absolute)
- m_absolute = true;
+ if(m_parts.empty() && attributes)
+ m_attributes = attributes;
for(const std::string &part : parts) {
if(part == DOTDOT) {
@@ -111,8 +115,8 @@ void Path::append(const std::string &input, const bool traversal)
void Path::append(const Path &o)
{
- if(m_parts.empty() && o.absolute())
- m_absolute = true;
+ if(m_parts.empty())
+ m_attributes = o.attributes();
m_parts.insert(m_parts.end(), o.m_parts.begin(), o.m_parts.end());
}
@@ -137,8 +141,8 @@ void Path::remove(const size_t pos, size_t count)
m_parts.erase(begin, end);
- if(!pos && m_absolute)
- m_absolute = false;
+ if(!pos)
+ m_attributes = 0;
}
void Path::removeLast()
@@ -177,27 +181,33 @@ std::string Path::join(const bool nativeSeparator) const
{
const char sep = nativeSeparator ? NATIVE_SEPARATOR : UNIX_SEPARATOR;
-#ifdef _WIN32
- constexpr bool absoluteSlash = false;
-#else
- const bool absoluteSlash = m_absolute;
-#endif
-
std::string path;
+#ifndef _WIN32
+ if(test(Absolute))
+ path += sep;
+#endif
+
for(const std::string &part : m_parts) {
- if(!path.empty() || absoluteSlash)
+#ifdef _WIN32
+ if(!path.empty())
+#else
+ if(path.size() > test(Absolute))
+#endif
path += sep;
path += part;
}
- if(m_parts.empty() && absoluteSlash)
- path += sep;
-
#ifdef _WIN32
- if(m_absolute && path.size() > MAX_PATH)
+ if(test(Absolute) && path.size() > MAX_PATH) {
path.insert(0, "\\\\?\\");
+
+ if(test(UNC))
+ path.insert(4, "UNC\\");
+ }
+ else if(test(UNC))
+ path.insert(0, "\\\\");
#endif
return path;
@@ -205,7 +215,7 @@ std::string Path::join(const bool nativeSeparator) const
bool Path::startsWith(const Path &o) const
{
- if(size() < o.size() || absolute() != o.absolute())
+ if(m_parts.size() < o.size() || m_attributes != o.attributes())
return false;
for(size_t i = 0; i < o.size(); i++) {
@@ -218,7 +228,7 @@ bool Path::startsWith(const Path &o) const
Path Path::prependRoot() const
{
- return m_absolute ? *this : s_root + *this;
+ return m_attributes & Absolute ? *this : s_root + *this;
}
Path Path::removeRoot() const
@@ -233,7 +243,7 @@ Path Path::removeRoot() const
bool Path::operator==(const Path &o) const
{
- return m_absolute == o.absolute() && m_parts == o.m_parts;
+ return m_attributes == o.attributes() && m_parts == o.m_parts;
}
bool Path::operator!=(const Path &o) const
diff --git a/src/path.hpp b/src/path.hpp
@@ -26,6 +26,11 @@ class UseRootPath;
class Path {
public:
+ enum Attribute {
+ Absolute = 1<<0,
+ UNC = 1<<1,
+ };
+
static const Path DATA;
static const Path CACHE;
static const Path CONFIG;
@@ -43,7 +48,8 @@ public:
bool empty() const { return m_parts.empty(); }
size_t size() const { return m_parts.size(); }
- bool absolute() const { return m_absolute; }
+ int attributes() const { return m_attributes; }
+ bool test(Attribute f) const { return (m_attributes & f) != 0; }
Path dirname() const;
std::string front() const;
@@ -74,7 +80,7 @@ private:
const std::string &at(size_t) const;
std::list<std::string> m_parts;
- bool m_absolute;
+ int m_attributes;
};
inline std::ostream &operator<<(std::ostream &os, const Path &p)
diff --git a/test/path.cpp b/test/path.cpp
@@ -157,7 +157,7 @@ TEST_CASE("split input", M) {
}
TEST_CASE("absolute path", M) {
- CHECK_FALSE(Path("a/b").absolute());
+ CHECK_FALSE(Path("a/b").test(Path::Absolute));
#ifdef _WIN32
const Path a("C:\\Windows\\System32");
@@ -165,7 +165,7 @@ TEST_CASE("absolute path", M) {
const Path a("/usr/bin/zsh");
#endif
- REQUIRE(a.absolute());
+ REQUIRE(a.test(Path::Absolute));
CHECK(a.size() == 3);
#ifdef _WIN32
@@ -184,7 +184,7 @@ TEST_CASE("absolute path (root only)", M) {
const Path a("/");
#endif
- REQUIRE(a.absolute());
+ REQUIRE(a.test(Path::Absolute));
#ifdef _WIN32
CHECK(a.size() == 1);
@@ -207,7 +207,7 @@ TEST_CASE("append absolute path to empty path", M) {
path += abs;
CHECK(path == abs);
- REQUIRE(path.absolute());
+ REQUIRE(path.test(Path::Absolute));
}
TEST_CASE("extended absolute paths", M) {
@@ -215,11 +215,11 @@ TEST_CASE("extended absolute paths", M) {
Path abs("C:\\");
abs.append(std::string(260, 'a'));
- CHECK(abs.absolute());
+ CHECK(abs.test(Path::Absolute));
REQUIRE_THAT(abs.join(), StartsWith("\\\\?\\"));
const Path path(std::string(500, 'a'));
- CHECK_FALSE(path.absolute());
+ CHECK_FALSE(path.test(Path::Absolute));
#else
Path path("/hello");
path.append(std::string(260, 'a'));
@@ -228,7 +228,26 @@ TEST_CASE("extended absolute paths", M) {
REQUIRE_THAT(path.join(), !StartsWith("\\\\?\\"));
}
-#ifndef _WIN32
+#ifdef _WIN32
+TEST_CASE("UNC path", M) {
+ const Path unc("\\\\FOO\\bar");
+ REQUIRE(unc.test(Path::Absolute));
+ REQUIRE(unc.test(Path::UNC));
+ CHECK(unc.size() == 2);
+
+ CHECK(unc[0] == "FOO");
+ CHECK(unc.join() == "\\\\FOO\\bar");
+}
+
+TEST_CASE("UNC path extended", M) {
+ Path unc("\\\\FOO");
+ unc.append(std::string(260, 'a'));
+
+ CHECK(unc.test(Path::Absolute));
+ CHECK(unc.test(Path::UNC));
+ REQUIRE_THAT(unc.join(), StartsWith("\\\\?\\UNC\\FOO"));
+}
+#else
TEST_CASE("compare absolute to relative path (unix)", M) {
REQUIRE(Path("/a/b") != Path("a/b"));
}
@@ -350,14 +369,14 @@ TEST_CASE("remove path segments", M) {
SECTION("remove from start") {
path.remove(0, 1);
REQUIRE(path == Path("b/c/d/e"));
- REQUIRE_FALSE(path.absolute());
+ REQUIRE_FALSE(path.test(Path::Absolute));
}
SECTION("remove from middle") {
path.remove(1, 2);
REQUIRE(path == Path("/a/d/e"));
#ifndef _WIN32
- REQUIRE(path.absolute());
+ REQUIRE(path.test(Path::Absolute));
#endif
}