mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
185 lines
6.6 KiB
Swift
185 lines
6.6 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftShims
|
|
|
|
#if SWIFT_STDLIB_HAS_COMMANDLINE
|
|
|
|
@_silgen_name("_swift_stdlib_getUnsafeArgvArgc")
|
|
internal func _swift_stdlib_getUnsafeArgvArgc(_: UnsafeMutablePointer<Int32>)
|
|
-> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>
|
|
|
|
@_silgen_name("_swift_stdlib_copyExecutablePath")
|
|
private func _copyExecutablePath() -> UnsafeMutablePointer<CChar>
|
|
|
|
@_silgen_name("_swift_stdlib_deallocExecutablePath")
|
|
private func _deallocExecutablePath(_ path: UnsafeMutablePointer<CChar>)
|
|
|
|
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)
|
|
@_extern(c, "_NSGetExecutablePath")
|
|
@usableFromInline
|
|
func _NSGetExecutablePath(
|
|
_ buf: UnsafeMutablePointer<CChar>,
|
|
_ bufsize: UnsafeMutablePointer<UInt32>
|
|
) -> CInt
|
|
#endif
|
|
|
|
/// Command-line arguments for the current process.
|
|
@frozen // namespace
|
|
public enum CommandLine: ~BitwiseCopyable {}
|
|
|
|
extension CommandLine {
|
|
/// The backing static variable for argument count may come either from the
|
|
/// entry point or it may need to be computed e.g. if we're in the REPL.
|
|
@usableFromInline
|
|
internal static var _argc: Int32 = 0
|
|
|
|
/// The backing static variable for arguments may come either from the
|
|
/// entry point or it may need to be computed e.g. if we're in the REPL.
|
|
///
|
|
/// Care must be taken to ensure that `_swift_stdlib_getUnsafeArgvArgc` is
|
|
/// not invoked more times than is necessary (at most once).
|
|
@usableFromInline
|
|
internal static var _unsafeArgv:
|
|
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>
|
|
= unsafe _swift_stdlib_getUnsafeArgvArgc(&_argc)
|
|
|
|
/// Access to the raw argc value from C.
|
|
public static var argc: Int32 {
|
|
// We intentionally ignore the argc value given to us from
|
|
// '_swift_stdlib_getUnsafeArgvArgc' because argv and argc are mutable, so
|
|
// someone can mutate the contents of argv and never update the argc value.
|
|
// This results in an out of sync argc which can lead to crashes on first
|
|
// access to 'CommandLine.arguments' due to attempting to read '0 ..< argc'
|
|
// strings.
|
|
//
|
|
// Note: It's still entirely possible that someone may update argv after
|
|
// this iteration and before we actually read argv, but we have no control
|
|
// over synchronizing access to argc and argv.
|
|
var argc: Int32 = 0
|
|
|
|
while let _ = unsafe _unsafeArgv[Int(argc)] {
|
|
argc += 1
|
|
}
|
|
|
|
return argc
|
|
}
|
|
|
|
/// Access to the raw argv value from C.
|
|
///
|
|
/// The value of this property is a `nil`-terminated C array. Including the
|
|
/// trailing `nil`, there are ``argc`` `+ 1` elements in the array.
|
|
///
|
|
/// - Note: Accessing the argument vector through this pointer is unsafe.
|
|
/// Where possible, use ``arguments`` instead.
|
|
public static var unsafeArgv:
|
|
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
|
|
return unsafe _unsafeArgv
|
|
}
|
|
|
|
// This is extremely unsafe and allows for concurrent writes with no
|
|
// synchronization to the underlying data. In a future version of Swift you
|
|
// will not be able to write to 'CommandLine.arguments'.
|
|
static nonisolated(unsafe) var _arguments: [String] = (0 ..< Int(argc)).map {
|
|
unsafe String(cString: _unsafeArgv[$0]!)
|
|
}
|
|
|
|
/// An array that provides access to this program's command line arguments.
|
|
///
|
|
/// Use `CommandLine.arguments` to access the command line arguments used
|
|
/// when executing the current program. The name of the executed program is
|
|
/// the first argument.
|
|
///
|
|
/// The following example shows a command line executable that squares the
|
|
/// integer given as an argument.
|
|
///
|
|
/// if CommandLine.arguments.count == 2,
|
|
/// let number = Int(CommandLine.arguments[1]) {
|
|
/// print("\(number) x \(number) is \(number * number)")
|
|
/// } else {
|
|
/// print(
|
|
/// """
|
|
/// Error: Please provide a number to square.
|
|
/// Usage: command <number>
|
|
/// """
|
|
/// )
|
|
/// }
|
|
///
|
|
/// Running the program results in the following output:
|
|
///
|
|
/// $ command 5
|
|
/// 5 x 5 is 25
|
|
/// $ command ZZZ
|
|
/// Error: Please provide a number to square.
|
|
/// Usage: command <number>
|
|
public static var arguments: [String] {
|
|
get {
|
|
_arguments
|
|
}
|
|
|
|
@available(*, deprecated, message: "Do not modify CommandLine.arguments. It will become read-only in a future version of Swift.")
|
|
@available(swift, obsoleted: 6.0)
|
|
set {
|
|
_arguments = newValue
|
|
}
|
|
}
|
|
|
|
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)
|
|
/// The path to the current executable.
|
|
///
|
|
/// - Important: On some systems, it is possible to move an executable file on
|
|
/// disk while it is running. If the current executable file is moved, the
|
|
/// value of this property is not updated to its new path.
|
|
@_unavailableInEmbedded
|
|
@_alwaysEmitIntoClient
|
|
public static var executablePath: String { // NOTE: can't be AEIC and stored!
|
|
// _NSGetExecutablePath() returns non-zero if the provided buffer is too
|
|
// small and updates its *bufsize argument to the required value, so we can
|
|
// just set a reasonable initial value, then loop and try again on failure.
|
|
var byteCount = UInt32(128)
|
|
while true {
|
|
let result: String? = unsafe withUnsafeTemporaryAllocation(
|
|
of: CChar.self,
|
|
capacity: Int(byteCount)
|
|
) { buffer in
|
|
if (unsafe 0 == _NSGetExecutablePath(buffer.baseAddress!, &byteCount)) {
|
|
return unsafe String(cString: buffer.baseAddress!)
|
|
}
|
|
return nil
|
|
}
|
|
if let result {
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
/// The path to the current executable.
|
|
///
|
|
/// - Important: On some systems, it is possible to move an executable file on
|
|
/// disk while it is running. If the current executable file is moved, the
|
|
/// value of this property is not updated to its new path.
|
|
@_unavailableInEmbedded
|
|
#if os(WASI)
|
|
@available(*, unavailable, message: "Unavailable on WASI")
|
|
#endif
|
|
public static let executablePath: String = {
|
|
// FIXME: avoid needing to allocate and free a temp C string (if possible)
|
|
let cString = unsafe _copyExecutablePath()
|
|
defer {
|
|
unsafe _deallocExecutablePath(cString)
|
|
}
|
|
return unsafe String(cString: cString)
|
|
}()
|
|
#endif
|
|
}
|
|
#endif // SWIFT_STDLIB_HAS_COMMANDLINE
|