@@ -145,6 +145,45 @@ const char* FilePath::FindLastPathSeparator() const {
145
145
return last_sep;
146
146
}
147
147
148
+ size_t FilePath::CalculateRootLength () const {
149
+ const auto &path = pathname_;
150
+ auto s = path.begin ();
151
+ auto end = path.end ();
152
+ #if GTEST_OS_WINDOWS
153
+ if (end - s >= 2 && s[1 ] == ' :' &&
154
+ (end - s == 2 || IsPathSeparator (s[2 ])) &&
155
+ ((' A' <= s[0 ] && s[0 ] <= ' Z' ) || (' a' <= s[0 ] && s[0 ] <= ' z' ))) {
156
+ // A typical absolute path like "C:\Windows" or "D:"
157
+ s += 2 ;
158
+ if (s != end) {
159
+ ++s;
160
+ }
161
+ } else if (end - s >= 3 && IsPathSeparator (*s) && IsPathSeparator (*(s + 1 ))
162
+ && !IsPathSeparator (*(s + 2 ))) {
163
+ // Move past the "\\" prefix in a UNC path like "\\Server\Share\Folder"
164
+ s += 2 ;
165
+ // Skip 2 components and their following separators ("Server\" and "Share\")
166
+ for (int i = 0 ; i < 2 ; ++i) {
167
+ while (s != end) {
168
+ bool stop = IsPathSeparator (*s);
169
+ ++s;
170
+ if (stop) {
171
+ break ;
172
+ }
173
+ }
174
+ }
175
+ } else if (s != end && IsPathSeparator (*s)) {
176
+ // A drive-rooted path like "\Windows"
177
+ ++s;
178
+ }
179
+ #else
180
+ if (s != end && IsPathSeparator (*s)) {
181
+ ++s;
182
+ }
183
+ #endif
184
+ return static_cast <size_t >(s - path.begin ());
185
+ }
186
+
148
187
// Returns a copy of the FilePath with the directory part removed.
149
188
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
150
189
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
@@ -246,26 +285,16 @@ bool FilePath::DirectoryExists() const {
246
285
}
247
286
248
287
// Returns true if pathname describes a root directory. (Windows has one
249
- // root directory per disk drive.)
288
+ // root directory per disk drive. UNC share roots are also included. )
250
289
bool FilePath::IsRootDirectory () const {
251
- #if GTEST_OS_WINDOWS
252
- return pathname_.length () == 3 && IsAbsolutePath ();
253
- #else
254
- return pathname_.length () == 1 && IsPathSeparator (pathname_.c_str ()[0 ]);
255
- #endif
290
+ size_t root_length = CalculateRootLength ();
291
+ return root_length > 0 && root_length == pathname_.size () &&
292
+ IsPathSeparator (pathname_[root_length - 1 ]);
256
293
}
257
294
258
295
// Returns true if pathname describes an absolute path.
259
296
bool FilePath::IsAbsolutePath () const {
260
- const char * const name = pathname_.c_str ();
261
- #if GTEST_OS_WINDOWS
262
- return pathname_.length () >= 3 &&
263
- ((name[0 ] >= ' a' && name[0 ] <= ' z' ) ||
264
- (name[0 ] >= ' A' && name[0 ] <= ' Z' )) &&
265
- name[1 ] == ' :' && IsPathSeparator (name[2 ]);
266
- #else
267
- return IsPathSeparator (name[0 ]);
268
- #endif
297
+ return CalculateRootLength () > 0 ;
269
298
}
270
299
271
300
// Returns a pathname for a file that does not currently exist. The pathname
@@ -347,17 +376,27 @@ FilePath FilePath::RemoveTrailingPathSeparator() const {
347
376
// Removes any redundant separators that might be in the pathname.
348
377
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
349
378
// redundancies that might be in a pathname involving "." or "..".
379
+ // Note that "\\Host\Share" does not contain a redundancy on Windows!
350
380
void FilePath::Normalize () {
351
381
auto out = pathname_.begin ();
352
382
353
- for (const char character : pathname_) {
383
+ auto i = pathname_.cbegin ();
384
+ #if GTEST_OS_WINDOWS
385
+ // UNC paths are treated specially
386
+ if (pathname_.end () - i >= 3 && IsPathSeparator (*i) &&
387
+ IsPathSeparator (*(i + 1 )) && !IsPathSeparator (*(i + 2 ))) {
388
+ *(out++) = kPathSeparator ;
389
+ *(out++) = kPathSeparator ;
390
+ }
391
+ #endif
392
+ while (i != pathname_.end ()) {
393
+ const char character = *i;
354
394
if (!IsPathSeparator (character)) {
355
395
*(out++) = character;
356
396
} else if (out == pathname_.begin () || *std::prev (out) != kPathSeparator ) {
357
397
*(out++) = kPathSeparator ;
358
- } else {
359
- continue ;
360
398
}
399
+ ++i;
361
400
}
362
401
363
402
pathname_.erase (out, pathname_.end ());
0 commit comments