@@ -429,8 +429,15 @@ private struct UNIXPath: Path {
429
429
430
430
var dirname : String {
431
431
#if os(Windows)
432
- let dir = string. deletingLastPathComponent
433
- return dir == " " ? " . " : dir
432
+ let fsr : UnsafePointer < Int8 > = string. fileSystemRepresentation
433
+ defer { fsr. deallocate ( ) }
434
+
435
+ let path : String = String ( cString: fsr)
436
+ return path. withCString ( encodedAs: UTF16 . self) {
437
+ let data = UnsafeMutablePointer ( mutating: $0)
438
+ PathCchRemoveFileSpec ( data, path. count)
439
+ return String ( decodingCString: data, as: UTF16 . self)
440
+ }
434
441
#else
435
442
// FIXME: This method seems too complicated; it should be simplified,
436
443
// if possible, and certainly optimized (using UTF8View).
@@ -459,6 +466,13 @@ private struct UNIXPath: Path {
459
466
}
460
467
461
468
var basename : String {
469
+ #if os(Windows)
470
+ let path : String = self . string
471
+ return path. withCString ( encodedAs: UTF16 . self) {
472
+ PathStripPathW ( UnsafeMutablePointer ( mutating: $0) )
473
+ return String ( decodingCString: $0, as: UTF16 . self)
474
+ }
475
+ #else
462
476
// FIXME: This method seems too complicated; it should be simplified,
463
477
// if possible, and certainly optimized (using UTF8View).
464
478
// Check for a special case of the root directory.
@@ -475,13 +489,17 @@ private struct UNIXPath: Path {
475
489
// Otherwise, it's the string from (but not including) the last path
476
490
// separator.
477
491
return String ( string. suffix ( from: string. index ( after: idx) ) )
492
+ #endif
478
493
}
479
494
480
495
// FIXME: We should investigate if it would be more efficient to instead
481
496
// return a path component iterator that does all its work lazily, moving
482
497
// from one path separator to the next on-demand.
483
498
//
484
499
var components : [ String ] {
500
+ #if os(Windows)
501
+ return string. components ( separatedBy: " \\ " ) . filter { !$0. isEmpty }
502
+ #else
485
503
// FIXME: This isn't particularly efficient; needs optimization, and
486
504
// in fact, it might well be best to return a custom iterator so we
487
505
// don't have to allocate everything up-front. It would be backed by
@@ -493,6 +511,7 @@ private struct UNIXPath: Path {
493
511
} else {
494
512
return components
495
513
}
514
+ #endif
496
515
}
497
516
498
517
var parentDirectory : UNIXPath {
@@ -505,7 +524,11 @@ private struct UNIXPath: Path {
505
524
506
525
init ( normalizingAbsolutePath path: String ) {
507
526
#if os(Windows)
508
- self . init ( string: path. standardizingPath)
527
+ var buffer : [ WCHAR ] = Array < WCHAR > ( repeating: 0 , count: Int ( MAX_PATH + 1 ) )
528
+ _ = path. withCString ( encodedAs: UTF16 . self) {
529
+ PathCanonicalizeW ( & buffer, $0)
530
+ }
531
+ self . init ( string: String ( decodingCString: buffer, as: UTF16 . self) )
509
532
#else
510
533
precondition ( path. first == " / " , " Failure normalizing \( path) , absolute paths should start with '/' " )
511
534
@@ -571,7 +594,11 @@ private struct UNIXPath: Path {
571
594
572
595
init ( normalizingRelativePath path: String ) {
573
596
#if os(Windows)
574
- self . init ( string: path. standardizingPath)
597
+ var buffer : [ WCHAR ] = Array < WCHAR > ( repeating: 0 , count: Int ( MAX_PATH + 1 ) )
598
+ _ = path. replacingOccurrences ( of: " / " , with: " \\ " ) . withCString ( encodedAs: UTF16 . self) {
599
+ PathCanonicalizeW ( & buffer, $0)
600
+ }
601
+ self . init ( string: String ( decodingCString: buffer, as: UTF16 . self) )
575
602
#else
576
603
precondition ( path. first != " / " )
577
604
@@ -679,6 +706,15 @@ private struct UNIXPath: Path {
679
706
}
680
707
681
708
func suffix( withDot: Bool ) -> String ? {
709
+ #if os(Windows)
710
+ let ext = self . string. withCString ( encodedAs: UTF16 . self) {
711
+ PathFindExtensionW ( $0)
712
+ }
713
+ var result = String ( decodingCString: ext!, as: UTF16 . self)
714
+ guard result. length > 0 else { return nil }
715
+ if !withDot { result. removeFirst ( 1 ) }
716
+ return result
717
+ #else
682
718
// FIXME: This method seems too complicated; it should be simplified,
683
719
// if possible, and certainly optimized (using UTF8View).
684
720
// Find the last path separator, if any.
@@ -700,9 +736,20 @@ private struct UNIXPath: Path {
700
736
}
701
737
// If we get this far, there is no suffix.
702
738
return nil
739
+ #endif
703
740
}
704
741
705
742
func appending( component name: String ) -> UNIXPath {
743
+ #if os(Windows)
744
+ var result : PWSTR ?
745
+ _ = string. withCString ( encodedAs: UTF16 . self) { root in
746
+ name. withCString ( encodedAs: UTF16 . self) { path in
747
+ PathAllocCombine ( root, path, ULONG ( PATHCCH_ALLOW_LONG_PATHS . rawValue) , & result)
748
+ }
749
+ }
750
+ defer { LocalFree ( result) }
751
+ return PathImpl ( string: String ( decodingCString: result!, as: UTF16 . self) )
752
+ #else
706
753
assert ( !name. contains ( " / " ) , " \( name) is invalid path component " )
707
754
708
755
// Handle pseudo paths.
@@ -720,9 +767,20 @@ private struct UNIXPath: Path {
720
767
} else {
721
768
return PathImpl ( string: string + " / " + name)
722
769
}
770
+ #endif
723
771
}
724
772
725
773
func appending( relativePath: UNIXPath ) -> UNIXPath {
774
+ #if os(Windows)
775
+ var result : PWSTR ?
776
+ _ = string. withCString ( encodedAs: UTF16 . self) { root in
777
+ relativePath. string. withCString ( encodedAs: UTF16 . self) { path in
778
+ PathAllocCombine ( root, path, ULONG ( PATHCCH_ALLOW_LONG_PATHS . rawValue) , & result)
779
+ }
780
+ }
781
+ defer { LocalFree ( result) }
782
+ return PathImpl ( string: String ( decodingCString: result!, as: UTF16 . self) )
783
+ #else
726
784
// Both paths are already normalized. The only case in which we have
727
785
// to renormalize their concatenation is if the relative path starts
728
786
// with a `..` path component.
@@ -748,6 +806,7 @@ private struct UNIXPath: Path {
748
806
} else {
749
807
return PathImpl ( string: newPathString)
750
808
}
809
+ #endif
751
810
}
752
811
}
753
812
@@ -795,7 +854,11 @@ extension AbsolutePath {
795
854
// Special case, which is a plain path without `..` components. It
796
855
// might be an empty path (when self and the base are equal).
797
856
let relComps = pathComps. dropFirst ( baseComps. count)
857
+ #if os(Windows)
858
+ result = RelativePath ( relComps. joined ( separator: " \\ " ) )
859
+ #else
798
860
result = RelativePath ( relComps. joined ( separator: " / " ) )
861
+ #endif
799
862
} else {
800
863
// General case, in which we might well need `..` components to go
801
864
// "up" before we can go "down" the directory tree.
@@ -810,7 +873,11 @@ extension AbsolutePath {
810
873
// `newBaseComps` followed by what remains in `newPathComps`.
811
874
var relComps = Array ( repeating: " .. " , count: newBaseComps. count)
812
875
relComps. append ( contentsOf: newPathComps)
876
+ #if os(Windows)
877
+ result = RelativePath ( relComps. joined ( separator: " \\ " ) )
878
+ #else
813
879
result = RelativePath ( relComps. joined ( separator: " / " ) )
880
+ #endif
814
881
}
815
882
assert ( base. appending ( result) == self )
816
883
return result
0 commit comments