Skip to content

Commit 6788430

Browse files
committed
Implement NSURLDirectoryEnumerator for Windows
1 parent d782c4f commit 6788430

File tree

1 file changed

+101
-28
lines changed

1 file changed

+101
-28
lines changed

Foundation/FileManager.swift

Lines changed: 101 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,46 +2377,122 @@ 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+
var relativePath: UnsafeMutableBufferPointer<WCHAR> = UnsafeMutableBufferPointer<WCHAR>.allocate(capacity: Int(MAX_PATH))
2388+
defer { relativePath.deallocate() }
2389+
guard TRUE == baseURL.absoluteString.withCString(encodedAs: UTF16.self, { pszFrom in
2390+
url.absoluteString.withCString(encodedAs: UTF16.self, { pszTo in
2391+
let fromAttrs = GetFileAttributesW(pszFrom)
2392+
let toAttrs = GetFileAttributesW(pszTo)
2393+
guard fromAttrs != INVALID_FILE_ATTRIBUTES,
2394+
toAttrs != INVALID_FILE_ATTRIBUTES else { return FALSE }
2395+
return PathRelativePathToW(
2396+
relativePath.baseAddress,
2397+
pszFrom,
2398+
fromAttrs,
2399+
pszTo,
2400+
toAttrs)
2401+
})
2402+
}) else { return nil }
2403+
guard let (path, _) = String.decodeCString(relativePath.baseAddress,
2404+
as: UTF16.self) else {
2405+
return nil as String?
2406+
}
23892407
#else
23902408
let path = url.path.replacingOccurrences(of: baseURL.path+"/", with: "")
2391-
_currentItemPath = path
2392-
return path
23932409
#endif
2410+
_currentItemPath = path
2411+
return _currentItemPath
23942412
}
23952413
}
23962414

