mirror of
https://github.com/apple/swift-foundation.git
synced 2025-05-26 08:01:20 +08:00
* (139469462) ObjC API for directory search paths should use Swift implementation * Fix trash directory bug
178 lines
7.4 KiB
Swift
178 lines
7.4 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if FOUNDATION_FRAMEWORK
|
|
internal import Foundation_Private.NSPathUtilities
|
|
internal import DarwinPrivate.dirhelper
|
|
internal import DarwinPrivate.sysdir
|
|
internal import _ForSwiftFoundation
|
|
#endif
|
|
|
|
#if canImport(Darwin)
|
|
import Darwin.sysdir
|
|
|
|
private func foundation_sysdir_start_search_path_enumeration(_ directory: UInt, _ domainMask: UInt) -> sysdir_search_path_enumeration_state {
|
|
#if FOUNDATION_FRAMEWORK
|
|
sysdir_start_search_path_enumeration_private(
|
|
sysdir_search_path_directory_t(UInt32(truncatingIfNeeded: directory)),
|
|
sysdir_search_path_domain_private_mask_t(rawValue: UInt32(truncatingIfNeeded: domainMask))
|
|
)
|
|
#else
|
|
sysdir_start_search_path_enumeration(
|
|
sysdir_search_path_directory_t(UInt32(truncatingIfNeeded: directory)),
|
|
sysdir_search_path_domain_mask_t(rawValue: UInt32(truncatingIfNeeded: domainMask))
|
|
)
|
|
#endif
|
|
}
|
|
|
|
struct _DarwinSearchPathsSequence: Sequence {
|
|
let directory: FileManager.SearchPathDirectory
|
|
let domainMask: FileManager.SearchPathDomainMask
|
|
|
|
final class Iterator: IteratorProtocol {
|
|
let directory: FileManager.SearchPathDirectory
|
|
let domainMask: FileManager.SearchPathDomainMask
|
|
|
|
private enum State {
|
|
case sysdir(sysdir_search_path_enumeration_state)
|
|
#if os(macOS) && FOUNDATION_FRAMEWORK
|
|
case special(FileManager.SearchPathDomainMask)
|
|
#endif
|
|
}
|
|
private var state: State
|
|
|
|
init(directory: FileManager.SearchPathDirectory, domainMask: FileManager.SearchPathDomainMask) {
|
|
self.directory = directory
|
|
self.domainMask = domainMask
|
|
|
|
switch directory {
|
|
#if os(macOS) && FOUNDATION_FRAMEWORK
|
|
case .trashDirectory:
|
|
state = .special(domainMask.intersection([.userDomainMask, .localDomainMask]))
|
|
case ._homeDirectory, .applicationScriptsDirectory:
|
|
state = .special(domainMask.intersection(.userDomainMask))
|
|
#endif
|
|
|
|
default:
|
|
state = .sysdir(foundation_sysdir_start_search_path_enumeration(directory.rawValue, domainMask.rawValue))
|
|
}
|
|
}
|
|
|
|
func next() -> String? {
|
|
switch state {
|
|
case .sysdir(let sysdirState):
|
|
return withUnsafeTemporaryAllocation(of: CChar.self, capacity: FileManager.MAX_PATH_SIZE) { buffer in
|
|
let newState = sysdir_get_next_search_path_enumeration(sysdirState, buffer.baseAddress!)
|
|
state = .sysdir(newState)
|
|
if newState != 0 {
|
|
return FileManager.default.string(withFileSystemRepresentation: buffer.baseAddress!, length: strlen(buffer.baseAddress!))
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
#if os(macOS) && FOUNDATION_FRAMEWORK
|
|
case .special(var mask):
|
|
defer { state = .special(mask) }
|
|
while let currentMask = mask.firstMask {
|
|
mask.remove(currentMask)
|
|
if let result = _specialFind(directory, in: currentMask) {
|
|
return result
|
|
}
|
|
}
|
|
return nil
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if os(macOS) && FOUNDATION_FRAMEWORK
|
|
private func _specialFindReturn(_ buffer: UnsafeMutableBufferPointer<CChar>) -> String? {
|
|
guard buffer.baseAddress!.pointee != 0 else { return nil }
|
|
|
|
let path = String(cString: buffer.baseAddress!)
|
|
// strip trailing slashes because NSPathUtilities doesn't return paths with trailing slashes.
|
|
guard let endIndex = path.unicodeScalars.lastIndex(where: { $0 != "/" }) else {
|
|
// It's only slashes, so just return a single slash
|
|
return "/"
|
|
}
|
|
return String(path[...endIndex])
|
|
}
|
|
|
|
private func _specialFind(_ directory: FileManager.SearchPathDirectory, in mask: FileManager.SearchPathDomainMask) -> String? {
|
|
withUnsafeTemporaryAllocation(of: CChar.self, capacity: FileManager.MAX_PATH_SIZE) { cpath in
|
|
switch (directory, mask) {
|
|
case (.trashDirectory, .userDomainMask):
|
|
// get the trash relative to the home directory without checking to see if the directory exists
|
|
return String.homeDirectoryPath().withFileSystemRepresentation { homePathPtr -> String? in
|
|
guard let homePathPtr else { return nil }
|
|
if __user_relative_dirname(geteuid(), DIRHELPER_RELATIVE_TRASH, homePathPtr, cpath.baseAddress!, FileManager.MAX_PATH_SIZE) != nil {
|
|
var buff = stat()
|
|
if lstat(cpath.baseAddress!, &buff) == 0 {
|
|
return _specialFindReturn(cpath)?.abbreviatingWithTildeInPath
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
case (.trashDirectory, .localDomainMask):
|
|
// get the trash on the boot volume without checking to see if the directory exists
|
|
if __user_relative_dirname(geteuid(), DIRHELPER_RELATIVE_TRASH, "/", cpath.baseAddress!, FileManager.MAX_PATH_SIZE) != nil {
|
|
var buff = stat()
|
|
if lstat(cpath.baseAddress!, &buff) == 0 {
|
|
return _specialFindReturn(cpath)
|
|
}
|
|
}
|
|
return nil
|
|
|
|
case (.applicationScriptsDirectory, .userDomainMask):
|
|
guard let id = _NSCodeSigningIdentifierForCurrentProcess() else {
|
|
return nil
|
|
}
|
|
return "\("~".replacingTildeWithRealHomeDirectory)/Library/Application Scripts/\(id)"
|
|
|
|
case (._homeDirectory, .userDomainMask):
|
|
return "~"
|
|
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func makeIterator() -> Iterator {
|
|
Iterator(directory: directory, domainMask: domainMask)
|
|
}
|
|
}
|
|
|
|
#if os(macOS) && FOUNDATION_FRAMEWORK
|
|
@_cdecl("_NSRealHomeDirectory")
|
|
internal func _NSRealHomeDirectory() -> String {
|
|
"~".replacingTildeWithRealHomeDirectory
|
|
}
|
|
|
|
extension String {
|
|
internal var replacingTildeWithRealHomeDirectory: String {
|
|
guard self == "~" || self.hasPrefix("~/") else {
|
|
return self
|
|
}
|
|
let euid = geteuid()
|
|
let trueUid = euid == 0 ? getuid() : euid
|
|
guard let home = Platform.homeDirectory(forUID: trueUid) else {
|
|
return self
|
|
}
|
|
return home.appendingPathComponent(String(self.dropFirst()))
|
|
}
|
|
}
|
|
#endif // os(macOS) && FOUNDATION_FRAMEWORK
|
|
#endif // canImport(Darwin)
|