



















@@ -0,0 +1,278 @@
1+import Foundation
2+3+enum ExecInlineCommandParser {
4+struct Match {
5+let tokenIndex: Int
6+let inlineCommand: String?
7+let valueTokenOffset: Int
8+9+init(tokenIndex: Int, inlineCommand: String?, valueTokenOffset: Int = 1) {
10+self.tokenIndex = tokenIndex
11+self.inlineCommand = inlineCommand
12+self.valueTokenOffset = valueTokenOffset
13+}
14+}
15+16+private struct CombinedCommandFlag {
17+let attachedCommand: String?
18+let separateValueCount: Int
19+}
20+21+private static let posixShellOptionsWithSeparateValues = Set([
22+"--init-file",
23+"--rcfile",
24+"-O",
25+"-o",
26+"+O",
27+"+o",
28+])
29+30+static func hasPosixInteractiveStartupBeforeInlineCommand(
31+ _ argv: [String],
32+ flags: Set<String>) -> Bool
33+{
34+var idx = 1
35+var sawInteractiveMode = false
36+while idx < argv.count {
37+let token = argv[idx].trimmingCharacters(in: .whitespacesAndNewlines)
38+if token.isEmpty {
39+ idx += 1
40+continue
41+}
42+if token == "--" {
43+return false
44+}
45+if self.isPosixInteractiveModeOption(token) {
46+ sawInteractiveMode = true
47+}
48+if flags.contains(token) || self.isCombinedCommandFlag(token) {
49+return sawInteractiveMode
50+}
51+if !token.hasPrefix("-"), !token.hasPrefix("+") {
52+return false
53+}
54+let combinedValueCount = self.combinedSeparateValueOptionCount(token)
55+if combinedValueCount > 0 {
56+ idx += 1 + combinedValueCount
57+continue
58+}
59+if self.consumesSeparateValue(token) {
60+ idx += 2
61+continue
62+}
63+ idx += 1
64+}
65+return false
66+}
67+68+static func hasPosixLoginStartupBeforeInlineCommand(
69+ _ argv: [String],
70+ flags: Set<String>) -> Bool
71+{
72+var idx = 1
73+var sawLoginMode = false
74+while idx < argv.count {
75+let token = argv[idx].trimmingCharacters(in: .whitespacesAndNewlines)
76+if token.isEmpty {
77+ idx += 1
78+continue
79+}
80+if token == "--" {
81+return false
82+}
83+if token == "--login" || self.isPosixShortOption(token, containing: "l") {
84+ sawLoginMode = true
85+}
86+if flags.contains(token) || self.isCombinedCommandFlag(token) {
87+return sawLoginMode
88+}
89+if !token.hasPrefix("-"), !token.hasPrefix("+") {
90+return false
91+}
92+let combinedValueCount = self.combinedSeparateValueOptionCount(token)
93+if combinedValueCount > 0 {
94+ idx += 1 + combinedValueCount
95+continue
96+}
97+if self.consumesSeparateValue(token) {
98+ idx += 2
99+continue
100+}
101+ idx += 1
102+}
103+return false
104+}
105+106+static func hasFishInitCommandOption(_ argv: [String]) -> Bool {
107+var idx = 1
108+while idx < argv.count {
109+let token = argv[idx].trimmingCharacters(in: .whitespacesAndNewlines)
110+if token.isEmpty {
111+ idx += 1
112+continue
113+}
114+if token == "--" {
115+return false
116+}
117+if token == "-C" || token == "--init-command" {
118+return true
119+}
120+if token.hasPrefix("-C"), token != "-C" {
121+return true
122+}
123+if token.hasPrefix("--init-command=") {
124+return true
125+}
126+if !token.hasPrefix("-"), !token.hasPrefix("+") {
127+return false
128+}
129+ idx += 1
130+}
131+return false
132+}
133+134+static func hasFishAttachedCommandOption(_ argv: [String]) -> Bool {
135+var idx = 1
136+while idx < argv.count {
137+let token = argv[idx].trimmingCharacters(in: .whitespacesAndNewlines)
138+if token.isEmpty {
139+ idx += 1
140+continue
141+}
142+if token == "--" {
143+return false
144+}
145+if token.hasPrefix("-c"), token != "-c" {
146+return true
147+}
148+if !token.hasPrefix("-"), !token.hasPrefix("+") {
149+return false
150+}
151+ idx += 1
152+}
153+return false
154+}
155+156+static func findMatch(
157+ _ argv: [String],
158+ flags: Set<String>,
159+ allowCombinedC: Bool) -> Match?
160+{
161+var idx = 1
162+while idx < argv.count {
163+let token = argv[idx].trimmingCharacters(in: .whitespacesAndNewlines)
164+if token.isEmpty {
165+ idx += 1
166+continue
167+}
168+if token == "--" {
169+break
170+}
171+let comparableToken = allowCombinedC ? token : token.lowercased()
172+if flags.contains(comparableToken) {
173+return Match(tokenIndex: idx, inlineCommand: nil)
174+}
175+if allowCombinedC, let combined = self.parseCombinedCommandFlag(token) {
176+if let attachedCommand = combined.attachedCommand {
177+return Match(tokenIndex: idx, inlineCommand: attachedCommand, valueTokenOffset: 0)
178+}
179+return Match(
180+ tokenIndex: idx,
181+ inlineCommand: nil,
182+ valueTokenOffset: 1 + combined.separateValueCount)
183+}
184+if allowCombinedC, !token.hasPrefix("-"), !token.hasPrefix("+") {
185+break
186+}
187+let combinedValueCount = allowCombinedC ? self.combinedSeparateValueOptionCount(token) : 0
188+if combinedValueCount > 0 {
189+ idx += 1 + combinedValueCount
190+continue
191+}
192+if allowCombinedC, self.consumesSeparateValue(token) {
193+ idx += 2
194+continue
195+}
196+ idx += 1
197+}
198+return nil
199+}
200+201+static func extractInlineCommand(
202+ _ argv: [String],
203+ flags: Set<String>,
204+ allowCombinedC: Bool) -> String?
205+{
206+guard let match = self.findMatch(argv, flags: flags, allowCombinedC: allowCombinedC) else {
207+return nil
208+}
209+if let inlineCommand = match.inlineCommand {
210+return inlineCommand
211+}
212+let nextIndex = match.tokenIndex + match.valueTokenOffset
213+let payload = nextIndex < argv.count
214+? argv[nextIndex].trimmingCharacters(in: .whitespacesAndNewlines)
215+: ""
216+return payload.isEmpty ? nil : payload
217+}
218+219+private static func isCombinedCommandFlag(_ token: String) -> Bool {
220+self.parseCombinedCommandFlag(token) != nil
221+}
222+223+private static func parseCombinedCommandFlag(_ token: String) -> CombinedCommandFlag? {
224+let chars = Array(token)
225+guard chars.count >= 2, chars[0] == "-", chars[1] != "-" else {
226+return nil
227+}
228+let optionChars = Array(chars.dropFirst())
229+guard let commandFlagIndex = optionChars.firstIndex(of: "c") else {
230+return nil
231+}
232+if optionChars.contains("-") {
233+return nil
234+}
235+let suffix = String(optionChars.dropFirst(commandFlagIndex + 1))
236+if !suffix.isEmpty,
237+ suffix.range(of: #"[^A-Za-z]"#, options: .regularExpression) != nil
238+{
239+return CombinedCommandFlag(attachedCommand: suffix, separateValueCount: 0)
240+}
241+let separateValueCount = optionChars.reduce(0) { count, char in
242+ count + ((char == "o" || char == "O") ? 1 : 0)
243+}
244+return CombinedCommandFlag(attachedCommand: nil, separateValueCount: separateValueCount)
245+}
246+247+private static func combinedSeparateValueOptionCount(_ token: String) -> Int {
248+let chars = Array(token)
249+guard chars.count >= 2, chars[0] == "-" || chars[0] == "+", chars[1] != "-" else {
250+return 0
251+}
252+if chars.dropFirst().contains("-") {
253+return 0
254+}
255+return chars.dropFirst().reduce(0) { count, char in
256+ count + ((char == "o" || char == "O") ? 1 : 0)
257+}
258+}
259+260+private static func consumesSeparateValue(_ token: String) -> Bool {
261+self.posixShellOptionsWithSeparateValues.contains(token)
262+}
263+264+private static func isPosixInteractiveModeOption(_ token: String) -> Bool {
265+ token == "--interactive" || self.isPosixShortOption(token, containing: "i")
266+}
267+268+private static func isPosixShortOption(_ token: String, containing option: Character) -> Bool {
269+let chars = Array(token)
270+guard chars.count >= 2, chars[0] == "-", chars[1] != "-" else {
271+return false
272+}
273+if chars.dropFirst().contains("-") {
274+return false
275+}
276+return chars.dropFirst().contains(option)
277+}
278+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。