Skip to content

Commit 3b47193

Browse files
authored
Merge pull request #2239 from gmittert/SlashingForwardsAndBack
Add Support for '\' in Windows Paths
2 parents d4641de + 64d5f3e commit 3b47193

File tree

2 files changed

+27
-21
lines changed

2 files changed

+27
-21
lines changed

Foundation/NSPathUtilities.swift

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99

1010
import CoreFoundation
1111

12+
#if os(Windows)
13+
let validPathSeps: [Character] = ["\\", "/"]
14+
#else
15+
let validPathSeps: [Character] = ["/"]
16+
#endif
17+
1218
public func NSTemporaryDirectory() -> String {
1319
#if os(Windows)
1420
let cchLength: DWORD = GetTempPathW(0, nil)
@@ -51,7 +57,7 @@ public func NSTemporaryDirectory() -> String {
5157
}
5258
#endif
5359
if let tmpdir = ProcessInfo.processInfo.environment["TMPDIR"] {
54-
if !tmpdir.hasSuffix("/") {
60+
if !validPathSeps.contains(where: { tmpdir.hasSuffix(String($0)) }) {
5561
return tmpdir + "/"
5662
} else {
5763
return tmpdir
@@ -70,15 +76,15 @@ public func NSTemporaryDirectory() -> String {
7076
extension String {
7177

7278
internal var _startOfLastPathComponent : String.Index {
73-
precondition(!hasSuffix("/") && length > 1)
79+
precondition(!validPathSeps.contains(where: { hasSuffix(String($0)) }) && length > 1)
7480

7581
let startPos = startIndex
7682
var curPos = endIndex
7783

7884
// Find the beginning of the component
7985
while curPos > startPos {
8086
let prevPos = index(before: curPos)
81-
if self[prevPos] == "/" {
87+
if validPathSeps.contains(self[prevPos]) {
8288
break
8389
}
8490
curPos = prevPos
@@ -88,7 +94,7 @@ extension String {
8894
}
8995

9096
internal var _startOfPathExtension : String.Index? {
91-
precondition(!hasSuffix("/"))
97+
precondition(!validPathSeps.contains(where: { hasSuffix(String($0)) }))
9298

9399
var currentPosition = endIndex
94100
let startOfLastPathComponent = _startOfLastPathComponent
@@ -97,7 +103,7 @@ extension String {
97103
while currentPosition > startOfLastPathComponent {
98104
let previousPosition = index(before: currentPosition)
99105
let character = self[previousPosition]
100-
if character == "/" {
106+
if validPathSeps.contains(character) {
101107
return nil
102108
} else if character == "." {
103109
if startOfLastPathComponent == previousPosition {
@@ -132,8 +138,8 @@ extension String {
132138
if isEmpty {
133139
return str
134140
}
135-
if hasSuffix("/") {
136-
return self + str
141+
if validPathSeps.contains(where: { hasSuffix(String($0)) }) {
142+
return self + str
137143
}
138144
return self + "/" + str
139145
}
@@ -146,9 +152,9 @@ extension String {
146152
var curPos = startPos
147153

148154
while curPos < endPos {
149-
if result[curPos] == "/" {
155+
if validPathSeps.contains(result[curPos]) {
150156
var afterLastSlashPos = curPos
151-
while afterLastSlashPos < endPos && result[afterLastSlashPos] == "/" {
157+
while afterLastSlashPos < endPos && validPathSeps.contains(result[afterLastSlashPos]) {
152158
afterLastSlashPos = result.index(after: afterLastSlashPos)
153159
}
154160
if afterLastSlashPos != result.index(after: curPos) {
@@ -161,7 +167,7 @@ extension String {
161167
}
162168
}
163169
}
164-
if stripTrailing && result.length > 1 && result.hasSuffix("/") {
170+
if stripTrailing && result.length > 1 && validPathSeps.contains(where: {result.hasSuffix(String($0))}) {
165171
result.remove(at: result.index(before: result.endIndex))
166172
}
167173
return result
@@ -248,7 +254,7 @@ extension NSString {
248254
}
249255

250256
internal func _stringByFixingSlashes(compress : Bool = true, stripTrailing: Bool = true) -> String {
251-
if _swiftObject == "/" {
257+
if validPathSeps.contains(where: { String($0) == _swiftObject }) {
252258
return _swiftObject
253259
}
254260

@@ -259,9 +265,9 @@ extension NSString {
259265
var curPos = startPos
260266

261267
while curPos < endPos {
262-
if result[curPos] == "/" {
268+
if validPathSeps.contains(result[curPos]) {
263269
var afterLastSlashPos = curPos
264-
while afterLastSlashPos < endPos && result[afterLastSlashPos] == "/" {
270+
while afterLastSlashPos < endPos && validPathSeps.contains(result[afterLastSlashPos]) {
265271
afterLastSlashPos = result.index(after: afterLastSlashPos)
266272
}
267273
if afterLastSlashPos != result.index(after: curPos) {
@@ -274,7 +280,7 @@ extension NSString {
274280
}
275281
}
276282
}
277-
if stripTrailing && result.hasSuffix("/") {
283+
if stripTrailing && validPathSeps.contains(where: { result.hasSuffix(String($0)) }) {
278284
result.remove(at: result.index(before: result.endIndex))
279285
}
280286
return result
@@ -314,7 +320,7 @@ extension NSString {
314320
}
315321

316322
public func appendingPathExtension(_ str: String) -> String? {
317-
if str.hasPrefix("/") || self == "" || self == "/" {
323+
if validPathSeps.contains(where: { str.hasPrefix(String($0)) }) || self == "" || validPathSeps.contains(where: { String($0)._nsObject == self }) {
318324
print("Cannot append extension \(str) to path \(self)")
319325
return nil
320326
}
@@ -327,7 +333,7 @@ extension NSString {
327333
return _swiftObject
328334
}
329335

330-
let endOfUserName = _swiftObject.firstIndex(of: "/") ?? _swiftObject.endIndex
336+
let endOfUserName = _swiftObject.firstIndex(where : { validPathSeps.contains($0) }) ?? _swiftObject.endIndex
331337
let startOfUserName = _swiftObject.index(after: _swiftObject.startIndex)
332338
let userName = String(_swiftObject[startOfUserName..<endOfUserName])
333339
let optUserName: String? = userName.isEmpty ? nil : userName
@@ -359,12 +365,10 @@ extension NSString {
359365
}
360366

361367
// TODO: pathComponents keeps final path separator if any. Check that logic.
362-
if components.last == "/" && components.count > 1 {
368+
if validPathSeps.contains(where: { String($0) == components.last }) && components.count > 1 {
363369
components.removeLast()
364370
}
365371

366-
let isAbsolutePath = components.first == "/"
367-
368372
var resolvedPath = components.removeFirst()
369373
for component in components {
370374
switch component {
@@ -439,7 +443,7 @@ extension NSString {
439443
}
440444

441445
internal func _stringIsPathToDirectory(_ path: String) -> Bool {
442-
if !path.hasSuffix("/") {
446+
if !validPathSeps.contains(where: { path.hasSuffix(String($0)) }) {
443447
return false
444448
}
445449

@@ -538,7 +542,7 @@ extension NSString {
538542
}
539543

540544
internal func _ensureLastPathSeparator(_ path: String) -> String {
541-
if path.hasSuffix("/") || path.isEmpty {
545+
if validPathSeps.contains(where: { path.hasSuffix(String($0)) }) || path.isEmpty {
542546
return path
543547
}
544548

Foundation/NSURL.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,8 @@ extension NSURL {
957957

958958
open func appendingPathComponent(_ pathComponent: String) -> URL? {
959959
var result : URL? = appendingPathComponent(pathComponent, isDirectory: false)
960+
// Since we are appending to a URL, path seperators should
961+
// always be '/', even if we're on Windows
960962
if !pathComponent.hasSuffix("/") && isFileURL {
961963
if let urlWithoutDirectory = result {
962964
var isDir: ObjCBool = false

0 commit comments

Comments
 (0)