1 //! Global initialization and retrieval of command line arguments.
3 //! On some platforms these are stored during runtime startup,
4 //! and on some they are retrieved from the system on demand.
6 #![allow(dead_code)] // runtime init functions not used during testing
8 use crate::ffi::OsString;
12 /// One-time global initialization.
13 pub unsafe fn init(argc: isize, argv: *const *const u8) {
17 /// One-time global cleanup.
18 pub unsafe fn cleanup() {
22 /// Returns the command line arguments
23 pub fn args() -> Args {
28 iter: vec::IntoIter<OsString>,
31 impl !Send for Args {}
32 impl !Sync for Args {}
34 impl fmt::Debug for Args {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 self.iter.as_slice().fmt(f)
40 impl Iterator for Args {
42 fn next(&mut self) -> Option<OsString> {
45 fn size_hint(&self) -> (usize, Option<usize>) {
50 impl ExactSizeIterator for Args {
51 fn len(&self) -> usize {
56 impl DoubleEndedIterator for Args {
57 fn next_back(&mut self) -> Option<OsString> {
64 target_os = "android",
65 target_os = "freebsd",
66 target_os = "dragonfly",
68 target_os = "openbsd",
69 target_os = "solaris",
70 target_os = "illumos",
71 target_os = "emscripten",
74 target_os = "fuchsia",
80 use crate::ffi::{CStr, OsString};
81 use crate::os::unix::prelude::*;
83 use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
85 use crate::sys_common::mutex::StaticMutex;
87 static ARGC: AtomicIsize = AtomicIsize::new(0);
88 static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
89 // We never call `ENV_LOCK.init()`, so it is UB to attempt to
90 // acquire this mutex reentrantly!
91 static LOCK: StaticMutex = StaticMutex::new();
93 unsafe fn really_init(argc: isize, argv: *const *const u8) {
94 let _guard = LOCK.lock();
95 ARGC.store(argc, Ordering::Relaxed);
96 ARGV.store(argv as *mut _, Ordering::Relaxed);
100 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
101 // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize
102 // `ARGC` and `ARGV`. But in Miri that does not actually happen so we
103 // still initialize here.
104 #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))]
105 really_init(_argc, _argv);
108 /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
109 /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
110 #[cfg(all(target_os = "linux", target_env = "gnu"))]
112 #[link_section = ".init_array.00099"]
113 static ARGV_INIT_ARRAY: extern "C" fn(
114 crate::os::raw::c_int,
118 extern "C" fn init_wrapper(
119 argc: crate::os::raw::c_int,
120 argv: *const *const u8,
121 _envp: *const *const u8,
124 really_init(argc as isize, argv);
130 pub unsafe fn cleanup() {
131 let _guard = LOCK.lock();
132 ARGC.store(0, Ordering::Relaxed);
133 ARGV.store(ptr::null_mut(), Ordering::Relaxed);
136 pub fn args() -> Args {
137 Args { iter: clone().into_iter() }
140 fn clone() -> Vec<OsString> {
142 let _guard = LOCK.lock();
143 let argc = ARGC.load(Ordering::Relaxed);
144 let argv = ARGV.load(Ordering::Relaxed);
147 let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char);
148 OsStringExt::from_vec(cstr.to_bytes().to_vec())
155 #[cfg(any(target_os = "macos", target_os = "ios"))]
158 use crate::ffi::CStr;
160 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
164 #[cfg(target_os = "macos")]
165 pub fn args() -> Args {
166 use crate::os::unix::prelude::*;
168 // These functions are in crt_externs.h.
169 fn _NSGetArgc() -> *mut libc::c_int;
170 fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
175 (*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char);
178 let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
179 OsStringExt::from_vec(bytes)
183 Args { iter: vec.into_iter() }
186 // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
187 // and use underscores in their names - they're most probably
188 // are considered private and therefore should be avoided
189 // Here is another way to get arguments using Objective C
192 // In general it looks like:
194 // let args = [[NSProcessInfo processInfo] arguments]
195 // for i in (0..[args count])
196 // res.push([args objectAtIndex:i])
198 #[cfg(target_os = "ios")]
199 pub fn args() -> Args {
200 use crate::ffi::OsString;
205 fn sel_registerName(name: *const libc::c_uchar) -> Sel;
206 fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
209 #[cfg(target_arch = "aarch64")]
211 fn objc_msgSend(obj: NsId, sel: Sel) -> NsId;
212 #[allow(clashing_extern_declarations)]
213 #[link_name = "objc_msgSend"]
214 fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId;
217 #[cfg(not(target_arch = "aarch64"))]
219 fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
220 #[allow(clashing_extern_declarations)]
221 #[link_name = "objc_msgSend"]
222 fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId;
225 type Sel = *const libc::c_void;
226 type NsId = *const libc::c_void;
228 let mut res = Vec::new();
231 let process_info_sel = sel_registerName("processInfo\0".as_ptr());
232 let arguments_sel = sel_registerName("arguments\0".as_ptr());
233 let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
234 let count_sel = sel_registerName("count\0".as_ptr());
235 let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
237 let klass = objc_getClass("NSProcessInfo\0".as_ptr());
238 let info = objc_msgSend(klass, process_info_sel);
239 let args = objc_msgSend(info, arguments_sel);
241 let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
243 let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong);
244 let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel));
245 let bytes = CStr::from_ptr(utf_c_str).to_bytes();
246 res.push(OsString::from(str::from_utf8(bytes).unwrap()))
250 Args { iter: res.into_iter() }