1//! Global initialization and retrieval of command line arguments.
2//!
3//! On some platforms these are stored during runtime startup,
4//! and on some they are retrieved from the system on demand.
5
6#![allow(dead_code)] // runtime init functions not used during testing
7
8use crate::ffi::OsString;
9use crate::fmt;
10use crate::vec;
11
12/// One-time global initialization.
13pub unsafe fn init(argc: isize, argv: *const *const u8) {
14 imp::init(argc, argv)
15}
16
17/// Returns the command line arguments
18pub fn args() -> Args {
19 imp::args()
20}
21
22pub struct Args {
23 iter: vec::IntoIter<OsString>,
24}
25
26impl !Send for Args {}
27impl !Sync for Args {}
28
29impl fmt::Debug for Args {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 self.iter.as_slice().fmt(f)
32 }
33}
34
35impl Iterator for Args {
36 type Item = OsString;
37 fn next(&mut self) -> Option<OsString> {
38 self.iter.next()
39 }
40 fn size_hint(&self) -> (usize, Option<usize>) {
41 self.iter.size_hint()
42 }
43}
44
45impl ExactSizeIterator for Args {
46 fn len(&self) -> usize {
47 self.iter.len()
48 }
49}
50
51impl DoubleEndedIterator for Args {
52 fn next_back(&mut self) -> Option<OsString> {
53 self.iter.next_back()
54 }
55}
56
57#[cfg(any(
58 target_os = "linux",
59 target_os = "android",
60 target_os = "freebsd",
61 target_os = "dragonfly",
62 target_os = "netbsd",
63 target_os = "openbsd",
64 target_os = "solaris",
65 target_os = "illumos",
66 target_os = "emscripten",
67 target_os = "haiku",
68 target_os = "l4re",
69 target_os = "fuchsia",
70 target_os = "redox",
71 target_os = "vxworks",
72 target_os = "horizon",
73 target_os = "aix",
74 target_os = "nto",
75 target_os = "hurd",
76))]
77mod imp {
78 use super::Args;
79 use crate::ffi::{CStr, OsString};
80 use crate::os::unix::prelude::*;
81 use crate::ptr;
82 use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
83
84 // The system-provided argc and argv, which we store in static memory
85 // here so that we can defer the work of parsing them until its actually
86 // needed.
87 //
88 // Note that we never mutate argv/argc, the argv array, or the argv
89 // strings, which allows the code in this file to be very simple.
90 static ARGC: AtomicIsize = AtomicIsize::new(0);
91 static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
92
93 unsafe fn really_init(argc: isize, argv: *const *const u8) {
94 // These don't need to be ordered with each other or other stores,
95 // because they only hold the unmodified system-provide argv/argc.
96 ARGC.store(argc, Ordering::Relaxed);
97 ARGV.store(argv as *mut _, Ordering::Relaxed);
98 }
99
100 #[inline(always)]
101 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
102 // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize
103 // `ARGC` and `ARGV`. But in Miri that does not actually happen so we
104 // still initialize here.
105 #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))]
106 really_init(_argc, _argv);
107 }
108
109 /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
110 /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
111 #[cfg(all(target_os = "linux", target_env = "gnu"))]
112 #[used]
113 #[link_section = ".init_array.00099"]
114 static ARGV_INIT_ARRAY: extern "C" fn(
115 crate::os::raw::c_int,
116 *const *const u8,
117 *const *const u8,
118 ) = {
119 extern "C" fn init_wrapper(
120 argc: crate::os::raw::c_int,
121 argv: *const *const u8,
122 _envp: *const *const u8,
123 ) {
124 unsafe {
125 really_init(argc as isize, argv);
126 }
127 }
128 init_wrapper
129 };
130
131 pub fn args() -> Args {
132 Args { iter: clone().into_iter() }
133 }
134
135 fn clone() -> Vec<OsString> {
136 unsafe {
137 // Load ARGC and ARGV, which hold the unmodified system-provided
138 // argc/argv, so we can read the pointed-to memory without atomics
139 // or synchronization.
140 //
141 // If either ARGC or ARGV is still zero or null, then either there
142 // really are no arguments, or someone is asking for `args()`
143 // before initialization has completed, and we return an empty
144 // list.
145 let argv = ARGV.load(Ordering::Relaxed);
146 let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) };
147 let mut args = Vec::with_capacity(argc as usize);
148 for i in 0..argc {
149 let ptr = *argv.offset(i) as *const libc::c_char;
150
151 // Some C commandline parsers (e.g. GLib and Qt) are replacing already
152 // handled arguments in `argv` with `NULL` and move them to the end. That
153 // means that `argc` might be bigger than the actual number of non-`NULL`
154 // pointers in `argv` at this point.
155 //
156 // To handle this we simply stop iterating at the first `NULL` argument.
157 //
158 // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments
159 // after the first `NULL` can safely be ignored.
160 if ptr.is_null() {
161 break;
162 }
163
164 let cstr = CStr::from_ptr(ptr);
165 args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
166 }
167
168 args
169 }
170 }
171}
172
173#[cfg(any(
174 target_os = "macos",
175 target_os = "ios",
176 target_os = "watchos",
177 target_os = "visionos",
178 target_os = "tvos"
179))]
180mod imp {
181 use super::Args;
182 use crate::ffi::CStr;
183
184 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
185
186 #[cfg(target_os = "macos")]
187 pub fn args() -> Args {
188 use crate::os::unix::prelude::*;
189 extern "C" {
190 // These functions are in crt_externs.h.
191 fn _NSGetArgc() -> *mut libc::c_int;
192 fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
193 }
194
195 let vec = unsafe {
196 let (argc, argv) =
197 (*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char);
198 (0..argc as isize)
199 .map(|i| {
200 let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
201 OsStringExt::from_vec(bytes)
202 })
203 .collect::<Vec<_>>()
204 };
205 Args { iter: vec.into_iter() }
206 }
207
208 // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
209 // and use underscores in their names - they're most probably
210 // are considered private and therefore should be avoided.
211 // Here is another way to get arguments using the Objective-C
212 // runtime.
213 //
214 // In general it looks like:
215 // res = Vec::new()
216 // let args = [[NSProcessInfo processInfo] arguments]
217 // for i in (0..[args count])
218 // res.push([args objectAtIndex:i])
219 // res
220 #[cfg(any(
221 target_os = "ios",
222 target_os = "tvos",
223 target_os = "watchos",
224 target_os = "visionos"
225 ))]
226 pub fn args() -> Args {
227 use crate::ffi::{c_char, c_void, OsString};
228 use crate::mem;
229 use crate::str;
230
231 type Sel = *const c_void;
232 type NsId = *const c_void;
233 type NSUInteger = usize;
234
235 extern "C" {
236 fn sel_registerName(name: *const c_char) -> Sel;
237 fn objc_getClass(class_name: *const c_char) -> NsId;
238
239 // This must be transmuted to an appropriate function pointer type before being called.
240 fn objc_msgSend();
241 }
242
243 const MSG_SEND_PTR: unsafe extern "C" fn() = objc_msgSend;
244 const MSG_SEND_NO_ARGUMENTS_RETURN_PTR: unsafe extern "C" fn(NsId, Sel) -> *const c_void =
245 unsafe { mem::transmute(MSG_SEND_PTR) };
246 const MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER: unsafe extern "C" fn(
247 NsId,
248 Sel,
249 ) -> NSUInteger = unsafe { mem::transmute(MSG_SEND_PTR) };
250 const MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR: unsafe extern "C" fn(
251 NsId,
252 Sel,
253 NSUInteger,
254 )
255 -> *const c_void = unsafe { mem::transmute(MSG_SEND_PTR) };
256
257 let mut res = Vec::new();
258
259 unsafe {
260 let process_info_sel = sel_registerName(c"processInfo".as_ptr());
261 let arguments_sel = sel_registerName(c"arguments".as_ptr());
262 let count_sel = sel_registerName(c"count".as_ptr());
263 let object_at_index_sel = sel_registerName(c"objectAtIndex:".as_ptr());
264 let utf8string_sel = sel_registerName(c"UTF8String".as_ptr());
265
266 let klass = objc_getClass(c"NSProcessInfo".as_ptr());
267 // `+[NSProcessInfo processInfo]` returns an object with +0 retain count, so no need to manually `retain/release`.
268 let info = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(klass, process_info_sel);
269
270 // `-[NSProcessInfo arguments]` returns an object with +0 retain count, so no need to manually `retain/release`.
271 let args = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(info, arguments_sel);
272
273 let cnt = MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER(args, count_sel);
274 for i in 0..cnt {
275 // `-[NSArray objectAtIndex:]` returns an object whose lifetime is tied to the array, so no need to manually `retain/release`.
276 let ns_string =
277 MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR(args, object_at_index_sel, i);
278 // 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.
279 let utf_c_str: *const c_char =
280 MSG_SEND_NO_ARGUMENTS_RETURN_PTR(ns_string, utf8string_sel).cast();
281 let bytes = CStr::from_ptr(utf_c_str).to_bytes();
282 res.push(OsString::from(str::from_utf8(bytes).unwrap()))
283 }
284 }
285
286 Args { iter: res.into_iter() }
287 }
288}
289
290#[cfg(any(target_os = "espidf", target_os = "vita"))]
291mod imp {
292 use super::Args;
293
294 #[inline(always)]
295 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
296
297 pub fn args() -> Args {
298 Args { iter: Vec::new().into_iter() }
299 }
300}
301