@@ -652,64 +652,69 @@ extension FileManager {
652
652
var _options : FileManager . DirectoryEnumerationOptions
653
653
var _errorHandler : ( ( URL , Error ) -> Bool ) ?
654
654
var _stack : [ URL ]
655
- var _current : URL ?
655
+ var _lastReturned : URL
656
656
var _rootDepth : Int
657
657
658
658
init ( url: URL , options: FileManager . DirectoryEnumerationOptions , errorHandler: ( /* @escaping */ ( URL , Error ) -> Bool ) ? ) {
659
659
_options = options
660
660
_errorHandler = errorHandler
661
- _stack = [ url ]
661
+ _stack = [ ]
662
662
_rootDepth = url. pathComponents. count
663
+ _lastReturned = url
663
664
}
664
665
665
666
override func nextObject( ) -> Any ? {
666
- func contentsOfDir( directory: URL ) -> [ URL ] ? {
667
- var ffd : WIN32_FIND_DATAW = WIN32_FIND_DATAW ( )
668
- guard let dirFSR = directory. withUnsafeFileSystemRepresentation ( { $0. flatMap { fsr in String ( utf8String: fsr) } } )
669
- else { return nil }
670
- let dirPath = joinPath ( prefix: dirFSR, suffix: " * " )
671
- let h : HANDLE = dirPath. withCString ( encodedAs: UTF16 . self) {
667
+ func firstValidItem( ) -> URL ? {
668
+ while let url = _stack. popLast ( ) {
669
+ if !FileManager. default. fileExists ( atPath: url. path, isDirectory: nil ) {
670
+ guard let handler = _errorHandler,
671
+ handler ( url, _NSErrorWithWindowsError ( GetLastError ( ) , reading: true ) )
672
+ else { return nil }
673
+ continue
674
+ }
675
+ _lastReturned = url
676
+ return _lastReturned
677
+ }
678
+ return nil
679
+ }
680
+
681
+ // If we most recently returned a directory, decend into it
682
+ var isDir : ObjCBool = false
683
+ guard FileManager . default. fileExists ( atPath: _lastReturned. path, isDirectory: & isDir) else {
684
+ guard let handler = _errorHandler,
685
+ handler ( _lastReturned, _NSErrorWithWindowsError ( GetLastError ( ) , reading: true ) )
686
+ else { return nil }
687
+ return firstValidItem ( )
688
+ }
689
+
690
+ if isDir. boolValue && ( level == 0 || !_options. contains ( . skipsSubdirectoryDescendants) ) {
691
+ var ffd = WIN32_FIND_DATAW ( )
692
+ let dirPath = joinPath ( prefix: _lastReturned. path, suffix: " * " )
693
+ let handle = dirPath. withCString ( encodedAs: UTF16 . self) {
672
694
FindFirstFileW ( $0, & ffd)
673
695
}
674
- guard h != INVALID_HANDLE_VALUE else { return nil }
675
- defer { FindClose ( h ) }
696
+ guard handle != INVALID_HANDLE_VALUE else { return firstValidItem ( ) }
697
+ defer { FindClose ( handle ) }
676
698
677
- var files : [ URL ] = [ ]
678
699
repeat {
679
700
let fileArr = Array < WCHAR > (
680
701
UnsafeBufferPointer ( start: & ffd. cFileName. 0 ,
681
702
count: MemoryLayout . size ( ofValue: ffd. cFileName) ) )
682
703
let file = String ( decodingCString: fileArr, as: UTF16 . self)
683
- if file != " . "
684
- && file != " .. "
704
+ if file != " . " && file != " .. "
685
705
&& ( !_options. contains ( . skipsHiddenFiles)
686
706
|| ( ffd. dwFileAttributes & DWORD ( FILE_ATTRIBUTE_HIDDEN) == 0 ) ) {
687
- files. append ( URL ( fileURLWithPath: joinPath ( prefix: dirFSR, suffix: file) ) )
688
- }
689
- } while FindNextFileW ( h, & ffd)
690
- return files
691
- }
692
- while let url = _stack. popLast ( ) {
693
- if url. hasDirectoryPath && !_options. contains ( . skipsSubdirectoryDescendants) {
694
- guard let dirContents = contentsOfDir ( directory: url) ? . reversed ( ) else {
695
- if let handler = _errorHandler {
696
- let dirFSR = url. withUnsafeFileSystemRepresentation { $0. flatMap { fsr in String ( utf8String: fsr) } }
697
- let keepGoing = handler ( URL ( fileURLWithPath: dirFSR ?? " " ) ,
698
- _NSErrorWithWindowsError ( GetLastError ( ) , reading: true ) )
699
- if !keepGoing { return nil }
700
- }
701
- continue
707
+ let relative = URL ( fileURLWithPath: file, relativeTo: _lastReturned)
708
+ _stack. append ( relative)
702
709
}
703
- _stack. append ( contentsOf: dirContents)
704
- }
705
- _current = url
706
- return url
710
+ } while FindNextFileW ( handle, & ffd)
707
711
}
708
- return nil
712
+
713
+ return firstValidItem ( )
709
714
}
710
715
711
716
override var level : Int {
712
- return _rootDepth - ( _current ? . pathComponents. count ?? _rootDepth)
717
+ return _lastReturned . pathComponents. count - _rootDepth
713
718
}
714
719
715
720
override func skipDescendants( ) {
@@ -728,31 +733,24 @@ extension FileManager {
728
733
729
734
extension FileManager . NSPathDirectoryEnumerator {
730
735
internal func _nextObject( ) -> Any ? {
731
- let o = innerEnumerator. nextObject ( )
732
- guard let url = o as? URL else {
733
- return nil
734
- }
736
+ guard let url = innerEnumerator. nextObject ( ) as? URL else { return nil }
735
737
736
- var relativePath = UnsafeMutableBufferPointer< WCHAR> . allocate( capacity: Int ( MAX_PATH) )
737
- defer { relativePath. deallocate ( ) }
738
- func withURLCString< Result> ( url: URL , _ f: ( UnsafePointer < WCHAR > ) -> Result ? ) -> Result ? {
739
- return url. withUnsafeFileSystemRepresentation { fsr in
740
- ( fsr. flatMap { String ( utf8String: $0) } ) ? . withCString ( encodedAs: UTF16 . self) { f ( $0) }
741
- }
742
- }
743
- guard withURLCString ( url: baseURL, { pszFrom -> Bool ? in
744
- withURLCString ( url: url) { pszTo in
745
- let fromAttrs = GetFileAttributesW ( pszFrom)
746
- let toAttrs = GetFileAttributesW ( pszTo)
738
+ var relativePath : [ WCHAR ] = Array < WCHAR > ( repeating: 0 , count: Int ( MAX_PATH) )
739
+
740
+ guard baseURL. _withUnsafeWideFileSystemRepresentation ( { baseUrlFsr in
741
+ url. _withUnsafeWideFileSystemRepresentation { urlFsr in
742
+ let fromAttrs = GetFileAttributesW ( baseUrlFsr)
743
+ let toAttrs = GetFileAttributesW ( urlFsr)
747
744
guard fromAttrs != INVALID_FILE_ATTRIBUTES, toAttrs != INVALID_FILE_ATTRIBUTES else {
748
745
return false
749
746
}
750
- return PathRelativePathToW ( relativePath. baseAddress , pszFrom , fromAttrs, pszTo , toAttrs)
747
+ return PathRelativePathToW ( & relativePath, baseUrlFsr , fromAttrs, urlFsr , toAttrs)
751
748
}
752
- } ) == true , let ( path, _) = String . decodeCString ( relativePath. baseAddress, as: UTF16 . self) else {
753
- return nil
754
- }
755
- _currentItemPath = path
749
+ } ) else { return nil }
750
+
751
+ let path = String ( decodingCString: & relativePath, as: UTF16 . self)
752
+ // Drop the leading ".\" from the path
753
+ _currentItemPath = String ( path. dropFirst ( 2 ) )
756
754
return _currentItemPath
757
755
}
758
756
0 commit comments