File: StringConversions.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 (126 lines) | stat: -rw-r--r-- 3,998 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
/*
 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
    }
}