Skip to content

Commit caea53a

Browse files
gmittertaciidgh
authored andcommitted
Remove use of fts
Replace the use of fts with Foundation's Directory Enumerator, which (on Darwin/Linux), calls fts instead, but is cross platform for Windows.
1 parent 0a176ca commit caea53a

File tree

3 files changed

+34
-81
lines changed

3 files changed

+34
-81
lines changed

Sources/Basic/FileSystem.swift

Lines changed: 33 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,18 @@ public enum FileMode {
8888
case userUnWritable
8989
case userWritable
9090
case executable
91-
92-
/// File mode as it would be passed to `chmod`.
93-
public var cliArgument: String {
91+
92+
internal var setMode: (Int16) -> Int16 {
9493
switch self {
9594
case .userUnWritable:
96-
return "u-w"
95+
// r-x rwx rwx
96+
return {$0 & 0o577}
9797
case .userWritable:
98-
return "u+w"
98+
// -w- --- ---
99+
return {$0 | 0o200}
99100
case .executable:
100-
return "+x"
101+
// --x --x --x
102+
return {$0 | 0o111}
101103
}
102104
}
103105
}
@@ -375,86 +377,39 @@ private class LocalFileSystem: FileSystem {
375377
}
376378

377379
func chmod(_ mode: FileMode, path: AbsolutePath, options: Set<FileMode.Option>) throws {
378-
#if os(macOS)
379-
// Get the mode we need to set.
380-
guard let setMode = setmode(mode.cliArgument) else {
381-
throw FileSystemError(errno: errno)
382-
}
383-
defer { setMode.deallocate() }
380+
guard exists(path) else { return }
381+
func setMode(path: String) throws {
382+
// Skip if only files should be changed.
383+
if options.contains(.onlyFiles) && isDirectory(AbsolutePath(path)) {
384+
return
385+
}
384386

385-
let recursive = options.contains(.recursive)
386-
// If we're in recursive mode, do physical walk otherwise logical.
387-
let ftsOptions = recursive ? FTS_PHYSICAL : FTS_LOGICAL
387+
let attrs = try FileManager.default.attributesOfItem(atPath: path)
388388

389-
// Get handle to the file hierarchy we want to traverse.
390-
let paths = CStringArray([path.pathString])
391-
guard let ftsp = fts_open(paths.cArray, ftsOptions, nil) else {
392-
throw FileSystemError(errno: errno)
389+
// Compute the new mode for this file.
390+
let currentMode = attrs[.posixPermissions] as! Int16
391+
let newMode = mode.setMode(currentMode)
392+
guard newMode != currentMode else { return }
393+
try FileManager.default.setAttributes([.posixPermissions : newMode],
394+
ofItemAtPath: path)
393395
}
394-
defer { fts_close(ftsp) }
395-
396-
// Start traversing.
397-
while let p = fts_read(ftsp) {
398-
399-
switch Int32(p.pointee.fts_info) {
400-
401-
// A directory being visited in pre-order.
402-
case FTS_D:
403-
// If we're not recursing, skip the contents of the directory.
404-
if !recursive {
405-
fts_set(ftsp, p, FTS_SKIP)
406-
}
407-
continue
408-
409-
// A directory couldn't be read.
410-
case FTS_DNR:
411-
// FIXME: We should warn here.
412-
break
413-
414-
// There was an error.
415-
case FTS_ERR:
416-
fallthrough
417396

418-
// No stat(2) information was available.
419-
case FTS_NS:
420-
// FIXME: We should warn here.
421-
continue
397+
try setMode(path: path.pathString)
398+
guard isDirectory(path) else { return }
422399

423-
// A symbolic link.
424-
case FTS_SL:
425-
fallthrough
426-
427-
// A symbolic link with a non-existent target.
428-
case FTS_SLNONE:
429-
// The only symlinks that end up here are ones that don't point
430-
// to anything and ones that we found doing a physical walk.
431-
continue
432-
433-
default:
434-
break
435-
}
436-
437-
// Compute the new mode for this file.
438-
let currentMode = mode_t(p.pointee.fts_statp.pointee.st_mode)
439-
440-
// Skip if only files should be changed.
441-
if options.contains(.onlyFiles) && (currentMode & S_IFMT) == S_IFDIR {
442-
continue
443-
}
400+
guard let traverse = FileManager.default.enumerator(
401+
at: URL(fileURLWithPath: path.pathString),
402+
includingPropertiesForKeys: nil) else {
403+
throw FileSystemError.noEntry
404+
}
444405

445-
// Compute the new mode.
446-
let newMode = getmode(setMode, currentMode)
447-
if newMode == currentMode {
448-
continue
449-
}
406+
if !options.contains(.recursive) {
407+
traverse.skipDescendants()
408+
}
450409

451-
// Update the mode.
452-
//
453-
// We ignore the errors for now but we should have a way to report back.
454-
_ = SPMLibc.chmod(p.pointee.fts_accpath, newMode)
410+
while let path = traverse.nextObject() {
411+
try setMode(path: (path as! URL).path)
455412
}
456-
#endif
457-
// FIXME: We only support macOS right now.
458413
}
459414
}
460415

Sources/clibc/include/clibc.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#include <fts.h>
2-
31
#if defined(__linux__)
42
#include <sys/inotify.h>
53
#endif

Tests/BasicTests/FileSystemTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ class FileSystemTests: XCTestCase {
388388
}
389389

390390
func testSetAttribute() throws {
391-
#if os(macOS)
391+
#if os(macOS) || os(Linux)
392392
mktmpdir { path in
393393
let fs = Basic.localFileSystem
394394

0 commit comments

Comments
 (0)