@@ -16,6 +16,45 @@ import Darwin
1616import Glibc
1717#endif
1818
19+ func argumentNeedsQuoting( _ argument: String ) -> Bool {
20+ if argument. isEmpty { return false }
21+ let chars : Set < Character > = Set ( " \t \" &'()*<> \\ `^| \n " )
22+ return argument. firstIndex ( where: { chars. contains ( $0) } ) != argument. endIndex
23+ }
24+
25+ func quoteArgument( _ argument: String ) -> String {
26+ #if os(Windows)
27+ var unquoted : Substring = argument [ ... ]
28+ var quoted : String = " \" "
29+ while !unquoted. isEmpty {
30+ guard let firstNonBS = unquoted. firstIndex ( where: { $0 != " \\ " } ) else {
31+ // The rest of the string is backslashes. Escape all of them and exit.
32+ ( 0 ..< ( 2 * unquoted. count) ) . forEach { _ in quoted += " \\ " }
33+ break
34+ }
35+
36+ let bsCount = unquoted. distance ( from: unquoted. startIndex, to: firstNonBS)
37+ if unquoted [ firstNonBS] == " \" " {
38+ // This is an embedded quote. Escape all preceding backslashes, then
39+ // add one additional backslash to escape the quote.
40+ ( 0 ..< ( 2 * bsCount + 1 ) ) . forEach { _ in quoted += " \\ " }
41+ quoted += " \" "
42+ } else {
43+ // This is just a normal character. Don't escape any of the preceding
44+ // backslashes, just append them as they are and then append the
45+ // character.
46+ ( 0 ..< bsCount) . forEach { _ in quoted += " \\ " }
47+ quoted += " \( unquoted [ firstNonBS] ) "
48+ }
49+
50+ unquoted = unquoted. dropFirst ( bsCount + 1 )
51+ }
52+ return quoted + " \" "
53+ #else
54+ return " ' " + argument + " ' "
55+ #endif
56+ }
57+
1958#if canImport(Darwin) || os(Linux) || os(Android) || os(OpenBSD)
2059// Adapted from llvm::sys::commandLineFitsWithinSystemLimits.
2160func commandLineFitsWithinSystemLimits( path: String , args: [ String ] ) -> Bool {
@@ -49,45 +88,10 @@ func commandLineFitsWithinSystemLimits(path: String, args: [String]) -> Bool {
4988#elseif os(Windows)
5089func commandLineFitsWithinSystemLimits( path: String , args: [ String ] ) -> Bool {
5190 func flattenWindowsCommandLine( _ arguments: [ String ] ) -> String {
52- func argNeedsQuoting( _ argument: String ) -> Bool {
53- if argument. isEmpty { return false }
54- let chars : Set < Character > = Set ( " \t \" &'()*<> \\ `^| \n " )
55- return argument. firstIndex ( where: { chars. contains ( $0) } ) != argument. endIndex
56- }
57-
58- func quote( _ argument: String ) -> String {
59- var unquoted : Substring = argument [ ... ]
60- var quoted : String = " \" "
61- while !unquoted. isEmpty {
62- guard let firstNonBS = unquoted. firstIndex ( where: { $0 != " \\ " } ) else {
63- // The rest of the string is backslashes. Escape all of them and exit.
64- ( 0 ..< ( 2 * unquoted. count) ) . forEach { _ in quoted += " \\ " }
65- break
66- }
67-
68- let bsCount = unquoted. distance ( from: unquoted. startIndex, to: firstNonBS)
69- if unquoted [ firstNonBS] == " \" " {
70- // This is an embedded quote. Escape all preceding backslashes, then
71- // add one additional backslash to escape the quote.
72- ( 0 ..< ( 2 * bsCount + 1 ) ) . forEach { _ in quoted += " \\ " }
73- quoted += " \" "
74- } else {
75- // This is just a normal character. Don't escape any of the preceding
76- // backslashes, just append them as they are and then append the
77- // character.
78- ( 0 ..< bsCount) . forEach { _ in quoted += " \\ " }
79- quoted += " \( unquoted [ firstNonBS] ) "
80- }
81-
82- unquoted = unquoted. dropFirst ( bsCount + 1 )
83- }
84- return quoted + " \" "
85- }
86-
8791 var quoted : String = " "
8892 for arg in arguments {
89- if argNeedsQuoting ( arg) {
90- quoted += quote ( arg)
93+ if argumentNeedsQuoting ( arg) {
94+ quoted += quoteArgument ( arg)
9195 } else {
9296 quoted += arg
9397 }
0 commit comments