@@ -2136,8 +2136,11 @@ extension URL {
2136
2136
}
2137
2137
#endif // FOUNDATION_FRAMEWORK
2138
2138
2139
- # if !NO_FILESYSTEM
2139
+ /// Checks the file system to determine if the path is a directory
2140
2140
private static func isDirectory( _ path: String ) -> Bool {
2141
+ #if NO_FILESYSTEM
2142
+ return path. utf8. last == . _slash
2143
+ #else
2141
2144
#if os(Windows)
2142
2145
let path = path. replacing ( . _slash, with: . _backslash)
2143
2146
#endif
@@ -2149,56 +2152,64 @@ extension URL {
2149
2152
var isDirectory : ObjCBool = false
2150
2153
_ = FileManager . default. fileExists ( atPath: path, isDirectory: & isDirectory)
2151
2154
return isDirectory. boolValue
2152
- #endif
2155
+ #endif // !FOUNDATION_FRAMEWORK
2156
+ #endif // NO_FILESYSTEM
2153
2157
}
2154
- #endif // !NO_FILESYSTEM
2155
2158
2156
- /// Checks if a file path is absolute and standardizes the inputted file path on Windows
2157
- /// Assumes the path only contains `/` as the path separator
2159
+ /// Checks if a file path is absolute and standardizes the inputted file path
2158
2160
internal static func isAbsolute( standardizing filePath: inout String ) -> Bool {
2159
2161
if filePath. utf8. first == . _slash {
2162
+ #if os(Windows)
2163
+ filePath = filePath. replacing ( . _backslash, with: . _slash)
2164
+ #endif
2160
2165
return true
2161
2166
}
2162
- #if os(Windows)
2163
- let utf8 = filePath. utf8
2164
- guard utf8. count >= 3 else {
2167
+ #if NO_FILESYSTEM
2168
+ return false
2169
+ #elseif os(Windows)
2170
+ // PathIsRelativeW:
2171
+ // - true for "path" and "\path"
2172
+ // - false otherwise (including "C:path")
2173
+ let isRelative : Bool = filePath. withCString ( encodedAs: UTF16 . self) { pwszPath in
2174
+ PathIsRelativeW ( pwszPath)
2175
+ }
2176
+ if isRelative && filePath. utf8. first != . _backslash {
2177
+ // e.g. "path" - only case where we won't resolve to an absolute path
2178
+ filePath = filePath. replacing ( . _backslash, with: . _slash)
2165
2179
return false
2166
2180
}
2167
- // Check if this is a drive letter
2168
- let first = utf8. first!
2169
- let secondIndex = utf8. index ( after: utf8. startIndex)
2170
- let second = utf8 [ secondIndex]
2171
- let thirdIndex = utf8. index ( after: secondIndex)
2172
- let third = utf8 [ thirdIndex]
2173
- let isAbsolute = (
2174
- first. isAlpha
2175
- && ( second == . _colon || second == . _pipe)
2176
- && third == . _slash
2177
- )
2178
- if isAbsolute {
2179
- // Standardize to "/[drive-letter]:/..."
2180
- if second == . _pipe {
2181
- var filePathArray = Array ( utf8)
2182
- filePathArray [ 1 ] = . _colon
2183
- filePathArray. insert ( . _slash, at: 0 )
2184
- filePath = String ( decoding: filePathArray, as: UTF8 . self)
2185
- } else {
2186
- filePath = " / " + filePath
2187
- }
2181
+ filePath = filePath. fullPathName ?? filePath
2182
+ filePath = filePath. replacing ( . _backslash, with: . _slash)
2183
+ if filePath. utf8. first != . _slash {
2184
+ // Prepend a "/" to form an RFC 8089 path
2185
+ filePath = " / " + filePath
2188
2186
}
2189
- return isAbsolute
2187
+ return true
2190
2188
#else // os(Windows)
2191
- #if !NO_FILESYSTEM
2192
2189
// Expand the tilde if present
2193
2190
if filePath. utf8. first == UInt8 ( ascii: " ~ " ) {
2194
2191
filePath = filePath. expandingTildeInPath
2195
2192
}
2196
- #endif
2197
2193
// Make sure the expanded path is absolute
2198
2194
return filePath. utf8. first == . _slash
2199
2195
#endif // os(Windows)
2200
2196
}
2201
2197
2198
+ private static func currentDirectoryOrNil( ) -> URL ? {
2199
+ #if NO_FILESYSTEM
2200
+ return nil
2201
+ #else
2202
+ let path : String ? = FileManager . default. currentDirectoryPath
2203
+ guard var filePath = path else {
2204
+ return nil
2205
+ }
2206
+ guard URL . isAbsolute ( standardizing: & filePath) else {
2207
+ return nil
2208
+ }
2209
+ return URL ( filePath: filePath, directoryHint: . isDirectory)
2210
+ #endif // NO_FILESYSTEM
2211
+ }
2212
+
2202
2213
/// Initializes a newly created file URL referencing the local file or directory at path, relative to a base URL.
2203
2214
///
2204
2215
/// If an empty string is used for the path, then the path is assumed to be ".".
@@ -2225,19 +2236,12 @@ extension URL {
2225
2236
#endif // FOUNDATION_FRAMEWORK
2226
2237
var baseURL = base
2227
2238
guard !path. isEmpty else {
2228
- #if !NO_FILESYSTEM
2229
2239
baseURL = baseURL ?? . currentDirectoryOrNil( )
2230
- #endif
2231
2240
self . init ( string: " " , relativeTo: baseURL) !
2232
2241
return
2233
2242
}
2234
2243
2235
- #if os(Windows)
2236
- // Convert any "\" to "/" before storing the URL parse info
2237
- var filePath = path. replacing ( . _backslash, with: . _slash)
2238
- #else
2239
2244
var filePath = path
2240
- #endif
2241
2245
2242
2246
#if FOUNDATION_FRAMEWORK
2243
2247
// Linked-on-or-after check for apps which incorrectly pass a full
@@ -2251,12 +2255,9 @@ extension URL {
2251
2255
#endif
2252
2256
2253
2257
let isAbsolute = URL . isAbsolute ( standardizing: & filePath)
2254
-
2255
- #if !NO_FILESYSTEM
2256
2258
if !isAbsolute {
2257
2259
baseURL = baseURL ?? . currentDirectoryOrNil( )
2258
2260
}
2259
- #endif
2260
2261
2261
2262
let isDirectory : Bool
2262
2263
switch directoryHint {
@@ -2266,7 +2267,6 @@ extension URL {
2266
2267
filePath = filePath. _droppingTrailingSlashes
2267
2268
isDirectory = false
2268
2269
case . checkFileSystem:
2269
- #if !NO_FILESYSTEM
2270
2270
func absoluteFilePath( ) -> String {
2271
2271
guard !isAbsolute, let baseURL else {
2272
2272
return filePath
@@ -2275,9 +2275,6 @@ extension URL {
2275
2275
return URL . fileSystemPath ( for: absolutePath)
2276
2276
}
2277
2277
isDirectory = URL . isDirectory ( absoluteFilePath ( ) )
2278
- #else
2279
- isDirectory = filePath. utf8. last == . _slash
2280
- #endif
2281
2278
case . inferFromPath:
2282
2279
isDirectory = filePath. utf8. last == . _slash
2283
2280
}
@@ -2571,20 +2568,6 @@ extension URL {
2571
2568
2572
2569
#if !NO_FILESYSTEM
2573
2570
extension URL {
2574
- private static func currentDirectoryOrNil( ) -> URL ? {
2575
- let path : String ? = FileManager . default. currentDirectoryPath
2576
- guard var filePath = path else {
2577
- return nil
2578
- }
2579
- #if os(Windows)
2580
- filePath = filePath. replacing ( . _backslash, with: . _slash)
2581
- #endif
2582
- guard URL . isAbsolute ( standardizing: & filePath) else {
2583
- return nil
2584
- }
2585
- return URL ( filePath: filePath, directoryHint: . isDirectory)
2586
- }
2587
-
2588
2571
/// The working directory of the current process.
2589
2572
/// Calling this property will issue a `getcwd` syscall.
2590
2573
@available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
0 commit comments