11/*
22 This source file is part of the Swift.org open source project
33
4- Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
4+ Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
55 Licensed under Apache License v2.0 with Runtime Library Exception
66
77 See http://swift.org/LICENSE.txt for license information
@@ -32,7 +32,6 @@ import var Foundation.NSLocalizedDescriptionKey
3232/// - Removing `.` path components
3333/// - Removing any trailing path separator
3434/// - Removing any redundant path separators
35- /// - Converting the disk designator to uppercase (Windows) i.e. c:\ to C:\
3635///
3736/// This string manipulation may change the meaning of a path if any of the
3837/// path components are symbolic links on disk. However, the file system is
@@ -507,30 +506,21 @@ private struct WindowsPath: Path, Sendable {
507506 var components : [ String ] {
508507 let normalized : UnsafePointer < Int8 > = string. fileSystemRepresentation
509508 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 }
517511 }
518512
519513 var parentDirectory : Self {
520514 return self == . root ? self : Self ( string: dirname)
521515 }
522516
523517 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 ) ] == " : "
530520 {
531- self . string = " \( prefix ) \( noPrefixPath . first!. uppercased ( ) ) \( noPrefixPath . dropFirst ( 1 ) ) "
521+ self . string = " \( string . first!. uppercased ( ) ) \( string . dropFirst ( 1 ) ) "
532522 } else {
533- self . string = prefix + noPrefixPath
523+ self . string = string
534524 }
535525 }
536526
@@ -546,13 +536,7 @@ private struct WindowsPath: Path, Sendable {
546536 if !Self. isAbsolutePath ( realpath) {
547537 throw PathValidationError . invalidAbsolutePath ( path)
548538 }
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)
556540 }
557541
558542 init ( validatingRelativePath path: String ) throws {
@@ -570,20 +554,12 @@ private struct WindowsPath: Path, Sendable {
570554
571555 func suffix( withDot: Bool ) -> String ? {
572556 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
587563 }
588564 }
589565
@@ -609,111 +585,6 @@ private struct WindowsPath: Path, Sendable {
609585 return Self ( string: String ( decodingCString: result!, as: UTF16 . self) )
610586 }
611587}
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- }
717588#else
718589private struct UNIXPath : Path , Sendable {
719590 let string : String
@@ -1095,8 +966,7 @@ extension AbsolutePath {
1095966 }
1096967 }
1097968
1098- assert ( AbsolutePath ( base, result) == self , " base: \( base) result: \( result) self: \( self ) " )
1099-
969+ assert ( AbsolutePath ( base, result) == self )
1100970 return result
1101971 }
1102972
0 commit comments