Skip to content

Commit 000c62b

Browse files
authored
Merge pull request swiftlang#2332 from gmittert/Hidden
Implement ._hidden on Windows
2 parents c2adcdc + 8613843 commit 000c62b

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

Foundation/FileManager.swift

+24-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ fileprivate let UF_HIDDEN: Int32 = 1
1616
#endif
1717

1818
import CoreFoundation
19+
#if os(Windows)
20+
import MSVCRT
21+
#endif
1922

2023
open class FileManager : NSObject {
2124

@@ -400,7 +403,6 @@ open class FileManager : NSObject {
400403
newAccessDate = providedDate
401404
}
402405
#endif
403-
404406
case .immutable: fallthrough
405407
case ._userImmutable:
406408
prepareToSetOrUnsetFlag(UF_IMMUTABLE)
@@ -412,7 +414,21 @@ open class FileManager : NSObject {
412414
prepareToSetOrUnsetFlag(UF_APPEND)
413415

414416
case ._hidden:
417+
#if os(Windows)
418+
let attrs = try windowsFileAttributes(atPath: path).dwFileAttributes
419+
guard let isHidden = attributeValues[attribute] as? Bool else {
420+
fatalError("Can't set \(attribute) to \(attributeValues[attribute] as Any?)")
421+
}
422+
423+
let hiddenAttrs = isHidden
424+
? attrs | DWORD(FILE_ATTRIBUTE_HIDDEN)
425+
: attrs & DWORD(bitPattern: ~FILE_ATTRIBUTE_HIDDEN)
426+
guard path.withCString(encodedAs: UTF16.self, { SetFileAttributesW($0, hiddenAttrs) }) else {
427+
fatalError("Couldn't set \(path) to be hidden")
428+
}
429+
#else
415430
prepareToSetOrUnsetFlag(UF_HIDDEN)
431+
#endif
416432

417433
// FIXME: On Darwin, these can be set with setattrlist(); and of course chown/chgrp on other OSes.
418434
case .ownerAccountID: fallthrough
@@ -531,7 +547,8 @@ open class FileManager : NSObject {
531547

532548
#if os(Windows)
533549
result[.deviceIdentifier] = NSNumber(value: UInt64(s.st_rdev))
534-
let type = FileAttributeType(attributes: try windowsFileAttributes(atPath: path), atPath: path)
550+
let attributes = try windowsFileAttributes(atPath: path)
551+
let type = FileAttributeType(attributes: attributes, atPath: path)
535552
#else
536553
if let pwd = getpwuid(s.st_uid), pwd.pointee.pw_name != nil {
537554
let name = String(cString: pwd.pointee.pw_name)
@@ -566,6 +583,11 @@ open class FileManager : NSObject {
566583
result[.appendOnly] = NSNumber(value: true)
567584
}
568585
#endif
586+
587+
#if os(Windows)
588+
let attrs = attributes.dwFileAttributes
589+
result[._hidden] = attrs & DWORD(FILE_ATTRIBUTE_HIDDEN) != 0
590+
#endif
569591
result[.ownerAccountID] = NSNumber(value: UInt64(s.st_uid))
570592
result[.groupOwnerAccountID] = NSNumber(value: UInt64(s.st_gid))
571593

TestFoundation/TestFileManager.swift

+32-4
Original file line numberDiff line numberDiff line change
@@ -496,15 +496,18 @@ class TestFileManager : XCTestCase {
496496
func test_directoryEnumerator() {
497497
let fm = FileManager.default
498498
let basePath = NSTemporaryDirectory() + "testdir\(NSUUID().uuidString)/"
499-
let subDirs1 = basePath + "subdir1/subdir2/.hiddenDir/subdir3/"
499+
let hiddenDir1 = basePath + "subdir1/subdir2/.hiddenDir/"
500+
let subDirs1 = hiddenDir1 + "subdir3/"
500501
let itemPath1 = basePath + "itemFile1"
501502
#if os(Windows)
502503
// Filenames ending with '.' are not valid on Windows, so don't bother testing them
503-
let subDirs2 = basePath + "subdir1/subdir2/subdir4.app/subdir5/.subdir6.ext/subdir7.ext/"
504+
let hiddenDir2 = basePath + "subdir1/subdir2/subdir4.app/subdir5/.subdir6.ext/"
505+
let subDirs2 = hiddenDir2 + "subdir7.ext/"
504506
let itemPath2 = subDirs1 + "itemFile2"
505507
let itemPath3 = subDirs1 + "itemFile3.ext"
506508
#else
507-
let subDirs2 = basePath + "subdir1/subdir2/subdir4.app/subdir5./.subdir6.ext/subdir7.ext./"
509+
let hiddenDir2 = basePath + "subdir1/subdir2/subdir4.app/subdir5./.subdir6.ext/"
510+
let subDirs2 = hiddenDir2 + "subdir7.ext./"
508511
let itemPath2 = subDirs1 + "itemFile2."
509512
let itemPath3 = subDirs1 + "itemFile3.ext."
510513
#endif
@@ -555,10 +558,35 @@ class TestFileManager : XCTestCase {
555558

556559
XCTAssertNotNil(try? fm.createDirectory(atPath: subDirs1, withIntermediateDirectories: true, attributes: nil))
557560
XCTAssertNotNil(try? fm.createDirectory(atPath: subDirs2, withIntermediateDirectories: true, attributes: nil))
558-
for filename in [itemPath1, itemPath2, itemPath3, hiddenItem1, hiddenItem2, hiddenItem3, hiddenItem4] {
561+
for filename in [itemPath1, itemPath2, itemPath3] {
559562
XCTAssertTrue(fm.createFile(atPath: filename, contents: Data(), attributes: nil), "Cant create file '\(filename)'")
560563
}
561564

565+
var resourceValues = URLResourceValues()
566+
resourceValues.isHidden = true
567+
for filename in [ hiddenItem1, hiddenItem2, hiddenItem3, hiddenItem4] {
568+
XCTAssertTrue(fm.createFile(atPath: filename, contents: Data(), attributes: nil), "Cant create file '\(filename)'")
569+
#if os(Windows)
570+
do {
571+
var url = URL(fileURLWithPath: filename)
572+
try url.setResourceValues(resourceValues)
573+
} catch {
574+
XCTFail("Couldn't make \(filename) a hidden file")
575+
}
576+
#endif
577+
}
578+
579+
#if os(Windows)
580+
do {
581+
var hiddenURL1 = URL(fileURLWithPath: hiddenDir1)
582+
var hiddenURL2 = URL(fileURLWithPath: hiddenDir2)
583+
try hiddenURL1.setResourceValues(resourceValues)
584+
try hiddenURL2.setResourceValues(resourceValues)
585+
} catch {
586+
XCTFail("Couldn't make \(hiddenDir1) and \(hiddenDir2) hidden directories")
587+
}
588+
#endif
589+
562590
if let foundItems = directoryItems(options: []) {
563591
XCTAssertEqual(foundItems.count, fileLevels.count)
564592
for (name, level) in foundItems {

0 commit comments

Comments
 (0)