Skip to content

URL(filePath:) should not treat "~" as absolute #961

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 36 additions & 30 deletions Sources/FoundationEssentials/URL/URL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1688,41 +1688,47 @@ extension URL {
/// Checks if a file path is absolute and standardizes the inputted file path on Windows
/// Assumes the path only contains `/` as the path separator
internal static func isAbsolute(standardizing filePath: inout String) -> Bool {
if filePath.utf8.first == ._slash {
return true
}
#if os(Windows)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored the Windows part and moved up the .first == ._slash check, which is valid for Windows and non-Windows platforms.

var isAbsolute = false
let utf8 = filePath.utf8
if utf8.first == ._slash {
// Either an absolute path or a UNC path
isAbsolute = true
} else if utf8.count >= 3 {
// Check if this is a drive letter
let first = utf8.first!
let secondIndex = utf8.index(after: utf8.startIndex)
let second = utf8[secondIndex]
let thirdIndex = utf8.index(after: secondIndex)
let third = utf8[thirdIndex]
isAbsolute = (
first.isAlpha
&& (second == ._colon || second == ._pipe)
&& third == ._slash
)

if isAbsolute {
// Standardize to "/[drive-letter]:/..."
if second == ._pipe {
var filePathArray = Array(utf8)
filePathArray[1] = ._colon
filePathArray.insert(._slash, at: 0)
filePath = String(decoding: filePathArray, as: UTF8.self)
} else {
filePath = "/" + filePath
}
guard utf8.count >= 3 else {
return false
}
// Check if this is a drive letter
let first = utf8.first!
let secondIndex = utf8.index(after: utf8.startIndex)
let second = utf8[secondIndex]
let thirdIndex = utf8.index(after: secondIndex)
let third = utf8[thirdIndex]
let isAbsolute = (
first.isAlpha
&& (second == ._colon || second == ._pipe)
&& third == ._slash
)
if isAbsolute {
// Standardize to "/[drive-letter]:/..."
if second == ._pipe {
var filePathArray = Array(utf8)
filePathArray[1] = ._colon
filePathArray.insert(._slash, at: 0)
filePath = String(decoding: filePathArray, as: UTF8.self)
} else {
filePath = "/" + filePath
}
}
#else
let isAbsolute = filePath.utf8.first == UInt8(ascii: "/") || filePath.utf8.first == UInt8(ascii: "~")
#endif
return isAbsolute
#else // os(Windows)
#if !NO_FILESYSTEM
// Expand the tilde if present
if filePath.utf8.first == UInt8(ascii: "~") {
filePath = filePath.expandingTildeInPath
}
#endif
// Make sure the expanded path is absolute
return filePath.utf8.first == ._slash
#endif // os(Windows)
}

/// Initializes a newly created file URL referencing the local file or directory at path, relative to a base URL.
Expand Down
20 changes: 20 additions & 0 deletions Tests/FoundationEssentialsTests/URLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,26 @@ final class URLTests : XCTestCase {
XCTAssertEqual(url.host, "*.xn--poema-9qae5a.com.br")
}

func testURLTildeFilePath() throws {
var url = URL(filePath: "~")
// "~" must either be expanded to an absolute path or resolved against a base URL
XCTAssertTrue(
url.relativePath.utf8.first == ._slash || (url.baseURL != nil && url.path().utf8.first == ._slash)
)

url = URL(filePath: "~", directoryHint: .isDirectory)
XCTAssertTrue(
url.relativePath.utf8.first == ._slash || (url.baseURL != nil && url.path().utf8.first == ._slash)
)
XCTAssertEqual(url.path().utf8.last, ._slash)

url = URL(filePath: "~/")
XCTAssertTrue(
url.relativePath.utf8.first == ._slash || (url.baseURL != nil && url.path().utf8.first == ._slash)
)
XCTAssertEqual(url.path().utf8.last, ._slash)
}

func testURLComponentsPercentEncodedUnencodedProperties() throws {
var comp = URLComponents()

Expand Down