@@ -88,16 +88,18 @@ public enum FileMode {
88
88
case userUnWritable
89
89
case userWritable
90
90
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 {
94
93
switch self {
95
94
case . userUnWritable:
96
- return " u-w "
95
+ // r-x rwx rwx
96
+ return { $0 & 0o577 }
97
97
case . userWritable:
98
- return " u+w "
98
+ // -w- --- ---
99
+ return { $0 | 0o200 }
99
100
case . executable:
100
- return " +x "
101
+ // --x --x --x
102
+ return { $0 | 0o111 }
101
103
}
102
104
}
103
105
}
@@ -375,86 +377,39 @@ private class LocalFileSystem: FileSystem {
375
377
}
376
378
377
379
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
+ }
384
386
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)
388
388
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)
393
395
}
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
417
396
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 }
422
399
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
+ }
444
405
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
+ }
450
409
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)
455
412
}
456
- #endif
457
- // FIXME: We only support macOS right now.
458
413
}
459
414
}
460
415
0 commit comments