2017-08-15 21:45:21 +02:00
|
|
|
//! Global initialization and retrieval of command line arguments.
|
2016-09-29 22:00:44 +00:00
|
|
|
//!
|
|
|
|
//! On some platforms these are stored during runtime startup,
|
|
|
|
//! and on some they are retrieved from the system on demand.
|
|
|
|
|
|
|
|
#![allow(dead_code)] // runtime init functions not used during testing
|
|
|
|
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::ffi::OsString;
|
2021-04-21 23:51:28 +02:00
|
|
|
use crate::fmt;
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::vec;
|
2016-09-29 22:00:44 +00:00
|
|
|
|
|
|
|
/// One-time global initialization.
|
|
|
|
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
|
|
|
imp::init(argc, argv)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the command line arguments
|
|
|
|
pub fn args() -> Args {
|
|
|
|
imp::args()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Args {
|
|
|
|
iter: vec::IntoIter<OsString>,
|
|
|
|
}
|
|
|
|
|
2021-04-14 04:04:27 +02:00
|
|
|
impl !Send for Args {}
|
|
|
|
impl !Sync for Args {}
|
|
|
|
|
2021-04-21 23:51:28 +02:00
|
|
|
impl fmt::Debug for Args {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
self.iter.as_slice().fmt(f)
|
2017-06-21 15:40:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-29 22:00:44 +00:00
|
|
|
impl Iterator for Args {
|
|
|
|
type Item = OsString;
|
|
|
|
fn next(&mut self) -> Option<OsString> {
|
|
|
|
self.iter.next()
|
|
|
|
}
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.iter.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExactSizeIterator for Args {
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
self.iter.len()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DoubleEndedIterator for Args {
|
|
|
|
fn next_back(&mut self) -> Option<OsString> {
|
|
|
|
self.iter.next_back()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "android",
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "dragonfly",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd",
|
|
|
|
target_os = "solaris",
|
2020-04-13 23:37:22 +00:00
|
|
|
target_os = "illumos",
|
2016-09-29 22:00:44 +00:00
|
|
|
target_os = "emscripten",
|
2016-10-18 13:43:18 -07:00
|
|
|
target_os = "haiku",
|
2017-08-18 11:50:20 +02:00
|
|
|
target_os = "l4re",
|
2018-07-30 15:50:51 +02:00
|
|
|
target_os = "fuchsia",
|
2020-10-07 20:38:25 +02:00
|
|
|
target_os = "redox",
|
2022-02-09 23:54:38 -05:00
|
|
|
target_os = "vxworks",
|
2023-01-10 10:44:05 +01:00
|
|
|
target_os = "horizon",
|
2023-04-03 12:33:51 +08:00
|
|
|
target_os = "aix",
|
2023-01-10 10:44:05 +01:00
|
|
|
target_os = "nto",
|
2023-09-19 12:41:30 +02:00
|
|
|
target_os = "hurd",
|
2019-04-07 08:39:54 -06:00
|
|
|
))]
|
2016-09-29 22:00:44 +00:00
|
|
|
mod imp {
|
2019-02-11 04:23:21 +09:00
|
|
|
use super::Args;
|
|
|
|
use crate::ffi::{CStr, OsString};
|
2016-09-29 22:00:44 +00:00
|
|
|
use crate::os::unix::prelude::*;
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::ptr;
|
2020-07-03 14:13:22 -04:00
|
|
|
use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
|
2016-09-29 22:00:44 +00:00
|
|
|
|
2021-07-19 07:06:42 -07:00
|
|
|
// The system-provided argc and argv, which we store in static memory
|
|
|
|
// here so that we can defer the work of parsing them until its actually
|
|
|
|
// needed.
|
|
|
|
//
|
|
|
|
// Note that we never mutate argv/argc, the argv array, or the argv
|
|
|
|
// strings, which allows the code in this file to be very simple.
|
2020-07-03 14:13:22 -04:00
|
|
|
static ARGC: AtomicIsize = AtomicIsize::new(0);
|
|
|
|
static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
|
2016-09-29 22:00:44 +00:00
|
|
|
|
2019-11-21 13:42:25 -05:00
|
|
|
unsafe fn really_init(argc: isize, argv: *const *const u8) {
|
2021-07-19 07:06:42 -07:00
|
|
|
// These don't need to be ordered with each other or other stores,
|
|
|
|
// because they only hold the unmodified system-provide argv/argc.
|
2020-07-03 14:13:22 -04:00
|
|
|
ARGC.store(argc, Ordering::Relaxed);
|
|
|
|
ARGV.store(argv as *mut _, Ordering::Relaxed);
|
2016-09-29 22:00:44 +00:00
|
|
|
}
|
|
|
|
|
2019-11-21 13:42:25 -05:00
|
|
|
#[inline(always)]
|
2024-04-27 11:29:24 -07:00
|
|
|
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
|
|
|
// on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work"
|
|
|
|
// BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc.
|
|
|
|
really_init(argc, argv);
|
2019-11-21 13:42:25 -05:00
|
|
|
}
|
|
|
|
|
2019-11-21 11:47:01 -05:00
|
|
|
/// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
|
|
|
|
/// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
|
2019-11-21 08:21:14 -05:00
|
|
|
#[cfg(all(target_os = "linux", target_env = "gnu"))]
|
|
|
|
#[used]
|
2019-11-21 11:44:22 -05:00
|
|
|
#[link_section = ".init_array.00099"]
|
2019-11-21 08:21:14 -05:00
|
|
|
static ARGV_INIT_ARRAY: extern "C" fn(
|
|
|
|
crate::os::raw::c_int,
|
|
|
|
*const *const u8,
|
|
|
|
*const *const u8,
|
|
|
|
) = {
|
|
|
|
extern "C" fn init_wrapper(
|
|
|
|
argc: crate::os::raw::c_int,
|
|
|
|
argv: *const *const u8,
|
|
|
|
_envp: *const *const u8,
|
|
|
|
) {
|
|
|
|
unsafe {
|
2019-11-21 13:42:25 -05:00
|
|
|
really_init(argc as isize, argv);
|
2019-11-21 08:21:14 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
init_wrapper
|
|
|
|
};
|
|
|
|
|
2016-09-29 22:00:44 +00:00
|
|
|
pub fn args() -> Args {
|
2021-04-14 04:04:27 +02:00
|
|
|
Args { iter: clone().into_iter() }
|
2016-09-29 22:00:44 +00:00
|
|
|
}
|
|
|
|
|
2018-01-03 12:09:22 -08:00
|
|
|
fn clone() -> Vec<OsString> {
|
2016-09-29 22:00:44 +00:00
|
|
|
unsafe {
|
2021-07-19 07:06:42 -07:00
|
|
|
// Load ARGC and ARGV, which hold the unmodified system-provided
|
|
|
|
// argc/argv, so we can read the pointed-to memory without atomics
|
|
|
|
// or synchronization.
|
|
|
|
//
|
|
|
|
// If either ARGC or ARGV is still zero or null, then either there
|
|
|
|
// really are no arguments, or someone is asking for `args()`
|
|
|
|
// before initialization has completed, and we return an empty
|
|
|
|
// list.
|
2020-07-03 14:13:22 -04:00
|
|
|
let argv = ARGV.load(Ordering::Relaxed);
|
2021-07-17 18:31:51 -07:00
|
|
|
let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) };
|
2022-12-21 16:01:18 +02:00
|
|
|
let mut args = Vec::with_capacity(argc as usize);
|
|
|
|
for i in 0..argc {
|
|
|
|
let ptr = *argv.offset(i) as *const libc::c_char;
|
|
|
|
|
|
|
|
// Some C commandline parsers (e.g. GLib and Qt) are replacing already
|
|
|
|
// handled arguments in `argv` with `NULL` and move them to the end. That
|
|
|
|
// means that `argc` might be bigger than the actual number of non-`NULL`
|
|
|
|
// pointers in `argv` at this point.
|
|
|
|
//
|
|
|
|
// To handle this we simply stop iterating at the first `NULL` argument.
|
|
|
|
//
|
|
|
|
// `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments
|
|
|
|
// after the first `NULL` can safely be ignored.
|
|
|
|
if ptr.is_null() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let cstr = CStr::from_ptr(ptr);
|
|
|
|
args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
|
|
|
|
}
|
|
|
|
|
|
|
|
args
|
2016-09-29 22:00:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-18 20:45:45 -07:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "ios",
|
|
|
|
target_os = "watchos",
|
|
|
|
target_os = "visionos",
|
|
|
|
target_os = "tvos"
|
|
|
|
))]
|
2016-09-29 22:00:44 +00:00
|
|
|
mod imp {
|
|
|
|
use super::Args;
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::ffi::CStr;
|
2016-09-29 22:00:44 +00:00
|
|
|
|
|
|
|
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
pub fn args() -> Args {
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::os::unix::prelude::*;
|
2016-09-29 22:00:44 +00:00
|
|
|
extern "C" {
|
|
|
|
// These functions are in crt_externs.h.
|
|
|
|
fn _NSGetArgc() -> *mut libc::c_int;
|
|
|
|
fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
|
|
|
|
}
|
|
|
|
|
|
|
|
let vec = unsafe {
|
|
|
|
let (argc, argv) =
|
|
|
|
(*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char);
|
|
|
|
(0..argc as isize)
|
|
|
|
.map(|i| {
|
|
|
|
let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
|
|
|
|
OsStringExt::from_vec(bytes)
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
};
|
2021-04-14 04:04:27 +02:00
|
|
|
Args { iter: vec.into_iter() }
|
2016-09-29 22:00:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
|
|
|
|
// and use underscores in their names - they're most probably
|
2023-11-14 14:52:41 +01:00
|
|
|
// are considered private and therefore should be avoided.
|
|
|
|
// Here is another way to get arguments using the Objective-C
|
|
|
|
// runtime.
|
2016-09-29 22:00:44 +00:00
|
|
|
//
|
|
|
|
// In general it looks like:
|
|
|
|
// res = Vec::new()
|
|
|
|
// let args = [[NSProcessInfo processInfo] arguments]
|
|
|
|
// for i in (0..[args count])
|
|
|
|
// res.push([args objectAtIndex:i])
|
|
|
|
// res
|
2024-03-18 20:45:45 -07:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "ios",
|
|
|
|
target_os = "tvos",
|
|
|
|
target_os = "watchos",
|
|
|
|
target_os = "visionos"
|
|
|
|
))]
|
2016-09-29 22:00:44 +00:00
|
|
|
pub fn args() -> Args {
|
2023-11-14 14:52:41 +01:00
|
|
|
use crate::ffi::{c_char, c_void, OsString};
|
2019-02-11 04:23:21 +09:00
|
|
|
use crate::mem;
|
|
|
|
use crate::str;
|
2016-09-29 22:00:44 +00:00
|
|
|
|
2023-11-14 14:52:41 +01:00
|
|
|
type Sel = *const c_void;
|
|
|
|
type NsId = *const c_void;
|
|
|
|
type NSUInteger = usize;
|
2016-09-29 22:00:44 +00:00
|
|
|
|
2016-12-03 19:47:27 +01:00
|
|
|
extern "C" {
|
2023-11-14 14:52:41 +01:00
|
|
|
fn sel_registerName(name: *const c_char) -> Sel;
|
|
|
|
fn objc_getClass(class_name: *const c_char) -> NsId;
|
2016-12-03 19:47:27 +01:00
|
|
|
|
2023-11-14 14:52:41 +01:00
|
|
|
// This must be transmuted to an appropriate function pointer type before being called.
|
|
|
|
fn objc_msgSend();
|
2016-12-03 19:47:27 +01:00
|
|
|
}
|
|
|
|
|
2023-11-14 14:52:41 +01:00
|
|
|
const MSG_SEND_PTR: unsafe extern "C" fn() = objc_msgSend;
|
|
|
|
const MSG_SEND_NO_ARGUMENTS_RETURN_PTR: unsafe extern "C" fn(NsId, Sel) -> *const c_void =
|
|
|
|
unsafe { mem::transmute(MSG_SEND_PTR) };
|
|
|
|
const MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER: unsafe extern "C" fn(
|
|
|
|
NsId,
|
|
|
|
Sel,
|
|
|
|
) -> NSUInteger = unsafe { mem::transmute(MSG_SEND_PTR) };
|
|
|
|
const MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR: unsafe extern "C" fn(
|
|
|
|
NsId,
|
|
|
|
Sel,
|
|
|
|
NSUInteger,
|
|
|
|
)
|
|
|
|
-> *const c_void = unsafe { mem::transmute(MSG_SEND_PTR) };
|
2016-09-29 22:00:44 +00:00
|
|
|
|
|
|
|
let mut res = Vec::new();
|
|
|
|
|
|
|
|
unsafe {
|
2023-11-14 14:52:41 +01:00
|
|
|
let process_info_sel = sel_registerName(c"processInfo".as_ptr());
|
|
|
|
let arguments_sel = sel_registerName(c"arguments".as_ptr());
|
|
|
|
let count_sel = sel_registerName(c"count".as_ptr());
|
|
|
|
let object_at_index_sel = sel_registerName(c"objectAtIndex:".as_ptr());
|
|
|
|
let utf8string_sel = sel_registerName(c"UTF8String".as_ptr());
|
|
|
|
|
|
|
|
let klass = objc_getClass(c"NSProcessInfo".as_ptr());
|
|
|
|
// `+[NSProcessInfo processInfo]` returns an object with +0 retain count, so no need to manually `retain/release`.
|
|
|
|
let info = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(klass, process_info_sel);
|
|
|
|
|
|
|
|
// `-[NSProcessInfo arguments]` returns an object with +0 retain count, so no need to manually `retain/release`.
|
|
|
|
let args = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(info, arguments_sel);
|
|
|
|
|
|
|
|
let cnt = MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER(args, count_sel);
|
2016-09-29 22:00:44 +00:00
|
|
|
for i in 0..cnt {
|
2023-11-14 14:52:41 +01:00
|
|
|
// `-[NSArray objectAtIndex:]` returns an object whose lifetime is tied to the array, so no need to manually `retain/release`.
|
|
|
|
let ns_string =
|
|
|
|
MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR(args, object_at_index_sel, i);
|
|
|
|
// The lifetime of this pointer is tied to the NSString, as well as the current autorelease pool, which is why we heap-allocate the string below.
|
|
|
|
let utf_c_str: *const c_char =
|
|
|
|
MSG_SEND_NO_ARGUMENTS_RETURN_PTR(ns_string, utf8string_sel).cast();
|
2016-09-29 22:00:44 +00:00
|
|
|
let bytes = CStr::from_ptr(utf_c_str).to_bytes();
|
|
|
|
res.push(OsString::from(str::from_utf8(bytes).unwrap()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-14 04:04:27 +02:00
|
|
|
Args { iter: res.into_iter() }
|
2016-09-29 22:00:44 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-29 20:18:22 +03:00
|
|
|
|
2023-05-07 11:20:01 +03:00
|
|
|
#[cfg(any(target_os = "espidf", target_os = "vita"))]
|
2021-07-29 20:18:22 +03:00
|
|
|
mod imp {
|
|
|
|
use super::Args;
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
|
|
|
|
|
|
|
pub fn args() -> Args {
|
|
|
|
Args { iter: Vec::new().into_iter() }
|
|
|
|
}
|
|
|
|
}
|