From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- library/std/src/sys/unix/args.rs | 261 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 library/std/src/sys/unix/args.rs (limited to 'library/std/src/sys/unix/args.rs') diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs new file mode 100644 index 000000000..a342f0f5e --- /dev/null +++ b/library/std/src/sys/unix/args.rs @@ -0,0 +1,261 @@ +//! Global initialization and retrieval of command line arguments. +//! +//! 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 + +use crate::ffi::OsString; +use crate::fmt; +use crate::vec; + +/// 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, +} + +impl !Send for Args {} +impl !Sync for Args {} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + 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 { + 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", + target_os = "illumos", + target_os = "emscripten", + target_os = "haiku", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox", + target_os = "vxworks", + target_os = "horizon" +))] +mod imp { + use super::Args; + use crate::ffi::{CStr, OsString}; + use crate::os::unix::prelude::*; + use crate::ptr; + use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; + + // 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. + static ARGC: AtomicIsize = AtomicIsize::new(0); + static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); + + unsafe fn really_init(argc: isize, argv: *const *const u8) { + // These don't need to be ordered with each other or other stores, + // because they only hold the unmodified system-provide argv/argc. + ARGC.store(argc, Ordering::Relaxed); + ARGV.store(argv as *mut _, Ordering::Relaxed); + } + + #[inline(always)] + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize + // `ARGC` and `ARGV`. But in Miri that does not actually happen so we + // still initialize here. + #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))] + really_init(_argc, _argv); + } + + /// 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. + #[cfg(all(target_os = "linux", target_env = "gnu"))] + #[used] + #[link_section = ".init_array.00099"] + 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 { + really_init(argc as isize, argv); + } + } + init_wrapper + }; + + pub fn args() -> Args { + Args { iter: clone().into_iter() } + } + + fn clone() -> Vec { + unsafe { + // 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. + let argv = ARGV.load(Ordering::Relaxed); + let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; + (0..argc) + .map(|i| { + let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char); + OsStringExt::from_vec(cstr.to_bytes().to_vec()) + }) + .collect() + } + } +} + +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +mod imp { + use super::Args; + use crate::ffi::CStr; + + pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} + + #[cfg(target_os = "macos")] + pub fn args() -> Args { + use crate::os::unix::prelude::*; + 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::>() + }; + Args { iter: vec.into_iter() } + } + + // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs + // and use underscores in their names - they're most probably + // are considered private and therefore should be avoided + // Here is another way to get arguments using Objective C + // runtime + // + // 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 + #[cfg(any(target_os = "ios", target_os = "watchos"))] + pub fn args() -> Args { + use crate::ffi::OsString; + use crate::mem; + use crate::str; + + extern "C" { + fn sel_registerName(name: *const libc::c_uchar) -> Sel; + fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; + } + + #[cfg(target_arch = "aarch64")] + extern "C" { + fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; + #[allow(clashing_extern_declarations)] + #[link_name = "objc_msgSend"] + fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; + } + + #[cfg(not(target_arch = "aarch64"))] + extern "C" { + fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; + #[allow(clashing_extern_declarations)] + #[link_name = "objc_msgSend"] + fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; + } + + type Sel = *const libc::c_void; + type NsId = *const libc::c_void; + + let mut res = Vec::new(); + + unsafe { + let process_info_sel = sel_registerName("processInfo\0".as_ptr()); + let arguments_sel = sel_registerName("arguments\0".as_ptr()); + let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); + let count_sel = sel_registerName("count\0".as_ptr()); + let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); + + let klass = objc_getClass("NSProcessInfo\0".as_ptr()); + let info = objc_msgSend(klass, process_info_sel); + let args = objc_msgSend(info, arguments_sel); + + let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); + for i in 0..cnt { + let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong); + let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel)); + let bytes = CStr::from_ptr(utf_c_str).to_bytes(); + res.push(OsString::from(str::from_utf8(bytes).unwrap())) + } + } + + Args { iter: res.into_iter() } + } +} + +#[cfg(target_os = "espidf")] +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() } + } +} -- cgit v1.2.3