File: FileManager%2BDarwinSearchPaths.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (172 lines) | stat: -rw-r--r-- 7,470 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//===----------------------------------------------------------------------===//
//
// 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.union([.userDomainMask, .localDomainMask]))
            case ._homeDirectory, .applicationScriptsDirectory:
                state = .special(domainMask.union(.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
extension String {
    internal var replacingTildeWithRealHomeDirectory: String {
        guard self == "~" || self.hasPrefix("~/") else {
            return self
        }
        let euid = geteuid()
        let trueUid = euid == 0 ? getuid() : euid
        guard let name = Platform.name(forUID: trueUid) else {
            return self
        }
        return name.appendingPathComponent(String(self.dropFirst()))
    }
}
#endif // os(macOS) && FOUNDATION_FRAMEWORK
#endif // canImport(Darwin)