Skip to content

Commit 3b9c7c2

Browse files
committed
Implement NSURLDirectoryEnumerator for Windows
1 parent d782c4f commit 3b9c7c2

File tree

1 file changed

+85
-27
lines changed

1 file changed

+85
-27
lines changed

Foundation/FileManager.swift

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,46 +2377,107 @@ extension FileManager {
23772377
self.innerEnumerator = ie
23782378
}
23792379

2380-
@available(Windows, deprecated, message: "Not Yet Implemented")
23812380
override func nextObject() -> Any? {
23822381
let o = innerEnumerator.nextObject()
23832382
guard let url = o as? URL else {
23842383
return nil
23852384
}
23862385

23872386
#if os(Windows)
2388-
NSUnimplemented()
2387+
let path = url.path.replacingOccurrences(of: baseURL.path+"\\", with: "")
23892388
#else
23902389
let path = url.path.replacingOccurrences(of: baseURL.path+"/", with: "")
2390+
#endif
23912391
_currentItemPath = path
23922392
return path
2393-
#endif
23942393
}
23952394
}
23962395

2397-
#if os(Windows)
23982396
internal class NSURLDirectoryEnumerator : DirectoryEnumerator {
2399-
internal typealias ErrorHandler = /* @escaping */ (URL, Error) -> Bool
2397+
#if os(Windows)
2398+
var _options : FileManager.DirectoryEnumerationOptions
2399+
var _errorHandler : ((URL, Error) -> Bool)?
2400+
var _stack: [URL]
2401+
var _current: URL?
2402+
var _root_depth : Int
2403+
24002404

2401-
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: ErrorHandler?) {
2405+
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) {
2406+
_options = options
2407+
_errorHandler = errorHandler
2408+
_stack = [url]
2409+
_root_depth = url.pathComponents.count
2410+
}
2411+
2412+
2413+
override func nextObject() -> Any? {
2414+
func contentsOfDir(directory: URL) -> [URL]? {
2415+
var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW()
2416+
let dirPath = Array((directory.absoluteString + "\\*\0").utf16)
2417+
guard let h = dirPath.withContiguousStorageIfAvailable(
2418+
{FindFirstFileW($0.baseAddress, &ffd)}),
2419+
h != INVALID_HANDLE_VALUE else {
2420+
return nil
2421+
}
2422+
defer {
2423+
FindClose(h)
2424+
}
2425+
2426+
var files: [URL] = []
2427+
repeat {
2428+
let fileArr = [WCHAR](UnsafeBufferPointer(start: &ffd.cFileName.0,
2429+
count: MemoryLayout.size(ofValue: ffd.cFileName)))
2430+
let file = String(decodingCString: fileArr, as: UTF16.self)
2431+
if file != "."
2432+
&& file != ".."
2433+
&& (_options.contains(.skipsHiddenFiles)
2434+
&& (ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_HIDDEN) == 0)) {
2435+
files.append(URL(fileURLWithPath: "\(directory.absoluteString)\\\(file)"))
2436+
}
2437+
} while(FindNextFileW(h, &ffd) != 0)
2438+
return files
2439+
}
2440+
while let url = _stack.popLast() {
2441+
if url.hasDirectoryPath && !_options.contains(.skipsSubdirectoryDescendants) {
2442+
guard let dirContents = contentsOfDir(directory: url)?.reversed() else {
2443+
if let handler = _errorHandler,
2444+
handler(URL(fileURLWithPath: url.absoluteString),
2445+
_NSErrorWithWindowsError(GetLastError(), reading: true)) {
2446+
return nil
2447+
}
2448+
continue
2449+
}
2450+
_stack.append(contentsOf: dirContents)
2451+
}
2452+
_current = url
2453+
return url
2454+
}
2455+
return nil
2456+
}
2457+
2458+
override var level: Int {
2459+
return _root_depth - (_current?.pathComponents.count ?? _root_depth)
2460+
}
2461+
2462+
override func skipDescendants() {
2463+
_options.insert(.skipsSubdirectoryDescendants)
24022464
}
2403-
}
24042465
#else
2405-
internal class NSURLDirectoryEnumerator : DirectoryEnumerator {
24062466
var _url : URL
24072467
var _options : FileManager.DirectoryEnumerationOptions
24082468
var _errorHandler : ((URL, Error) -> Bool)?
24092469
var _stream : UnsafeMutablePointer<FTS>? = nil
24102470
var _current : UnsafeMutablePointer<FTSENT>? = nil
24112471
var _rootError : Error? = nil
24122472
var _gotRoot : Bool = false
2413-
2473+
2474+
24142475
// See @escaping comments above.
24152476
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) {
24162477
_url = url
24172478
_options = options
24182479
_errorHandler = errorHandler
2419-
2480+
24202481
if FileManager.default.fileExists(atPath: _url.path) {
24212482
let fsRep = FileManager.default.fileSystemRepresentation(withPath: _url.path)
24222483
let ps = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 2)
@@ -2430,15 +2491,15 @@ extension FileManager {
24302491
_rootError = _NSErrorWithErrno(ENOENT, reading: true, url: url)
24312492
}
24322493
}
2433-
2494+
24342495
deinit {
24352496
if let stream = _stream {
24362497
fts_close(stream)
24372498
}
24382499
}
2439-
2440-
override func nextObject() -> Any? {
24412500

2501+
2502+
override func nextObject() -> Any? {
24422503
func match(filename: String, to options: DirectoryEnumerationOptions, isDir: Bool) -> (Bool, Bool) {
24432504
var showFile = true
24442505
var skipDescendants = false
@@ -2459,10 +2520,10 @@ extension FileManager {
24592520

24602521

24612522
if let stream = _stream {
2462-
2523+
24632524
if !_gotRoot {
24642525
_gotRoot = true
2465-
2526+
24662527
// Skip the root.
24672528
_current = fts_read(stream)
24682529
}
@@ -2504,7 +2565,7 @@ extension FileManager {
25042565
_current = fts_read(stream)
25052566
}
25062567
// TODO: Error handling if fts_read fails.
2507-
2568+
25082569
} else if let error = _rootError {
25092570
// Was there an error opening the stream?
25102571
if let handler = _errorHandler {
@@ -2513,24 +2574,21 @@ extension FileManager {
25132574
}
25142575
return nil
25152576
}
2516-
2517-
override var directoryAttributes : [FileAttributeKey : Any]? {
2518-
return nil
2519-
}
2520-
2521-
override var fileAttributes: [FileAttributeKey : Any]? {
2522-
return nil
2523-
}
2524-
25252577
override var level: Int {
25262578
return Int(_current?.pointee.fts_level ?? 0)
25272579
}
2528-
2580+
25292581
override func skipDescendants() {
25302582
if let stream = _stream, let current = _current {
25312583
fts_set(stream, current, FTS_SKIP)
25322584
}
25332585
}
2534-
}
25352586
#endif
2587+
override var directoryAttributes : [FileAttributeKey : Any]? {
2588+
return nil
2589+
}
2590+
override var fileAttributes: [FileAttributeKey : Any]? {
2591+
return nil
2592+
}
2593+
}
25362594
}

0 commit comments

Comments
 (0)