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
|
/*
This source file is part of the Swift.org open source project
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
/// Check if the given code unit needs shell escaping.
//
/// - Parameters:
/// - codeUnit: The code unit to be checked.
///
/// - Returns: True if shell escaping is not needed.
private func inShellAllowlist(_ codeUnit: UInt8) -> Bool {
#if os(Windows)
if codeUnit == UInt8(ascii: "\\") {
return true
}
#endif
switch codeUnit {
case UInt8(ascii: "a")...UInt8(ascii: "z"),
UInt8(ascii: "A")...UInt8(ascii: "Z"),
UInt8(ascii: "0")...UInt8(ascii: "9"),
UInt8(ascii: "-"),
UInt8(ascii: "_"),
UInt8(ascii: "/"),
UInt8(ascii: ":"),
UInt8(ascii: "@"),
UInt8(ascii: "%"),
UInt8(ascii: "+"),
UInt8(ascii: "="),
UInt8(ascii: "."),
UInt8(ascii: ","):
return true
default:
return false
}
}
extension String {
/// Creates a shell escaped string. If the string does not need escaping, returns the original string.
/// Otherwise escapes using single quotes on Unix and double quotes on Windows. For example:
/// hello -> hello, hello$world -> 'hello$world', input A -> 'input A'
///
/// - Returns: Shell escaped string.
public func spm_shellEscaped() -> String {
// If all the characters in the string are in the allow list then no need to escape.
guard let pos = utf8.firstIndex(where: { !inShellAllowlist($0) }) else {
return self
}
#if os(Windows)
let quoteCharacter: Character = "\""
let escapedQuoteCharacter = "\"\""
#else
let quoteCharacter: Character = "'"
let escapedQuoteCharacter = "'\\''"
#endif
// If there are no quote characters then we can just wrap the string within the quotes.
guard let quotePos = utf8[pos...].firstIndex(of: quoteCharacter.asciiValue!) else {
return String(quoteCharacter) + self + String(quoteCharacter)
}
// Otherwise iterate and escape all the single quotes.
var newString = String(quoteCharacter) + String(self[..<quotePos])
for char in self[quotePos...] {
if char == quoteCharacter {
newString += escapedQuoteCharacter
} else {
newString += String(char)
}
}
newString += String(quoteCharacter)
return newString
}
/// Shell escapes the current string. This method is mutating version of shellEscaped().
public mutating func spm_shellEscape() {
self = spm_shellEscaped()
}
}
/// Type of localized join operator.
public enum LocalizedJoinType: String {
/// A conjunction join operator (ie: blue, white, and red)
case conjunction = "and"
/// A disjunction join operator (ie: blue, white, or red)
case disjunction = "or"
}
//FIXME: Migrate to DiagnosticFragmentBuilder
public extension Array where Element == String {
/// Returns a localized list of terms representing a conjunction or disjunction.
func spm_localizedJoin(type: LocalizedJoinType) -> String {
var result = ""
for (i, item) in enumerated() {
// Add the separator, if necessary.
if i == count - 1 {
switch count {
case 1:
break
case 2:
result += " \(type.rawValue) "
default:
result += ", \(type.rawValue) "
}
} else if i != 0 {
result += ", "
}
result += item
}
return result
}
}
|