Skip to content

Commit 6d90596

Browse files
committed
win32: add support for UNC paths [p=2207357]
1 parent 1b2d79e commit 6d90596

File tree

3 files changed

+94
-59
lines changed

3 files changed

+94
-59
lines changed

src/path.cpp

+58-48
Original file line numberDiff line numberDiff line change
@@ -40,50 +40,54 @@ const Path Path::REGISTRY = Path::DATA + "registry.db";
4040

4141
Path Path::s_root;
4242

43-
static std::vector<std::string> Split(const std::string &input, bool *absolute)
43+
static std::vector<std::string> Split(const std::string &input, int *attributes)
4444
{
4545
std::vector<std::string> list;
46+
*attributes = 0;
4647

47-
const auto append = [&list, absolute] (const std::string &part) {
48-
if(part.empty() || part == DOT)
49-
return;
48+
size_t last = 0;
5049

51-
#ifdef _WIN32
52-
if(list.empty() && part.size() == 2 && isalpha(part[0]) && part[1] == ':')
53-
*absolute = true;
54-
#else
55-
(void)absolute;
56-
#endif
57-
58-
list.push_back(part);
59-
};
60-
61-
size_t last = 0, size = input.size();
62-
63-
while(last < size) {
50+
while(last < input.size()) {
6451
const size_t pos = input.find_first_of("\\/", last);
6552

66-
if(pos == std::string::npos) {
67-
append(input.substr(last));
68-
break;
69-
}
70-
else if(last + pos == 0) {
53+
if(last + pos == 0) {
7154
#ifndef _WIN32
72-
*absolute = true;
55+
*attributes |= Path::Absolute;
7356
#endif
7457
last++;
7558
continue;
7659
}
60+
else if(last == pos) {
61+
#ifdef _WIN32
62+
if(pos == 1)
63+
*attributes |= Path::UNC | Path::Absolute;
64+
#endif
65+
last++;
66+
continue;
67+
}
68+
69+
std::string part;
7770

78-
append(input.substr(last, pos - last));
71+
if(pos == std::string::npos)
72+
part = input.substr(last);
73+
else
74+
part = input.substr(last, pos - last);
75+
76+
#ifdef _WIN32
77+
if(list.empty() && part.size() == 2 && part[1] == ':' && isalpha(part[0]))
78+
*attributes |= Path::Absolute;
79+
#endif
7980

80-
last = pos + 1;
81+
if(part != DOT)
82+
list.push_back(part);
83+
84+
last += part.size() + 1;
8185
}
8286

8387
return list;
8488
}
8589

86-
Path::Path(const std::string &path) : m_absolute(false)
90+
Path::Path(const std::string &path) : m_attributes{}
8791
{
8892
append(path);
8993
}
@@ -93,11 +97,11 @@ void Path::append(const std::string &input, const bool traversal)
9397
if(input.empty())
9498
return;
9599

96-
bool absolute = false;
97-
const auto &parts = Split(input, &absolute);
100+
int attributes;
101+
const auto &parts = Split(input, &attributes);
98102

99-
if(m_parts.empty() && absolute)
100-
m_absolute = true;
103+
if(m_parts.empty() && attributes)
104+
m_attributes = attributes;
101105

102106
for(const std::string &part : parts) {
103107
if(part == DOTDOT) {
@@ -111,8 +115,8 @@ void Path::append(const std::string &input, const bool traversal)
111115

112116
void Path::append(const Path &o)
113117
{
114-
if(m_parts.empty() && o.absolute())
115-
m_absolute = true;
118+
if(m_parts.empty())
119+
m_attributes = o.attributes();
116120

117121
m_parts.insert(m_parts.end(), o.m_parts.begin(), o.m_parts.end());
118122
}
@@ -137,8 +141,8 @@ void Path::remove(const size_t pos, size_t count)
137141

138142
m_parts.erase(begin, end);
139143

140-
if(!pos && m_absolute)
141-
m_absolute = false;
144+
if(!pos)
145+
m_attributes = 0;
142146
}
143147

144148
void Path::removeLast()
@@ -177,35 +181,41 @@ std::string Path::join(const bool nativeSeparator) const
177181
{
178182
const char sep = nativeSeparator ? NATIVE_SEPARATOR : UNIX_SEPARATOR;
179183

180-
#ifdef _WIN32
181-
constexpr bool absoluteSlash = false;
182-
#else
183-
const bool absoluteSlash = m_absolute;
184-
#endif
185-
186184
std::string path;
187185

186+
#ifndef _WIN32
187+
if(test(Absolute))
188+
path += sep;
189+
#endif
190+
188191
for(const std::string &part : m_parts) {
189-
if(!path.empty() || absoluteSlash)
192+
#ifdef _WIN32
193+
if(!path.empty())
194+
#else
195+
if(path.size() > test(Absolute))
196+
#endif
190197
path += sep;
191198

192199
path += part;
193200
}
194201

195-
if(m_parts.empty() && absoluteSlash)
196-
path += sep;
197-
198202
#ifdef _WIN32
199-
if(m_absolute && path.size() > MAX_PATH)
203+
if(test(Absolute) && path.size() > MAX_PATH) {
200204
path.insert(0, "\\\\?\\");
205+
206+
if(test(UNC))
207+
path.insert(4, "UNC\\");
208+
}
209+
else if(test(UNC))
210+
path.insert(0, "\\\\");
201211
#endif
202212

203213
return path;
204214
}
205215

206216
bool Path::startsWith(const Path &o) const
207217
{
208-
if(size() < o.size() || absolute() != o.absolute())
218+
if(m_parts.size() < o.size() || m_attributes != o.attributes())
209219
return false;
210220

211221
for(size_t i = 0; i < o.size(); i++) {
@@ -218,7 +228,7 @@ bool Path::startsWith(const Path &o) const
218228

219229
Path Path::prependRoot() const
220230
{
221-
return m_absolute ? *this : s_root + *this;
231+
return m_attributes & Absolute ? *this : s_root + *this;
222232
}
223233

224234
Path Path::removeRoot() const
@@ -233,7 +243,7 @@ Path Path::removeRoot() const
233243

234244
bool Path::operator==(const Path &o) const
235245
{
236-
return m_absolute == o.absolute() && m_parts == o.m_parts;
246+
return m_attributes == o.attributes() && m_parts == o.m_parts;
237247
}
238248

239249
bool Path::operator!=(const Path &o) const

src/path.hpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ class UseRootPath;
2626

2727
class Path {
2828
public:
29+
enum Attribute {
30+
Absolute = 1<<0,
31+
UNC = 1<<1,
32+
};
33+
2934
static const Path DATA;
3035
static const Path CACHE;
3136
static const Path CONFIG;
@@ -43,7 +48,8 @@ class Path {
4348

4449
bool empty() const { return m_parts.empty(); }
4550
size_t size() const { return m_parts.size(); }
46-
bool absolute() const { return m_absolute; }
51+
int attributes() const { return m_attributes; }
52+
bool test(Attribute f) const { return (m_attributes & f) != 0; }
4753

4854
Path dirname() const;
4955
std::string front() const;
@@ -74,7 +80,7 @@ class Path {
7480
const std::string &at(size_t) const;
7581

7682
std::list<std::string> m_parts;
77-
bool m_absolute;
83+
int m_attributes;
7884
};
7985

8086
inline std::ostream &operator<<(std::ostream &os, const Path &p)

test/path.cpp

+28-9
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,15 @@ TEST_CASE("split input", M) {
157157
}
158158

159159
TEST_CASE("absolute path", M) {
160-
CHECK_FALSE(Path("a/b").absolute());
160+
CHECK_FALSE(Path("a/b").test(Path::Absolute));
161161

162162
#ifdef _WIN32
163163
const Path a("C:\\Windows\\System32");
164164
#else
165165
const Path a("/usr/bin/zsh");
166166
#endif
167167

168-
REQUIRE(a.absolute());
168+
REQUIRE(a.test(Path::Absolute));
169169
CHECK(a.size() == 3);
170170

171171
#ifdef _WIN32
@@ -184,7 +184,7 @@ TEST_CASE("absolute path (root only)", M) {
184184
const Path a("/");
185185
#endif
186186

187-
REQUIRE(a.absolute());
187+
REQUIRE(a.test(Path::Absolute));
188188

189189
#ifdef _WIN32
190190
CHECK(a.size() == 1);
@@ -207,19 +207,19 @@ TEST_CASE("append absolute path to empty path", M) {
207207
path += abs;
208208

209209
CHECK(path == abs);
210-
REQUIRE(path.absolute());
210+
REQUIRE(path.test(Path::Absolute));
211211
}
212212

213213
TEST_CASE("extended absolute paths", M) {
214214
#ifdef _WIN32
215215
Path abs("C:\\");
216216
abs.append(std::string(260, 'a'));
217217

218-
CHECK(abs.absolute());
218+
CHECK(abs.test(Path::Absolute));
219219
REQUIRE_THAT(abs.join(), StartsWith("\\\\?\\"));
220220

221221
const Path path(std::string(500, 'a'));
222-
CHECK_FALSE(path.absolute());
222+
CHECK_FALSE(path.test(Path::Absolute));
223223
#else
224224
Path path("/hello");
225225
path.append(std::string(260, 'a'));
@@ -228,7 +228,26 @@ TEST_CASE("extended absolute paths", M) {
228228
REQUIRE_THAT(path.join(), !StartsWith("\\\\?\\"));
229229
}
230230

231-
#ifndef _WIN32
231+
#ifdef _WIN32
232+
TEST_CASE("UNC path", M) {
233+
const Path unc("\\\\FOO\\bar");
234+
REQUIRE(unc.test(Path::Absolute));
235+
REQUIRE(unc.test(Path::UNC));
236+
CHECK(unc.size() == 2);
237+
238+
CHECK(unc[0] == "FOO");
239+
CHECK(unc.join() == "\\\\FOO\\bar");
240+
}
241+
242+
TEST_CASE("UNC path extended", M) {
243+
Path unc("\\\\FOO");
244+
unc.append(std::string(260, 'a'));
245+
246+
CHECK(unc.test(Path::Absolute));
247+
CHECK(unc.test(Path::UNC));
248+
REQUIRE_THAT(unc.join(), StartsWith("\\\\?\\UNC\\FOO"));
249+
}
250+
#else
232251
TEST_CASE("compare absolute to relative path (unix)", M) {
233252
REQUIRE(Path("/a/b") != Path("a/b"));
234253
}
@@ -350,14 +369,14 @@ TEST_CASE("remove path segments", M) {
350369
SECTION("remove from start") {
351370
path.remove(0, 1);
352371
REQUIRE(path == Path("b/c/d/e"));
353-
REQUIRE_FALSE(path.absolute());
372+
REQUIRE_FALSE(path.test(Path::Absolute));
354373
}
355374

356375
SECTION("remove from middle") {
357376
path.remove(1, 2);
358377
REQUIRE(path == Path("/a/d/e"));
359378
#ifndef _WIN32
360-
REQUIRE(path.absolute());
379+
REQUIRE(path.test(Path::Absolute));
361380
#endif
362381
}
363382

0 commit comments

Comments
 (0)