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 | |
8 | use crate::ffi::OsString; |
9 | use crate::fmt; |
10 | use crate::vec; |
11 | |
12 | /// One-time global initialization. |
13 | pub unsafe fn init(argc: isize, argv: *const *const u8) { |
14 | imp::init(argc, argv) |
15 | } |
16 | |
17 | /// Returns the command line arguments |
18 | pub fn args() -> Args { |
19 | imp::args() |
20 | } |
21 | |
22 | pub struct Args { |
23 | iter: vec::IntoIter<OsString>, |
24 | } |
25 | |
26 | impl !Send for Args {} |
27 | impl !Sync for Args {} |
28 | |
29 | impl fmt::Debug for Args { |
30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
31 | self.iter.as_slice().fmt(f) |
32 | } |
33 | } |
34 | |
35 | impl 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 | |
45 | impl ExactSizeIterator for Args { |
46 | fn len(&self) -> usize { |
47 | self.iter.len() |
48 | } |
49 | } |
50 | |
51 | impl 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 | ))] |
77 | mod 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 | ))] |
180 | mod 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" ))] |
291 | mod 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 | |