Skip to content

Commit

Permalink
(139676985) URL(filePath:) should resolve Windows drive-relative paths
Browse files Browse the repository at this point in the history
  • Loading branch information
jrflat committed Nov 12, 2024
1 parent 221a0dd commit 6249fc1
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
33 changes: 31 additions & 2 deletions Sources/FoundationEssentials/URL/URL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2225,11 +2225,40 @@ extension URL {
#if os(Windows)
// Convert any "\" to "/" before storing the URL parse info
var filePath = path.replacing(._backslash, with: ._slash)
let isAbsolute: Bool
var iter = filePath.utf8.makeIterator()
if let driveLetter = iter.next(), driveLetter.isAlpha,
iter.next() == ._colon,
iter.next() != ._slash {
// Drive-relative path: use the current directory for the given drive letter
// as the base URL, and remove the drive letter from the relative path.
let relativePath = String(Substring(filePath.utf8.dropFirst(2)))
let basePath: String? = "\(Unicode.Scalar(driveLetter)):".withCString(encodedAs: UTF16.self) { pwszDriveLetter in
let dwLength: DWORD = GetFullPathNameW(pwszDriveLetter, 0, nil, nil)
guard dwLength > 0 else {
return nil
}
return try? withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwLength)) {
guard GetFullPathNameW(pwszDriveLetter, DWORD($0.count), $0.baseAddress, nil) > 0 else {
return nil
}
return String(decodingCString: $0.baseAddress!, as: UTF16.self)
}
}
guard let basePath else {
self.init(filePath: relativePath, directoryHint: directoryHint, relativeTo: base)
return
}
baseURL = URL(filePath: basePath, directoryHint: .isDirectory)
filePath = relativePath
isAbsolute = false
} else {
isAbsolute = URL.isAbsolute(standardizing: &filePath)
}
#else
var filePath = path
#endif

let isAbsolute = URL.isAbsolute(standardizing: &filePath)
#endif

#if !NO_FILESYSTEM
if !isAbsolute {
Expand Down
11 changes: 10 additions & 1 deletion Tests/FoundationEssentialsTests/URLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,19 @@ final class URLTests : XCTestCase {
if iter.next() == ._slash,
let driveLetter = iter.next(), driveLetter.isLetter!,
iter.next() == ._colon {
let path = #"\\?\"# + "\(Unicode.Scalar(driveLetter))" + #":\"#
let drive = "\(Unicode.Scalar(driveLetter))"
let path = #"\\?\"# + drive + #":\"#
url = URL(filePath: path, directoryHint: .isDirectory)
XCTAssertEqual(url.path.last, "/")
XCTAssertEqual(url.fileSystemPath.last, "/")

// Test drive-relative path
let driveRelativePath = "\(drive):hello"
url = URL(filePath: driveRelativePath)
XCTAssertEqual(url.relativePath, "hello")
XCTAssertEqual(url.relativeString, "hello")
XCTAssertTrue(url.baseURL?.path.starts(with: "\(drive):/") ?? false)
XCTAssertTrue(url.path.starts(with: "\(drive):/"))
}
}
#endif
Expand Down

0 comments on commit 6249fc1

Please sign in to comment.