File: System.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 (120 lines) | stat: -rw-r--r-- 4,100 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
//===------------ 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