@@ -22,7 +22,7 @@ public func NSTemporaryDirectory() -> String {
22
22
guard GetTempPathW ( DWORD ( wszPath. count) , & wszPath) <= cchLength else {
23
23
preconditionFailure ( " GetTempPathW mutation race " )
24
24
}
25
- return String ( decodingCString: wszPath, as: UTF16 . self)
25
+ return String ( decodingCString: wszPath, as: UTF16 . self) . standardizingPath
26
26
#else
27
27
#if canImport(Darwin)
28
28
let safe_confstr = { ( name: Int32 , buf: UnsafeMutablePointer < Int8 > ? , len: Int ) -> Int in
@@ -348,14 +348,32 @@ extension NSString {
348
348
349
349
return result
350
350
}
351
-
351
+
352
+ #if os(Windows)
353
+ // Convert to a posix style '/' separated path
354
+ internal var unixPath : String {
355
+ var droppedPrefix = self as String
356
+ // If there is anything before the drive letter,
357
+ // e.g. "\\?\, \\host\, \??\", remove it
358
+ if isAbsolutePath, let idx = droppedPrefix. firstIndex ( of: " : " ) {
359
+ droppedPrefix. removeSubrange ( ..< droppedPrefix. index ( before: idx) )
360
+ }
361
+ let slashesConverted = String ( droppedPrefix. map ( { $0 == " \\ " ? " / " : $0 } ) )
362
+ let compressTrailing = slashesConverted. _stringByFixingSlashes ( stripTrailing: false )
363
+ return compressTrailing
364
+ }
365
+ #endif
366
+
352
367
public var standardizingPath : String {
368
+ #if os(Windows)
369
+ let expanded = unixPath. expandingTildeInPath
370
+ #else
353
371
let expanded = expandingTildeInPath
354
- var resolved = expanded. _bridgeToObjectiveC ( ) . resolvingSymlinksInPath
372
+ #endif
373
+ let resolved = expanded. _bridgeToObjectiveC ( ) . resolvingSymlinksInPath
355
374
356
375
let automount = " /var/automount "
357
- resolved = resolved. _tryToRemovePathPrefix ( automount) ?? resolved
358
- return resolved
376
+ return resolved. _tryToRemovePathPrefix ( automount) ?? resolved
359
377
}
360
378
361
379
public var resolvingSymlinksInPath : String {
@@ -554,11 +572,61 @@ extension NSString {
554
572
}
555
573
556
574
public func getFileSystemRepresentation( _ cname: UnsafeMutablePointer < Int8 > , maxLength max: Int ) -> Bool {
575
+ #if os(Windows)
576
+ let fsr = UnsafeMutablePointer< WCHAR> . allocate( capacity: max)
577
+ defer { fsr. deallocate ( ) }
578
+ guard _getFileSystemRepresentation ( fsr, maxLength: max) else { return false }
579
+ return String ( decodingCString: fsr, as: UTF16 . self) . withCString ( ) {
580
+ let chars = strnlen_s ( $0, max)
581
+ guard chars < max else { return false }
582
+ cname. assign ( from: $0, count: chars + 1 )
583
+ return true
584
+ }
585
+ #else
586
+ return _getFileSystemRepresentation ( cname, maxLength: max)
587
+ #endif
588
+ }
589
+
590
+ internal func _getFileSystemRepresentation( _ cname: UnsafeMutablePointer < NativeFSRCharType > , maxLength max: Int ) -> Bool {
557
591
guard self . length > 0 else {
558
592
return false
559
593
}
560
-
594
+ #if os(Windows)
595
+ var fsr = self . _swiftObject
596
+ let idx = fsr. startIndex
597
+
598
+ // If we have an RFC 8089 style path e.g. `/[drive-letter]:/...`, drop the
599
+ // leading /, otherwise, a leading slash indicates a rooted path on the
600
+ // drive for the current working directory
601
+ if fsr. count >= 3 && fsr [ idx] == " / " && fsr [ fsr. index ( after: idx) ] . isLetter && fsr [ fsr. index ( idx, offsetBy: 2 ) ] == " : " {
602
+ fsr. removeFirst ( )
603
+ }
604
+
605
+ // Windows APIS that go through the path parser can handle
606
+ // forward slashes in paths. However, symlinks created with
607
+ // forward slashes do not resolve properly, so we normalize
608
+ // the path separators anyways.
609
+ fsr = fsr. replacingOccurrences ( of: " / " , with: " \\ " )
610
+
611
+ // Drop trailing slashes unless it follows a drive letter. On
612
+ // Windows the path `C:\` indicates the root directory of the
613
+ // `C:` drive. The path `C:` indicates the current working
614
+ // directory on the `C:` drive.
615
+ while fsr. count > 1
616
+ && ( fsr [ fsr. index ( before: fsr. endIndex) ] == " \\ " )
617
+ && !( fsr. count == 3 && fsr [ fsr. index ( fsr. endIndex, offsetBy: - 2 ) ] == " : " ) {
618
+ fsr. removeLast ( )
619
+ }
620
+
621
+ return fsr. withCString ( encodedAs: UTF16 . self) {
622
+ let wchars = wcsnlen_s ( $0, max)
623
+ guard wchars < max else { return false }
624
+ cname. assign ( from: $0, count: wchars + 1 )
625
+ return true
626
+ }
627
+ #else
561
628
return CFStringGetFileSystemRepresentation ( self . _cfObject, cname, max)
629
+ #endif
562
630
}
563
631
564
632
}
0 commit comments