@@ -22,8 +22,28 @@ import WinSDK
2222
2323internal import _FoundationCShims
2424
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+
2541extension String {
2642 internal func deletingLastPathComponent( ) -> String {
43+ _standardizingSlashes ( ) . _deletingLastPathComponent ( )
44+ }
45+
46+ private func _deletingLastPathComponent( ) -> String {
2747 let lastSlash = self . lastIndex { $0 == " / " }
2848 guard let lastSlash else {
2949 // No slash
@@ -50,6 +70,10 @@ extension String {
5070 }
5171
5272 internal func appendingPathComponent( _ component: String ) -> String {
73+ _standardizingSlashes ( ) . _appendingPathComponent ( component)
74+ }
75+
76+ private func _appendingPathComponent( _ component: String ) -> String {
5377 var result = self
5478 if !component. isEmpty {
5579 var needsSlash = true
@@ -103,6 +127,10 @@ extension String {
103127 }
104128
105129 internal var lastPathComponent : String {
130+ _standardizingSlashes ( ) . _lastPathComponent
131+ }
132+
133+ private var _lastPathComponent : String {
106134 let lastSlash = self . lastIndex { $0 == " / " }
107135 guard let lastSlash else {
108136 // No slash, just return self
@@ -170,11 +198,11 @@ extension String {
170198 return false
171199 }
172200 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
175203 return beforeDot. allSatisfy { $0 != " / " } && afterDot. allSatisfy { !String. invalidExtensionScalars. contains ( $0) }
176204 } else {
177- return pathExtension. unicodeScalars. allSatisfy { !String. invalidExtensionScalars. contains ( $0) }
205+ return pathExtension. _standardizingSlashes ( ) . unicodeScalars. allSatisfy { !String. invalidExtensionScalars. contains ( $0) }
178206 }
179207 }
180208
@@ -202,6 +230,10 @@ extension String {
202230 }
203231
204232 internal func merging( relativePath: String ) -> String {
233+ _standardizingSlashes ( ) . _merging ( relativePath: relativePath)
234+ }
235+
236+ private func _merging( relativePath: String ) -> String {
205237 guard relativePath. utf8. first != UInt8 ( ascii: " / " ) else {
206238 return relativePath
207239 }
@@ -212,6 +244,10 @@ extension String {
212244 }
213245
214246 internal var removingDotSegments : String {
247+ _standardizingSlashes ( ) . _removingDotSegments
248+ }
249+
250+ private var _removingDotSegments : String {
215251 let input = self . utf8
216252 guard !input. isEmpty else {
217253 return " "
@@ -440,18 +476,12 @@ extension String {
440476
441477 // From swift-corelibs-foundation's NSTemporaryDirectory. Internal for now, pending a better public API.
442478 internal static var temporaryDirectoryPath : String {
443- #if os(Windows)
444- let validPathSeps : [ Character ] = [ " \\ " , " / " ]
445- #else
446- let validPathSeps : [ Character ] = [ " / " ]
447- #endif
448-
449479 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
454483 }
484+ return result + " / "
455485 }
456486#if os(Windows)
457487 let cchLength : DWORD = GetTempPathW ( 0 , nil )
@@ -547,7 +577,7 @@ extension String {
547577 static var NETWORK_PREFIX : String { #"\\"# }
548578
549579 private var _standardizingPath : String {
550- var result = _transmutingCompressingSlashes ( ) . _droppingTrailingSlashes
580+ var result = _standardizingSlashes ( ) . _transmutingCompressingSlashes ( ) . _droppingTrailingSlashes
551581 let postNetStart = if result. starts ( with: String . NETWORK_PREFIX) {
552582 result. firstIndex ( of: " / " ) ?? result. endIndex
553583 } else {
@@ -558,7 +588,7 @@ extension String {
558588 result = resolved
559589 }
560590
561- result = result. removingDotSegments
591+ result = result. _removingDotSegments
562592
563593 // Automounted paths need to be stripped for various flavors of paths
564594 let exclusionList = [ " /Applications " , " /Library " , " /System " , " /Users " , " /Volumes " , " /bin " , " /cores " , " /dev " , " /opt " , " /private " , " /sbin " , " /usr " ]
@@ -584,6 +614,10 @@ extension String {
584614
585615 // _NSPathComponents
586616 var pathComponents : [ String ] {
617+ _standardizingSlashes ( ) . _pathComponents
618+ }
619+
620+ private var _pathComponents : [ String ] {
587621 var components = self . components ( separatedBy: " / " ) . filter { !$0. isEmpty }
588622 if self . first == " / " {
589623 components. insert ( " / " , at: 0 )
@@ -596,6 +630,10 @@ extension String {
596630
597631 #if !NO_FILESYSTEM
598632 var abbreviatingWithTildeInPath : String {
633+ _standardizingSlashes ( ) . _abbreviatingWithTildeInPath
634+ }
635+
636+ private var _abbreviatingWithTildeInPath : String {
599637 guard !self . isEmpty && self != " / " else { return self }
600638 let homeDir = String . homeDirectoryPath ( )
601639 guard self . starts ( with: homeDir) else { return self }
@@ -605,6 +643,10 @@ extension String {
605643 }
606644
607645 var expandingTildeInPath : String {
646+ _standardizingSlashes ( ) . _expandingTildeInPath
647+ }
648+
649+ private var _expandingTildeInPath : String {
608650 guard self . first == " ~ " else { return self }
609651 var user : String ? = nil
610652 let firstSlash = self . firstIndex ( of: " / " ) ?? self . endIndex
@@ -781,6 +823,7 @@ extension StringProtocol {
781823 }
782824 }
783825
826+ // Internal for testing purposes
784827 internal func _hasDotDotComponent( ) -> Bool {
785828 let input = self . utf8
786829 guard input. count >= 2 else {
0 commit comments