@@ -22,8 +22,28 @@ import WinSDK
22
22
23
23
internal import _FoundationCShims
24
24
25
+ extension StringProtocol {
26
+ fileprivate func _standardizingSlashes( ) -> String {
27
+ #if os(Windows)
28
+ // The string functions below all assume that the path separator is a forward slash
29
+ // Standardize the path to use forward slashes before processing for consistency
30
+ return self . replacing ( . _backslash, with: . _slash)
31
+ #else
32
+ if let str = _specializingCast ( self , to: String . self) {
33
+ return str
34
+ } else {
35
+ return String ( self )
36
+ }
37
+ #endif
38
+ }
39
+ }
40
+
25
41
extension String {
26
42
internal func deletingLastPathComponent( ) -> String {
43
+ _standardizingSlashes ( ) . _deletingLastPathComponent ( )
44
+ }
45
+
46
+ private func _deletingLastPathComponent( ) -> String {
27
47
let lastSlash = self . lastIndex { $0 == " / " }
28
48
guard let lastSlash else {
29
49
// No slash
@@ -50,6 +70,10 @@ extension String {
50
70
}
51
71
52
72
internal func appendingPathComponent( _ component: String ) -> String {
73
+ _standardizingSlashes ( ) . _appendingPathComponent ( component)
74
+ }
75
+
76
+ private func _appendingPathComponent( _ component: String ) -> String {
53
77
var result = self
54
78
if !component. isEmpty {
55
79
var needsSlash = true
@@ -103,6 +127,10 @@ extension String {
103
127
}
104
128
105
129
internal var lastPathComponent : String {
130
+ _standardizingSlashes ( ) . _lastPathComponent
131
+ }
132
+
133
+ private var _lastPathComponent : String {
106
134
let lastSlash = self . lastIndex { $0 == " / " }
107
135
guard let lastSlash else {
108
136
// No slash, just return self
@@ -170,11 +198,11 @@ extension String {
170
198
return false
171
199
}
172
200
if let lastDot = pathExtension. utf8. lastIndex ( of: UInt8 ( ascii: " . " ) ) {
173
- let beforeDot = pathExtension [ ..< lastDot] . unicodeScalars
174
- let afterDot = pathExtension [ pathExtension. index ( after: lastDot) ... ] . unicodeScalars
201
+ let beforeDot = pathExtension [ ..< lastDot] . _standardizingSlashes ( ) . unicodeScalars
202
+ let afterDot = pathExtension [ pathExtension. index ( after: lastDot) ... ] . _standardizingSlashes ( ) . unicodeScalars
175
203
return beforeDot. allSatisfy { $0 != " / " } && afterDot. allSatisfy { !String. invalidExtensionScalars. contains ( $0) }
176
204
} else {
177
- return pathExtension. unicodeScalars. allSatisfy { !String. invalidExtensionScalars. contains ( $0) }
205
+ return pathExtension. _standardizingSlashes ( ) . unicodeScalars. allSatisfy { !String. invalidExtensionScalars. contains ( $0) }
178
206
}
179
207
}
180
208
@@ -202,6 +230,10 @@ extension String {
202
230
}
203
231
204
232
internal func merging( relativePath: String ) -> String {
233
+ _standardizingSlashes ( ) . _merging ( relativePath: relativePath)
234
+ }
235
+
236
+ private func _merging( relativePath: String ) -> String {
205
237
guard relativePath. utf8. first != UInt8 ( ascii: " / " ) else {
206
238
return relativePath
207
239
}
@@ -212,6 +244,10 @@ extension String {
212
244
}
213
245
214
246
internal var removingDotSegments : String {
247
+ _standardizingSlashes ( ) . _removingDotSegments
248
+ }
249
+
250
+ private var _removingDotSegments : String {
215
251
let input = self . utf8
216
252
guard !input. isEmpty else {
217
253
return " "
@@ -440,18 +476,12 @@ extension String {
440
476
441
477
// From swift-corelibs-foundation's NSTemporaryDirectory. Internal for now, pending a better public API.
442
478
internal static var temporaryDirectoryPath : String {
443
- #if os(Windows)
444
- let validPathSeps : [ Character ] = [ " \\ " , " / " ]
445
- #else
446
- let validPathSeps : [ Character ] = [ " / " ]
447
- #endif
448
-
449
479
func normalizedPath( with path: String ) -> String {
450
- if validPathSeps. contains ( where: { path. hasSuffix ( String ( $0) ) } ) {
451
- return path
452
- } else {
453
- return path + String( validPathSeps. last!)
480
+ var result = path. _standardizingSlashes ( )
481
+ guard result. utf8. last != . _slash else {
482
+ return result
454
483
}
484
+ return result + " / "
455
485
}
456
486
#if os(Windows)
457
487
let cchLength : DWORD = GetTempPathW ( 0 , nil )
@@ -547,7 +577,7 @@ extension String {
547
577
static var NETWORK_PREFIX : String { #"\\"# }
548
578
549
579
private var _standardizingPath : String {
550
- var result = _transmutingCompressingSlashes ( ) . _droppingTrailingSlashes
580
+ var result = _standardizingSlashes ( ) . _transmutingCompressingSlashes ( ) . _droppingTrailingSlashes
551
581
let postNetStart = if result. starts ( with: String . NETWORK_PREFIX) {
552
582
result. firstIndex ( of: " / " ) ?? result. endIndex
553
583
} else {
@@ -558,7 +588,7 @@ extension String {
558
588
result = resolved
559
589
}
560
590
561
- result = result. removingDotSegments
591
+ result = result. _removingDotSegments
562
592
563
593
// Automounted paths need to be stripped for various flavors of paths
564
594
let exclusionList = [ " /Applications " , " /Library " , " /System " , " /Users " , " /Volumes " , " /bin " , " /cores " , " /dev " , " /opt " , " /private " , " /sbin " , " /usr " ]
@@ -584,6 +614,10 @@ extension String {
584
614
585
615
// _NSPathComponents
586
616
var pathComponents : [ String ] {
617
+ _standardizingSlashes ( ) . _pathComponents
618
+ }
619
+
620
+ private var _pathComponents : [ String ] {
587
621
var components = self . components ( separatedBy: " / " ) . filter { !$0. isEmpty }
588
622
if self . first == " / " {
589
623
components. insert ( " / " , at: 0 )
@@ -596,6 +630,10 @@ extension String {
596
630
597
631
#if !NO_FILESYSTEM
598
632
var abbreviatingWithTildeInPath : String {
633
+ _standardizingSlashes ( ) . _abbreviatingWithTildeInPath
634
+ }
635
+
636
+ private var _abbreviatingWithTildeInPath : String {
599
637
guard !self . isEmpty && self != " / " else { return self }
600
638
let homeDir = String . homeDirectoryPath ( )
601
639
guard self . starts ( with: homeDir) else { return self }
@@ -605,6 +643,10 @@ extension String {
605
643
}
606
644
607
645
var expandingTildeInPath : String {
646
+ _standardizingSlashes ( ) . _expandingTildeInPath
647
+ }
648
+
649
+ private var _expandingTildeInPath : String {
608
650
guard self . first == " ~ " else { return self }
609
651
var user : String ? = nil
610
652
let firstSlash = self . firstIndex ( of: " / " ) ?? self . endIndex
@@ -781,6 +823,7 @@ extension StringProtocol {
781
823
}
782
824
}
783
825
826
+ // Internal for testing purposes
784
827
internal func _hasDotDotComponent( ) -> Bool {
785
828
let input = self . utf8
786
829
guard input. count >= 2 else {
0 commit comments