2397-
#if os(Windows)
23982415
internal class NSURLDirectoryEnumerator : DirectoryEnumerator {
2399-
internal typealias ErrorHandler = /* @escaping */ (URL, Error) -> Bool
2416+
#if os(Windows)
2417+
var _options : FileManager.DirectoryEnumerationOptions
2418+
var _errorHandler : ((URL, Error) -> Bool)?
2419+
var _stack: [URL]
2420+
var _current: URL?
2421+
var _rootDepth : Int
24002422

2401-
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: ErrorHandler?) {
2423+
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) {
2424+
_options = options
2425+
_errorHandler = errorHandler
2426+
_stack = [url]
2427+
_rootDepth = url.pathComponents.count
2428+
}
2429+
2430+
override func nextObject() -> Any? {
2431+
func contentsOfDir(directory: URL) -> [URL]? {
2432+
var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW()
2433+
let dirPath = directory.absoluteString + "\\*"
2434+
let h: HANDLE = dirPath.withCString(encodedAs: UTF16.self) {
2435+
FindFirstFileW($0, &ffd)
2436+
}
2437+
guard h != INVALID_HANDLE_VALUE else { return nil }
2438+
defer { FindClose(h) }
2439+
2440+
var files: [URL] = []
2441+
repeat {
2442+
let fileArr = Array<WCHAR>(
2443+
UnsafeBufferPointer(start: &ffd.cFileName.0,
2444+
count: MemoryLayout.size(ofValue: ffd.cFileName)))
2445+
let file = String(decodingCString: fileArr, as: UTF16.self)
2446+
if file != "."
2447+
&& file != ".."
2448+
&& (!_options.contains(.skipsHiddenFiles)
2449+
|| (ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_HIDDEN) == 0)) {
2450+
files.append(URL(fileURLWithPath: "\(directory.absoluteString)\\\(file)"))
2451+
}
2452+
} while(FindNextFileW(h, &ffd) != 0)
2453+
return files
2454+
}
2455+
while let url = _stack.popLast() {
2456+
if url.hasDirectoryPath && !_options.contains(.skipsSubdirectoryDescendants) {
2457+
guard let dirContents = contentsOfDir(directory: url)?.reversed() else {
2458+
if let handler = _errorHandler {
2459+
let keepGoing = handler(URL(fileURLWithPath: url.absoluteString),
2460+
_NSErrorWithWindowsError(GetLastError(), reading: true))
2461+
if !keepGoing { return nil }
2462+
}
2463+
continue
2464+
}
2465+
_stack.append(contentsOf: dirContents)
2466+
}
2467+
_current = url
2468+
return url
2469+
}
2470+
return nil
2471+
}
2472+
2473+
override var level: Int {
2474+
return _rootDepth - (_current?.pathComponents.count ?? _rootDepth)
2475+
}
2476+
2477+
override func skipDescendants() {
2478+
_options.insert(.skipsSubdirectoryDescendants)
24022479
}
2403-
}
24042480
#else
2405-
internal class NSURLDirectoryEnumerator : DirectoryEnumerator {
24062481
var _url : URL
24072482
var _options : FileManager.DirectoryEnumerationOptions
24082483
var _errorHandler : ((URL, Error) -> Bool)?
24092484
var _stream : UnsafeMutablePointer<FTS>? = nil
24102485
var _current : UnsafeMutablePointer<FTSENT>? = nil
24112486
var _rootError : Error? = nil
24122487
var _gotRoot : Bool = false
2413-
2488+
2489+
24142490
// See @escaping comments above.
24152491
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) {
24162492
_url = url
24172493
_options = options
24182494
_errorHandler = errorHandler
2419-
2495+
24202496
if FileManager.default.fileExists(atPath: _url.path) {
24212497
let fsRep = FileManager.default.fileSystemRepresentation(withPath: _url.path)
24222498
let ps = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 2)
@@ -2430,15 +2506,15 @@ extension FileManager {
24302506
_rootError = _NSErrorWithErrno(ENOENT, reading: true, url: url)
24312507
}
24322508
}
2433-
2509+
24342510
deinit {
24352511
if let stream = _stream {
24362512
fts_close(stream)
24372513
}
24382514
}
2439-
2440-
override func nextObject() -> Any? {
24412515

2516+
2517+
override func nextObject() -> Any? {
24422518
func match(filename: String, to options: DirectoryEnumerationOptions, isDir: Bool) -> (Bool, Bool) {
24432519
var showFile = true
24442520
var skipDescendants = false
@@ -2459,10 +2535,10 @@ extension FileManager {
24592535

24602536

24612537
if let stream = _stream {
2462-
2538+
24632539
if !_gotRoot {
24642540
_gotRoot = true
2465-
2541+
24662542
// Skip the root.
24672543
_current = fts_read(stream)
24682544
}
@@ -2504,7 +2580,7 @@ extension FileManager {
25042580
_current = fts_read(stream)
25052581
}
25062582
// TODO: Error handling if fts_read fails.
2507-
2583+
25082584
} else if let error = _rootError {
25092585
// Was there an error opening the stream?
25102586
if let handler = _errorHandler {
@@ -2513,24 +2589,21 @@ extension FileManager {
25132589
}
25142590
return nil
25152591
}
2516-
2517-
override var directoryAttributes : [FileAttributeKey : Any]? {
2518-
return nil
2519-
}
2520-
2521-
override var fileAttributes: [FileAttributeKey : Any]? {
2522-
return nil
2523-
}
2524-
25252592
override var level: Int {
25262593
return Int(_current?.pointee.fts_level ?? 0)
25272594
}
2528-
2595+
25292596
override func skipDescendants() {
25302597
if let stream = _stream, let current = _current {
25312598
fts_set(stream, current, FTS_SKIP)
25322599
}
25332600
}
2534-
}
25352601
#endif
2602+
override var directoryAttributes : [FileAttributeKey : Any]? {
2603+
return nil
2604+
}
2605+
override var fileAttributes: [FileAttributeKey : Any]? {
2606+
return nil
2607+
}
2608+
}
25362609
}

0 commit comments

Comments
 (0)