Skip to content

Commit 6682092

Browse files
authored
Merge pull request swiftlang#1481 from spevans/pr_filemanager_fixes
2 parents 38f4633 + 6de796e commit 6682092

File tree

3 files changed

+54
-67
lines changed

3 files changed

+54
-67
lines changed

CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <CoreFoundation/ForFoundationOnly.h>
2828
#include <fts.h>
2929
#include <pthread.h>
30+
#include <dirent.h>
3031

3132
#if __has_include(<execinfo.h>)
3233
#include <execinfo.h>
@@ -399,6 +400,13 @@ static inline _Bool _withStackOrHeapBuffer(size_t amount, void (__attribute__((n
399400
return true;
400401
}
401402

403+
static inline int _direntNameLength(struct dirent *entry) {
404+
#ifdef _D_EXACT_NAMLEN // defined on Linux
405+
return _D_EXACT_NAMLEN(entry);
406+
#else
407+
return entry->d_namlen;
408+
#endif
409+
}
402410

403411
_CF_EXPORT_SCOPE_END
404412

Foundation/FileManager.swift

Lines changed: 41 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,35 @@ open class FileManager : NSObject {
171171
}
172172
}
173173
}
174-
174+
175+
private func _contentsOfDir(atPath path: String, _ closure: (String, Int32) throws -> () ) throws {
176+
let fsRep = fileSystemRepresentation(withPath: path)
177+
defer { fsRep.deallocate() }
178+
179+
guard let dir = opendir(fsRep) else {
180+
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadNoSuchFile.rawValue, userInfo: [NSFilePathErrorKey: path])
181+
}
182+
defer { closedir(dir) }
183+
184+
var entry = dirent()
185+
var result: UnsafeMutablePointer<dirent>? = nil
186+
187+
while readdir_r(dir, &entry, &result) == 0 {
188+
guard result != nil else {
189+
return
190+
}
191+
let length = Int(_direntNameLength(&entry))
192+
let entryName = withUnsafePointer(to: &entry.d_name) { (ptr) -> String in
193+
let namePtr = UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self)
194+
return string(withFileSystemRepresentation: namePtr, length: length)
195+
}
196+
if entryName != "." && entryName != ".." {
197+
let entryType = Int32(entry.d_type)
198+
try closure(entryName, entryType)
199+
}
200+
}
201+
}
202+
175203
/**
176204
Performs a shallow search of the specified directory and returns the paths of any contained items.
177205

@@ -186,28 +214,11 @@ open class FileManager : NSObject {
186214
- Returns: An array of String each of which identifies a file, directory, or symbolic link contained in `path`. The order of the files returned is undefined.
187215
*/
188216
open func contentsOfDirectory(atPath path: String) throws -> [String] {
189-
var contents : [String] = [String]()
190-
191-
let dir = opendir(path)
192-
193-
if dir == nil {
194-
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadNoSuchFile.rawValue, userInfo: [NSFilePathErrorKey: path])
195-
}
196-
197-
defer {
198-
closedir(dir!)
199-
}
217+
var contents: [String] = []
200218

201-
while let entry = readdir(dir!) {
202-
let entryName = withUnsafePointer(to: &entry.pointee.d_name) {
203-
String(cString: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self))
204-
}
205-
// TODO: `entryName` should be limited in length to `entry.memory.d_namlen`.
206-
if entryName != "." && entryName != ".." {
207-
contents.append(entryName)
208-
}
209-
}
210-
219+
try _contentsOfDir(atPath: path, { (entryName, entryType) throws in
220+
contents.append(entryName)
221+
})
211222
return contents
212223
}
213224

@@ -225,48 +236,16 @@ open class FileManager : NSObject {
225236
- Returns: An array of NSString objects, each of which contains the path of an item in the directory specified by path. If path is a symbolic link, this method traverses the link. This method returns nil if it cannot retrieve the device of the linked-to file.
226237
*/
227238
open func subpathsOfDirectory(atPath path: String) throws -> [String] {
228-
var contents : [String] = [String]()
229-
230-
let dir = opendir(path)
231-
232-
if dir == nil {
233-
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadNoSuchFile.rawValue, userInfo: [NSFilePathErrorKey: path])
234-
}
235-
236-
defer {
237-
closedir(dir!)
238-
}
239-
240-
var entry = readdir(dir!)
241-
242-
while entry != nil {
243-
let entryName = withUnsafePointer(to: &entry!.pointee.d_name) {
244-
String(cString: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self))
245-
}
246-
// TODO: `entryName` should be limited in length to `entry.memory.d_namlen`.
247-
if entryName != "." && entryName != ".." {
248-
contents.append(entryName)
249-
250-
let entryType = withUnsafePointer(to: &entry!.pointee.d_type) { (ptr) -> Int32 in
251-
return Int32(ptr.pointee)
252-
}
253-
#if os(OSX) || os(iOS)
254-
let tempEntryType = entryType
255-
#elseif os(Linux) || os(Android) || CYGWIN
256-
let tempEntryType = Int32(entryType)
257-
#endif
239+
var contents: [String] = []
258240

259-
if tempEntryType == Int32(DT_DIR) {
260-
let subPath: String = path + "/" + entryName
261-
262-
let entries = try subpathsOfDirectory(atPath: subPath)
263-
contents.append(contentsOf: entries.map({file in "\(entryName)/\(file)"}))
264-
}
241+
try _contentsOfDir(atPath: path, { (entryName, entryType) throws in
242+
contents.append(entryName)
243+
if entryType == Int32(DT_DIR) {
244+
let subPath: String = path + "/" + entryName
245+
let entries = try subpathsOfDirectory(atPath: subPath)
246+
contents.append(contentsOf: entries.map({file in "\(entryName)/\(file)"}))
265247
}
266-
267-
entry = readdir(dir!)
268-
}
269-
248+
})
270249
return contents
271250
}
272251

TestFoundation/TestFileManager.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ class TestFileManager : XCTestCase {
329329
} catch _ {
330330
XCTFail()
331331
}
332-
332+
333333
if let e = FileManager.default.enumerator(at: URL(fileURLWithPath: path), includingPropertiesForKeys: nil, options: [], errorHandler: nil) {
334334
var foundItems = [String:Int]()
335335
while let item = e.nextObject() as? URL {
@@ -442,8 +442,8 @@ class TestFileManager : XCTestCase {
442442
}
443443

444444
do {
445-
let _ = try fm.contentsOfDirectory(atPath: "")
446-
445+
// Check a bad path fails
446+
let _ = try fm.contentsOfDirectory(atPath: "/...")
447447
XCTFail()
448448
}
449449
catch _ {
@@ -492,8 +492,8 @@ class TestFileManager : XCTestCase {
492492
}
493493

494494
do {
495-
let _ = try fm.subpathsOfDirectory(atPath: "")
496-
495+
// Check a bad path fails
496+
let _ = try fm.subpathsOfDirectory(atPath: "/...")
497497
XCTFail()
498498
}
499499
catch _ {

0 commit comments

Comments
 (0)