Skip to content

Commit 988d616

Browse files
committed
(137129292) URL(filePath:) should not treat "~" as absolute (swiftlang#961)
1 parent 768e032 commit 988d616

File tree

2 files changed

+56
-30
lines changed

2 files changed

+56
-30
lines changed

Sources/FoundationEssentials/URL/URL.swift

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,41 +2040,47 @@ extension URL {
20402040
/// Checks if a file path is absolute and standardizes the inputted file path on Windows
20412041
/// Assumes the path only contains `/` as the path separator
20422042
internal static func isAbsolute(standardizing filePath: inout String) -> Bool {
2043+
if filePath.utf8.first == ._slash {
2044+
return true
2045+
}
20432046
#if os(Windows)
2044-
var isAbsolute = false
20452047
let utf8 = filePath.utf8
2046-
if utf8.first == ._slash {
2047-
// Either an absolute path or a UNC path
2048-
isAbsolute = true
2049-
} else if utf8.count >= 3 {
2050-
// Check if this is a drive letter
2051-
let first = utf8.first!
2052-
let secondIndex = utf8.index(after: utf8.startIndex)
2053-
let second = utf8[secondIndex]
2054-
let thirdIndex = utf8.index(after: secondIndex)
2055-
let third = utf8[thirdIndex]
2056-
isAbsolute = (
2057-
first.isAlpha
2058-
&& (second == ._colon || second == ._pipe)
2059-
&& third == ._slash
2060-
)
2061-
2062-
if isAbsolute {
2063-
// Standardize to "/[drive-letter]:/..."
2064-
if second == ._pipe {
2065-
var filePathArray = Array(utf8)
2066-
filePathArray[1] = ._colon
2067-
filePathArray.insert(._slash, at: 0)
2068-
filePath = String(decoding: filePathArray, as: UTF8.self)
2069-
} else {
2070-
filePath = "/" + filePath
2071-
}
2048+
guard utf8.count >= 3 else {
2049+
return false
2050+
}
2051+
// Check if this is a drive letter
2052+
let first = utf8.first!
2053+
let secondIndex = utf8.index(after: utf8.startIndex)
2054+
let second = utf8[secondIndex]
2055+
let thirdIndex = utf8.index(after: secondIndex)
2056+
let third = utf8[thirdIndex]
2057+
let isAbsolute = (
2058+
first.isAlpha
2059+
&& (second == ._colon || second == ._pipe)
2060+
&& third == ._slash
2061+
)
2062+
if isAbsolute {
2063+
// Standardize to "/[drive-letter]:/..."
2064+
if second == ._pipe {
2065+
var filePathArray = Array(utf8)
2066+
filePathArray[1] = ._colon
2067+
filePathArray.insert(._slash, at: 0)
2068+
filePath = String(decoding: filePathArray, as: UTF8.self)
2069+
} else {
2070+
filePath = "/" + filePath
20722071
}
20732072
}
2074-
#else
2075-
let isAbsolute = filePath.utf8.first == UInt8(ascii: "/") || filePath.utf8.first == UInt8(ascii: "~")
2076-
#endif
20772073
return isAbsolute
2074+
#else // os(Windows)
2075+
#if !NO_FILESYSTEM
2076+
// Expand the tilde if present
2077+
if filePath.utf8.first == UInt8(ascii: "~") {
2078+
filePath = filePath.expandingTildeInPath
2079+
}
2080+
#endif
2081+
// Make sure the expanded path is absolute
2082+
return filePath.utf8.first == ._slash
2083+
#endif // os(Windows)
20782084
}
20792085

20802086
/// Initializes a newly created file URL referencing the local file or directory at path, relative to a base URL.

Tests/FoundationEssentialsTests/URLTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,26 @@ final class URLTests : XCTestCase {
627627
XCTAssertEqual(url.host, "*.xn--poema-9qae5a.com.br")
628628
}
629629

630+
func testURLTildeFilePath() throws {
631+
var url = URL(filePath: "~")
632+
// "~" must either be expanded to an absolute path or resolved against a base URL
633+
XCTAssertTrue(
634+
url.relativePath.utf8.first == ._slash || (url.baseURL != nil && url.path().utf8.first == ._slash)
635+
)
636+
637+
url = URL(filePath: "~", directoryHint: .isDirectory)
638+
XCTAssertTrue(
639+
url.relativePath.utf8.first == ._slash || (url.baseURL != nil && url.path().utf8.first == ._slash)
640+
)
641+
XCTAssertEqual(url.path().utf8.last, ._slash)
642+
643+
url = URL(filePath: "~/")
644+
XCTAssertTrue(
645+
url.relativePath.utf8.first == ._slash || (url.baseURL != nil && url.path().utf8.first == ._slash)
646+
)
647+
XCTAssertEqual(url.path().utf8.last, ._slash)
648+
}
649+
630650
func testURLComponentsPercentEncodedUnencodedProperties() throws {
631651
var comp = URLComponents()
632652

0 commit comments

Comments
 (0)