@@ -434,12 +434,17 @@ private struct UNIXPath: Path {
434434 static let root = UNIXPath ( string: " / " )
435435
436436 static func isValidComponent( _ name: String ) -> Bool {
437+ #if os(Windows)
438+ if name. contains ( " \\ " ) {
439+ return false
440+ }
441+ #endif
437442 return name != " " && name != " . " && name != " .. " && !name. contains ( " / " )
438443 }
439444
440445#if os(Windows)
441446 static func isAbsolutePath( _ path: String ) -> Bool {
442- return !path. withCString ( encodedAs: UTF16 . self, PathIsRelativeW)
447+ return !path. prenormalized ( ) . withCString ( encodedAs: UTF16 . self, PathIsRelativeW)
443448 }
444449#endif
445450
@@ -541,13 +546,13 @@ private struct UNIXPath: Path {
541546
542547 init ( normalizingAbsolutePath path: String ) {
543548 #if os(Windows)
544- var result : PWSTR ?
549+ var result : [ WCHAR ] = Array < WCHAR > ( repeating : 0 , count : Int ( MAX_PATH + 1 ) )
545550 defer { LocalFree ( result) }
546551
547- _ = path. withCString ( encodedAs: UTF16 . self) {
548- PathAllocCanonicalize ( $0, ULONG ( PATHCCH_ALLOW_LONG_PATHS . rawValue ) , & result )
552+ _ = path. prenormalized ( ) . withCString ( encodedAs: UTF16 . self) {
553+ PathCchCanonicalize ( $0, result . length , $0 )
549554 }
550- self . init ( string: String ( decodingCString: result! , as: UTF16 . self) )
555+ self . init ( string: String ( decodingCString: result, as: UTF16 . self) )
551556 #else
552557 precondition ( path. first == " / " , " Failure normalizing \( path) , absolute paths should start with '/' " )
553558
@@ -615,7 +620,7 @@ private struct UNIXPath: Path {
615620 let pathSeparator : Character
616621#if os(Windows)
617622 pathSeparator = " \\ "
618- let path = path. replacingOccurrences ( of : " / " , with : " \\ " )
623+ let path = path. prenormalized ( )
619624#else
620625 pathSeparator = " / "
621626#endif
@@ -683,7 +688,9 @@ private struct UNIXPath: Path {
683688
684689 init ( validatingAbsolutePath path: String ) throws {
685690#if os(Windows)
686- guard path != " " else {
691+ // Explicitly handle the empty path, since retrieving
692+ // `fileSystemRepresentation` of it is illegal.
693+ guard !path. isEmpty else {
687694 throw PathValidationError . invalidAbsolutePath ( path)
688695 }
689696 let fsr : UnsafePointer < Int8 > = path. fileSystemRepresentation
@@ -708,7 +715,9 @@ private struct UNIXPath: Path {
708715
709716 init ( validatingRelativePath path: String ) throws {
710717#if os(Windows)
711- guard path != " " else {
718+ // Explicitly handle the empty path, since retrieving
719+ // `fileSystemRepresentation` of it is illegal.
720+ guard !path. isEmpty else {
712721 self . init ( normalizingRelativePath: path)
713722 return
714723 }
@@ -946,3 +955,11 @@ private func mayNeedNormalization(absolute string: String) -> Bool {
946955 }
947956 return false
948957}
958+
959+ #if os(Windows)
960+ fileprivate extension String {
961+ func prenormalized( ) -> String {
962+ return self . replacingOccurrences ( of: " / " , with: " \\ " )
963+ }
964+ }
965+ #endif
0 commit comments