1
1
/*
2
2
This source file is part of the Swift.org open source project
3
3
4
- Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
4
+ Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5
5
Licensed under Apache License v2.0 with Runtime Library Exception
6
6
7
7
See http://swift.org/LICENSE.txt for license information
@@ -32,7 +32,6 @@ import var Foundation.NSLocalizedDescriptionKey
32
32
/// - Removing `.` path components
33
33
/// - Removing any trailing path separator
34
34
/// - Removing any redundant path separators
35
- /// - Converting the disk designator to uppercase (Windows) i.e. c:\ to C:\
36
35
///
37
36
/// This string manipulation may change the meaning of a path if any of the
38
37
/// path components are symbolic links on disk. However, the file system is
@@ -507,30 +506,21 @@ private struct WindowsPath: Path, Sendable {
507
506
var components : [ String ] {
508
507
let normalized : UnsafePointer < Int8 > = string. fileSystemRepresentation
509
508
defer { normalized. deallocate ( ) }
510
- // Remove prefix from the components, allowing for comparison across normalized paths.
511
- var prefixStrippedPath = PathCchStripPrefix ( String ( cString: normalized) )
512
- // The '\\.\'' prefix is not removed by PathCchStripPrefix do this manually.
513
- if prefixStrippedPath. starts ( with: #"\\.\"# ) {
514
- prefixStrippedPath = String ( prefixStrippedPath. dropFirst ( 4 ) )
515
- }
516
- return prefixStrippedPath. components ( separatedBy: #"\"# ) . filter { !$0. isEmpty }
509
+
510
+ return String ( cString: normalized) . components ( separatedBy: " \\ " ) . filter { !$0. isEmpty }
517
511
}
518
512
519
513
var parentDirectory : Self {
520
514
return self == . root ? self : Self ( string: dirname)
521
515
}
522
516
523
517
init ( string: String ) {
524
- let noPrefixPath = PathCchStripPrefix ( string)
525
- let prefix = string. replacingOccurrences ( of: noPrefixPath, with: " " ) // Just the prefix or empty
526
-
527
- // Perform drive designator normalization i.e. 'c:\' to 'C:\' on string.
528
- if noPrefixPath. first? . isASCII ?? false , noPrefixPath. first? . isLetter ?? false , noPrefixPath. first? . isLowercase ?? false ,
529
- noPrefixPath. count > 1 , noPrefixPath [ noPrefixPath. index ( noPrefixPath. startIndex, offsetBy: 1 ) ] == " : "
518
+ if string. first? . isASCII ?? false , string. first? . isLetter ?? false , string. first? . isLowercase ?? false ,
519
+ string. count > 1 , string [ string. index ( string. startIndex, offsetBy: 1 ) ] == " : "
530
520
{
531
- self . string = " \( prefix ) \( noPrefixPath . first!. uppercased ( ) ) \( noPrefixPath . dropFirst ( 1 ) ) "
521
+ self . string = " \( string . first!. uppercased ( ) ) \( string . dropFirst ( 1 ) ) "
532
522
} else {
533
- self . string = prefix + noPrefixPath
523
+ self . string = string
534
524
}
535
525
}
536
526
@@ -546,13 +536,7 @@ private struct WindowsPath: Path, Sendable {
546
536
if !Self. isAbsolutePath ( realpath) {
547
537
throw PathValidationError . invalidAbsolutePath ( path)
548
538
}
549
- do {
550
- let canonicalizedPath = try canonicalPathRepresentation ( realpath)
551
- let normalizedPath = PathCchRemoveBackslash ( canonicalizedPath) // AbsolutePath states paths have no trailing separator.
552
- self . init ( string: normalizedPath)
553
- } catch {
554
- throw PathValidationError . invalidAbsolutePath ( " \( path) : \( error) " )
555
- }
539
+ self . init ( string: realpath)
556
540
}
557
541
558
542
init ( validatingRelativePath path: String ) throws {
@@ -570,20 +554,12 @@ private struct WindowsPath: Path, Sendable {
570
554
571
555
func suffix( withDot: Bool ) -> String ? {
572
556
return self . string. withCString ( encodedAs: UTF16 . self) {
573
- if let dotPointer = PathFindExtensionW ( $0) {
574
- // If the dotPointer is the same as the full path, there are no components before
575
- // the suffix and therefore there is no suffix.
576
- if dotPointer == $0 {
577
- return nil
578
- }
579
- let substring = String ( decodingCString: dotPointer, as: UTF16 . self)
580
- // Substring must have a dot and one more character to be considered a suffix
581
- guard substring. length > 1 else {
582
- return nil
583
- }
584
- return withDot ? substring : String ( substring. dropFirst ( 1 ) )
585
- }
586
- return nil
557
+ if let pointer = PathFindExtensionW ( $0) {
558
+ let substring = String ( decodingCString: pointer, as: UTF16 . self)
559
+ guard substring. length > 0 else { return nil }
560
+ return withDot ? substring : String ( substring. dropFirst ( 1 ) )
561
+ }
562
+ return nil
587
563
}
588
564
}
589
565
@@ -609,111 +585,6 @@ private struct WindowsPath: Path, Sendable {
609
585
return Self ( string: String ( decodingCString: result!, as: UTF16 . self) )
610
586
}
611
587
}
612
-
613
- fileprivate func HRESULT_CODE( _ hr: HRESULT ) -> DWORD {
614
- DWORD ( hr) & 0xFFFF
615
- }
616
-
617
- @inline ( __always)
618
- fileprivate func HRESULT_FACILITY( _ hr: HRESULT ) -> DWORD {
619
- DWORD ( hr << 16 ) & 0x1FFF
620
- }
621
-
622
- @inline ( __always)
623
- fileprivate func SUCCEEDED( _ hr: HRESULT ) -> Bool {
624
- hr >= 0
625
- }
626
-
627
- // This is a non-standard extension to the Windows SDK that allows us to convert
628
- // an HRESULT to a Win32 error code.
629
- @inline ( __always)
630
- fileprivate func WIN32_FROM_HRESULT( _ hr: HRESULT ) -> DWORD {
631
- if SUCCEEDED ( hr) { return DWORD ( ERROR_SUCCESS) }
632
- if HRESULT_FACILITY ( hr) == FACILITY_WIN32 {
633
- return HRESULT_CODE ( hr)
634
- }
635
- return DWORD ( hr)
636
- }
637
-
638
- /// Create a canonicalized path representation for Windows.
639
- /// Returns a potentially `\\?\`-prefixed version of the path,
640
- /// to ensure long paths greater than MAX_PATH (260) characters are handled correctly.
641
- ///
642
- /// - seealso: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
643
- fileprivate func canonicalPathRepresentation( _ path: String ) throws -> String {
644
- return try path. withCString ( encodedAs: UTF16 . self) { pwszPlatformPath in
645
- // 1. Normalize the path first.
646
- // Contrary to the documentation, this works on long paths independently
647
- // of the registry or process setting to enable long paths (but it will also
648
- // not add the \\?\ prefix required by other functions under these conditions).
649
- let dwLength : DWORD = GetFullPathNameW ( pwszPlatformPath, 0 , nil , nil )
650
-
651
- return try withUnsafeTemporaryAllocation ( of: WCHAR . self, capacity: Int ( dwLength) ) { pwszFullPath in
652
- guard ( 1 ..< dwLength) . contains ( GetFullPathNameW ( pwszPlatformPath, DWORD ( pwszFullPath. count) , pwszFullPath. baseAddress, nil ) ) else {
653
- throw Win32Error ( GetLastError ( ) )
654
- }
655
- // 1.5 Leave \\.\ prefixed paths alone since device paths are already an exact representation and PathCchCanonicalizeEx will mangle these.
656
- if pwszFullPath. count >= 4 {
657
- if let base = pwszFullPath. baseAddress,
658
- base [ 0 ] == UInt8 ( ascii: " \\ " ) ,
659
- base [ 1 ] == UInt8 ( ascii: " \\ " ) ,
660
- base [ 2 ] == UInt8 ( ascii: " . " ) ,
661
- base [ 3 ] == UInt8 ( ascii: " \\ " )
662
- {
663
- return String ( decodingCString: base, as: UTF16 . self)
664
- }
665
- }
666
- // 2. Canonicalize the path.
667
- // This will add the \\?\ prefix if needed based on the path's length.
668
- var pwszCanonicalPath : LPWSTR ?
669
- let flags : ULONG = numericCast ( PATHCCH_ALLOW_LONG_PATHS . rawValue)
670
- let result = PathAllocCanonicalize ( pwszFullPath. baseAddress, flags, & pwszCanonicalPath)
671
- if let pwszCanonicalPath {
672
- defer { LocalFree ( pwszCanonicalPath) }
673
- if result == S_OK {
674
- // 3. Perform the operation on the normalized path.
675
- return String ( decodingCString: pwszCanonicalPath, as: UTF16 . self)
676
- }
677
- }
678
- throw Win32Error ( WIN32_FROM_HRESULT ( result) )
679
- }
680
- }
681
- }
682
-
683
- /// Removes the "\\?\" prefix, if present, from a file path. When this function returns successfully,
684
- /// the same path string will have the prefix removed,if the prefix was present.
685
- /// If no prefix was present,the string will be unchanged.
686
- fileprivate func PathCchStripPrefix( _ path: String ) -> String {
687
- return path. withCString ( encodedAs: UTF16 . self) { cStringPtr in
688
- withUnsafeTemporaryAllocation ( of: WCHAR . self, capacity: path. utf16. count + 1 ) { buffer in
689
- buffer. initialize ( from: UnsafeBufferPointer ( start: cStringPtr, count: path. utf16. count + 1 ) )
690
- let result = PathCchStripPrefix ( buffer. baseAddress!, buffer. count)
691
- if result == S_OK {
692
- return String ( decodingCString: buffer. baseAddress!, as: UTF16 . self)
693
- }
694
- return path
695
- }
696
- }
697
- }
698
-
699
- /// Remove a trailing backslash from a path if the following conditions
700
- /// are true:
701
- /// * Path is not a root path
702
- /// * Pash has a trailing backslash
703
- /// If conditions are not met then the string is returned unchanged.
704
- fileprivate func PathCchRemoveBackslash( _ path: String ) -> String {
705
- return path. withCString ( encodedAs: UTF16 . self) { cStringPtr in
706
- return withUnsafeTemporaryAllocation ( of: WCHAR . self, capacity: path. utf16. count + 1 ) { buffer in
707
- buffer. initialize ( from: UnsafeBufferPointer ( start: cStringPtr, count: path. utf16. count + 1 ) )
708
- let result = PathCchRemoveBackslash ( buffer. baseAddress!, path. utf16. count + 1 )
709
- if result == S_OK {
710
- return String ( decodingCString: buffer. baseAddress!, as: UTF16 . self)
711
- }
712
- return path
713
- }
714
- return path
715
- }
716
- }
717
588
#else
718
589
private struct UNIXPath : Path , Sendable {
719
590
let string : String
@@ -1095,8 +966,7 @@ extension AbsolutePath {
1095
966
}
1096
967
}
1097
968
1098
- assert ( AbsolutePath ( base, result) == self , " base: \( base) result: \( result) self: \( self ) " )
1099
-
969
+ assert ( AbsolutePath ( base, result) == self )
1100
970
return result
1101
971
}
1102
972
0 commit comments