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
|
//===------------ System.swift - Swift Driver System Utilities ------------===//
//
// 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 https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if canImport(Darwin)
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(Android)
import Android
#endif
func argumentNeedsQuoting(_ argument: String) -> Bool {
if argument.isEmpty { return false }
let chars: Set<Character> = Set("\t \"&'()*<>\\`^|\n")
return argument.firstIndex(where: { chars.contains($0) }) != argument.endIndex
}
func quoteArgument(_ argument: String) -> String {
#if os(Windows)
var unquoted: Substring = argument[...]
var quoted: String = "\""
while !unquoted.isEmpty {
guard let firstNonBS = unquoted.firstIndex(where: { $0 != "\\" }) else {
// The rest of the string is backslashes. Escape all of them and exit.
(0 ..< (2 * unquoted.count)).forEach { _ in quoted += "\\" }
break
}
let bsCount = unquoted.distance(from: unquoted.startIndex, to: firstNonBS)
if unquoted[firstNonBS] == "\"" {
// This is an embedded quote. Escape all preceding backslashes, then
// add one additional backslash to escape the quote.
(0 ..< (2 * bsCount + 1)).forEach { _ in quoted += "\\" }
quoted += "\""
} else {
// This is just a normal character. Don't escape any of the preceding
// backslashes, just append them as they are and then append the
// character.
(0 ..< bsCount).forEach { _ in quoted += "\\" }
quoted += "\(unquoted[firstNonBS])"
}
unquoted = unquoted.dropFirst(bsCount + 1)
}
return quoted + "\""
#else
return "'" + argument + "'"
#endif
}
#if canImport(Darwin) || os(Linux) || os(Android) || os(OpenBSD)
// Adapted from llvm::sys::commandLineFitsWithinSystemLimits.
func commandLineFitsWithinSystemLimits(path: String, args: [String]) -> Bool {
let upperBound = sysconf(Int32(_SC_ARG_MAX))
guard upperBound != -1 else {
// The system reports no limit.
return true
}
// The lower bound for ARG_MAX on a POSIX system
let lowerBound = Int(_POSIX_ARG_MAX)
// This the same baseline used by xargs.
let baseline = 128 * 1024
var effectiveArgMax = max(min(baseline, upperBound), lowerBound)
// Conservatively assume environment variables consume half the space.
effectiveArgMax /= 2
var commandLineLength = path.utf8.count + 1
for arg in args {
#if os(Linux) || os(Android)
// Linux limits the length of each individual argument to MAX_ARG_STRLEN.
// There is no available constant, so it is hardcoded here.
guard arg.utf8.count < 32 * 4096 else {
return false
}
#endif
commandLineLength += arg.utf8.count + 1
}
return commandLineLength < effectiveArgMax
}
#elseif os(Windows)
func commandLineFitsWithinSystemLimits(path: String, args: [String]) -> Bool {
func flattenWindowsCommandLine(_ arguments: [String]) -> String {
var quoted: String = ""
for arg in arguments {
if argumentNeedsQuoting(arg) {
quoted += quoteArgument(arg)
} else {
quoted += arg
}
quoted += " "
}
return quoted
}
let arguments: [String] = [path] + args
let commandLine = flattenWindowsCommandLine(arguments)
// `CreateProcessW` requires the length of `lpCommandLine` not exceed 32767
// characters, including the Unicode terminating null character. We use a
// smaller value to reduce risk of getting invalid command line due to
// unaccounted factors.
return commandLine.count <= 32000
}
#else
func commandLineFitsWithinSystemLimits(path: String, args: [String]) -> Bool {
#warning("missing implementation for current platform")
return true
}
#endif
|