Skip to content

Commit 32efc3e

Browse files
jrflatcthielen
authored andcommitted
(137129292) URL(filePath:) should not treat "~" as absolute (swiftlang#961)
1 parent dedbb24 commit 32efc3e

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
@@ -1688,41 +1688,47 @@ extension URL {
16881688
/// Checks if a file path is absolute and standardizes the inputted file path on Windows
16891689
/// Assumes the path only contains `/` as the path separator
16901690
internal static func isAbsolute(standardizing filePath: inout String) -> Bool {
1691+
if filePath.utf8.first == ._slash {
1692+
return true
1693+
}
16911694
#if os(Windows)
1692-
var isAbsolute = false
16931695
let utf8 = filePath.utf8
1694-
if utf8.first == ._slash {
1695-
// Either an absolute path or a UNC path
1696-
isAbsolute = true
1697-
} else if utf8.count >= 3 {
1698-
// Check if this is a drive letter
1699-
let first = utf8.first!
1700-
let secondIndex = utf8.index(after: utf8.startIndex)
1701-
let second = utf8[secondIndex]
1702-
let thirdIndex = utf8.index(after: secondIndex)
1703-
let third = utf8[thirdIndex]
1704-
isAbsolute = (
1705-
first.isAlpha
1706-
&& (second == ._colon || second == ._pipe)
1707-
&& third == ._slash
1708-
)
1709-
1710-
if isAbsolute {
1711-
// Standardize to "/[drive-letter]:/..."
1712-
if second == ._pipe {
1713-
var filePathArray = Array(utf8)
1714-
filePathArray[1] = ._colon
1715-
filePathArray.insert(._slash, at: 0)
1716-
filePath = String(decoding: filePathArray, as: UTF8.self)
1717-
} else {
1718-
filePath = "/" + filePath
1719-
}
1696+
guard utf8.count >= 3 else {
1697+
return false
1698+
}
1699+
// Check if this is a drive letter
1700+
let first = utf8.first!
1701+
let secondIndex = utf8.index(after: utf8.startIndex)
1702+
let second = utf8[secondIndex]
1703+
let thirdIndex = utf8.index(after: secondIndex)
1704+
let third = utf8[thirdIndex]
1705+
let isAbsolute = (
1706+
first.isAlpha
1707+
&& (second == ._colon || second == ._pipe)
1708+
&& third == ._slash
1709+
)
1710+
if isAbsolute {
1711+
// Standardize to "/[drive-letter]:/..."
1712+
if second == ._pipe {
1713+
var filePathArray = Array(utf8)
1714+
filePathArray[1] = ._colon
1715+
filePathArray.insert(._slash, at: 0)
1716+
filePath = String(decoding: filePathArray, as: UTF8.self)
1717+
} else {
1718+
filePath = "/" + filePath
17201719
}
17211720
}
1722-
#else
1723-
let isAbsolute = filePath.utf8.first == UInt8(ascii: "/") || filePath.utf8.first == UInt8(ascii: "~")
1724-
#endif
17251721
return isAbsolute
1722+
#else // os(Windows)
1723+
#if !NO_FILESYSTEM
1724+
// Expand the tilde if present
1725+
if filePath.utf8.first == UInt8(ascii: "~") {
1726+
filePath = filePath.expandingTildeInPath
1727+
}
1728+
#endif
1729+
// Make sure the expanded path is absolute
1730+
return filePath.utf8.first == ._slash
1731+
#endif // os(Windows)
17261732
}
17271733

17281734
/// 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)