@@ -458,36 +458,6 @@ private struct WindowsPath: Path, Sendable {
458
458
return !path. withCString ( encodedAs: UTF16 . self, PathIsRelativeW)
459
459
}
460
460
461
- /// When this function returns successfully, the same path string will have had the prefix removed,
462
- /// if the prefix was present. If no prefix was present, the string will be unchanged.
463
- static func stripPrefix( _ path: String ) -> String {
464
- return path. withCString ( encodedAs: UTF16 . self) { cStringPtr in
465
- let mutableCStringPtr = UnsafeMutablePointer ( mutating: cStringPtr)
466
- let result = PathCchStripPrefix ( mutableCStringPtr, path. utf16. count + 1 )
467
- if result == S_OK {
468
- return String ( decodingCString: mutableCStringPtr, as: UTF16 . self)
469
- }
470
- return path
471
- }
472
- }
473
-
474
- /// Remove a trailing backslash from a path if the following conditions
475
- /// are true:
476
- /// * Path is not a root path
477
- /// * Pash has a trailing backslash
478
- /// If conditions are not met then the string is returned unchanged.
479
- static func removeTrailingBackslash( _ path: String ) -> String {
480
- return path. withCString ( encodedAs: UTF16 . self) { cStringPtr in
481
- let mutableCStringPtr = UnsafeMutablePointer ( mutating: cStringPtr)
482
- let result = PathCchRemoveBackslash ( mutableCStringPtr, path. utf16. count + 1 )
483
-
484
- if result == S_OK {
485
- return String ( decodingCString: mutableCStringPtr, as: UTF16 . self)
486
- }
487
- return path
488
- }
489
- }
490
-
491
461
/// Create a canonicalized path representation for Windows.
492
462
/// Returns a potentially `\\?\`-prefixed version of the path,
493
463
/// to ensure long paths greater than MAX_PATH (260) characters are handled correctly.
@@ -517,7 +487,7 @@ private struct WindowsPath: Path, Sendable {
517
487
// 2. Canonicalize the path.
518
488
// This will add the \\?\ prefix if needed based on the path's length.
519
489
var pwszCanonicalPath : LPWSTR ?
520
- let flags : ULONG = numericCast ( PATHCCH_ALLOW_LONG_PATHS . rawValue) | numericCast ( PATHCCH_CANONICALIZE_SLASHES . rawValue )
490
+ let flags : ULONG = numericCast ( PATHCCH_ALLOW_LONG_PATHS . rawValue)
521
491
let result = PathAllocCanonicalize ( pwszFullPath. baseAddress, flags, & pwszCanonicalPath)
522
492
if let pwszCanonicalPath {
523
493
defer { LocalFree ( pwszCanonicalPath) }
@@ -581,15 +551,20 @@ private struct WindowsPath: Path, Sendable {
581
551
let normalized : UnsafePointer < Int8 > = string. fileSystemRepresentation
582
552
defer { normalized. deallocate ( ) }
583
553
// Remove prefix from the components, allowing for comparison across normalized paths.
584
- return Self . stripPrefix ( String ( cString: normalized) ) . components ( separatedBy: #"\"# ) . filter { !$0. isEmpty }
554
+ var prefixStrippedPath = PathCchStripPrefix ( String ( cString: normalized) )
555
+ // The '\\.\'' prefix is not removed by PathCchStripPrefix do this manually.
556
+ if prefixStrippedPath. starts ( with: #"\\.\"# ) {
557
+ prefixStrippedPath = String ( prefixStrippedPath. dropFirst ( 4 ) )
558
+ }
559
+ return prefixStrippedPath. components ( separatedBy: #"\"# ) . filter { !$0. isEmpty }
585
560
}
586
561
587
562
var parentDirectory : Self {
588
563
return self == . root ? self : Self ( string: dirname)
589
564
}
590
565
591
566
init ( string: String ) {
592
- let noPrefixPath = Self . stripPrefix ( string)
567
+ let noPrefixPath = PathCchStripPrefix ( string)
593
568
let prefix = string. replacingOccurrences ( of: noPrefixPath, with: " " ) // Just the prefix or empty
594
569
595
570
// Perform drive designator normalization i.e. 'c:\' to 'C:\' on string.
@@ -616,7 +591,7 @@ private struct WindowsPath: Path, Sendable {
616
591
}
617
592
do {
618
593
let canonicalizedPath = try Self . canonicalPathRepresentation ( realpath)
619
- let normalizedPath = Self . removeTrailingBackslash ( canonicalizedPath) // AbsolutePath states paths have no trailing separator.
594
+ let normalizedPath = PathCchRemoveBackslash ( canonicalizedPath) // AbsolutePath states paths have no trailing separator.
620
595
self . init ( string: normalizedPath)
621
596
} catch {
622
597
throw PathValidationError . invalidAbsolutePath ( " \( path) : \( error) " )
@@ -703,6 +678,40 @@ fileprivate func WIN32_FROM_HRESULT(_ hr: HRESULT) -> DWORD {
703
678
return DWORD ( hr)
704
679
}
705
680
681
+ /// Removes the "\\?\" prefix, if present, from a file path. When this function returns successfully,
682
+ /// the same path string will have the prefix removed,if the prefix was present.
683
+ /// If no prefix was present,the string will be unchanged.
684
+ func PathCchStripPrefix( _ path: String ) -> String {
685
+ return path. withCString ( encodedAs: UTF16 . self) { cStringPtr in
686
+ withUnsafeTemporaryAllocation ( of: WCHAR . self, capacity: path. utf16. count + 1 ) { buffer in
687
+ buffer. initialize ( from: UnsafeBufferPointer ( start: cStringPtr, count: path. utf16. count + 1 ) )
688
+ let result = PathCchStripPrefix ( buffer. baseAddress!, buffer. count)
689
+ if result == S_OK {
690
+ return String ( decodingCString: buffer. baseAddress!, as: UTF16 . self)
691
+ }
692
+ return path
693
+ }
694
+ }
695
+ }
696
+
697
+ /// Remove a trailing backslash from a path if the following conditions
698
+ /// are true:
699
+ /// * Path is not a root path
700
+ /// * Pash has a trailing backslash
701
+ /// If conditions are not met then the string is returned unchanged.
702
+ func PathCchRemoveBackslash( _ path: String ) -> String {
703
+ return path. withCString ( encodedAs: UTF16 . self) { cStringPtr in
704
+ return withUnsafeTemporaryAllocation ( of: WCHAR . self, capacity: path. utf16. count + 1 ) { buffer in
705
+ buffer. initialize ( from: UnsafeBufferPointer ( start: cStringPtr, count: path. utf16. count + 1 ) )
706
+ let result = PathCchRemoveBackslash ( buffer. baseAddress!, path. utf16. count + 1 )
707
+ if result == S_OK {
708
+ return String ( decodingCString: buffer. baseAddress!, as: UTF16 . self)
709
+ }
710
+ return path
711
+ }
712
+ return path
713
+ }
714
+ }
706
715
#else
707
716
private struct UNIXPath : Path , Sendable {
708
717
let string : String
0 commit comments