]>
Commit | Line | Data |
---|---|---|
3b2f2976 | 1 | //! Global initialization and retrieval of command line arguments. |
c30ab7b3 SL |
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 | ||
532ac7d7 | 8 | use crate::ffi::OsString; |
cdc7bbd5 | 9 | use crate::fmt; |
532ac7d7 | 10 | use crate::vec; |
c30ab7b3 SL |
11 | |
12 | /// One-time global initialization. | |
dfeec247 XL |
13 | pub unsafe fn init(argc: isize, argv: *const *const u8) { |
14 | imp::init(argc, argv) | |
15 | } | |
c30ab7b3 | 16 | |
c30ab7b3 SL |
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>, | |
c30ab7b3 SL |
24 | } |
25 | ||
cdc7bbd5 XL |
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) | |
041b39d2 XL |
32 | } |
33 | } | |
34 | ||
c30ab7b3 SL |
35 | impl Iterator for Args { |
36 | type Item = OsString; | |
dfeec247 XL |
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 | } | |
c30ab7b3 SL |
43 | } |
44 | ||
45 | impl ExactSizeIterator for Args { | |
dfeec247 XL |
46 | fn len(&self) -> usize { |
47 | self.iter.len() | |
48 | } | |
c30ab7b3 SL |
49 | } |
50 | ||
51 | impl DoubleEndedIterator for Args { | |
dfeec247 XL |
52 | fn next_back(&mut self) -> Option<OsString> { |
53 | self.iter.next_back() | |
54 | } | |
c30ab7b3 SL |
55 | } |
56 | ||
dfeec247 XL |
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", | |
ba9703b0 | 65 | target_os = "illumos", |
dfeec247 XL |
66 | target_os = "emscripten", |
67 | target_os = "haiku", | |
68 | target_os = "l4re", | |
69 | target_os = "fuchsia", | |
29967ef6 | 70 | target_os = "redox", |
923072b8 FG |
71 | target_os = "vxworks", |
72 | target_os = "horizon" | |
dfeec247 | 73 | ))] |
c30ab7b3 | 74 | mod imp { |
dfeec247 | 75 | use super::Args; |
532ac7d7 | 76 | use crate::ffi::{CStr, OsString}; |
dfeec247 XL |
77 | use crate::os::unix::prelude::*; |
78 | use crate::ptr; | |
f035d41b | 79 | use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; |
c30ab7b3 | 80 | |
136023e0 XL |
81 | // The system-provided argc and argv, which we store in static memory |
82 | // here so that we can defer the work of parsing them until its actually | |
83 | // needed. | |
84 | // | |
85 | // Note that we never mutate argv/argc, the argv array, or the argv | |
86 | // strings, which allows the code in this file to be very simple. | |
f035d41b XL |
87 | static ARGC: AtomicIsize = AtomicIsize::new(0); |
88 | static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); | |
c30ab7b3 | 89 | |
60c5eb7d | 90 | unsafe fn really_init(argc: isize, argv: *const *const u8) { |
136023e0 XL |
91 | // These don't need to be ordered with each other or other stores, |
92 | // because they only hold the unmodified system-provide argv/argc. | |
f035d41b XL |
93 | ARGC.store(argc, Ordering::Relaxed); |
94 | ARGV.store(argv as *mut _, Ordering::Relaxed); | |
c30ab7b3 SL |
95 | } |
96 | ||
60c5eb7d XL |
97 | #[inline(always)] |
98 | pub unsafe fn init(_argc: isize, _argv: *const *const u8) { | |
99 | // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize | |
100 | // `ARGC` and `ARGV`. But in Miri that does not actually happen so we | |
101 | // still initialize here. | |
dfeec247 | 102 | #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))] |
60c5eb7d XL |
103 | really_init(_argc, _argv); |
104 | } | |
105 | ||
106 | /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. | |
107 | /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows. | |
108 | #[cfg(all(target_os = "linux", target_env = "gnu"))] | |
109 | #[used] | |
110 | #[link_section = ".init_array.00099"] | |
111 | static ARGV_INIT_ARRAY: extern "C" fn( | |
112 | crate::os::raw::c_int, | |
113 | *const *const u8, | |
114 | *const *const u8, | |
115 | ) = { | |
116 | extern "C" fn init_wrapper( | |
117 | argc: crate::os::raw::c_int, | |
118 | argv: *const *const u8, | |
119 | _envp: *const *const u8, | |
120 | ) { | |
121 | unsafe { | |
122 | really_init(argc as isize, argv); | |
123 | } | |
124 | } | |
125 | init_wrapper | |
126 | }; | |
127 | ||
c30ab7b3 | 128 | pub fn args() -> Args { |
cdc7bbd5 | 129 | Args { iter: clone().into_iter() } |
c30ab7b3 SL |
130 | } |
131 | ||
2c00a5a8 | 132 | fn clone() -> Vec<OsString> { |
c30ab7b3 | 133 | unsafe { |
136023e0 XL |
134 | // Load ARGC and ARGV, which hold the unmodified system-provided |
135 | // argc/argv, so we can read the pointed-to memory without atomics | |
136 | // or synchronization. | |
137 | // | |
138 | // If either ARGC or ARGV is still zero or null, then either there | |
139 | // really are no arguments, or someone is asking for `args()` | |
140 | // before initialization has completed, and we return an empty | |
141 | // list. | |
f035d41b | 142 | let argv = ARGV.load(Ordering::Relaxed); |
136023e0 | 143 | let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; |
f035d41b | 144 | (0..argc) |
dfeec247 | 145 | .map(|i| { |
f035d41b | 146 | let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char); |
dfeec247 XL |
147 | OsStringExt::from_vec(cstr.to_bytes().to_vec()) |
148 | }) | |
149 | .collect() | |
c30ab7b3 SL |
150 | } |
151 | } | |
c30ab7b3 SL |
152 | } |
153 | ||
064997fb | 154 | #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] |
c30ab7b3 | 155 | mod imp { |
dfeec247 | 156 | use super::Args; |
532ac7d7 | 157 | use crate::ffi::CStr; |
c30ab7b3 | 158 | |
dfeec247 | 159 | pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} |
c30ab7b3 | 160 | |
c30ab7b3 SL |
161 | #[cfg(target_os = "macos")] |
162 | pub fn args() -> Args { | |
532ac7d7 | 163 | use crate::os::unix::prelude::*; |
dfeec247 | 164 | extern "C" { |
c30ab7b3 SL |
165 | // These functions are in crt_externs.h. |
166 | fn _NSGetArgc() -> *mut libc::c_int; | |
167 | fn _NSGetArgv() -> *mut *mut *mut libc::c_char; | |
168 | } | |
169 | ||
170 | let vec = unsafe { | |
dfeec247 XL |
171 | let (argc, argv) = |
172 | (*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char); | |
173 | (0..argc as isize) | |
174 | .map(|i| { | |
175 | let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec(); | |
176 | OsStringExt::from_vec(bytes) | |
177 | }) | |
178 | .collect::<Vec<_>>() | |
c30ab7b3 | 179 | }; |
cdc7bbd5 | 180 | Args { iter: vec.into_iter() } |
c30ab7b3 SL |
181 | } |
182 | ||
183 | // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs | |
184 | // and use underscores in their names - they're most probably | |
185 | // are considered private and therefore should be avoided | |
186 | // Here is another way to get arguments using Objective C | |
187 | // runtime | |
188 | // | |
189 | // In general it looks like: | |
190 | // res = Vec::new() | |
191 | // let args = [[NSProcessInfo processInfo] arguments] | |
192 | // for i in (0..[args count]) | |
193 | // res.push([args objectAtIndex:i]) | |
194 | // res | |
064997fb | 195 | #[cfg(any(target_os = "ios", target_os = "watchos"))] |
c30ab7b3 | 196 | pub fn args() -> Args { |
532ac7d7 XL |
197 | use crate::ffi::OsString; |
198 | use crate::mem; | |
199 | use crate::str; | |
c30ab7b3 | 200 | |
dfeec247 | 201 | extern "C" { |
c30ab7b3 | 202 | fn sel_registerName(name: *const libc::c_uchar) -> Sel; |
c30ab7b3 SL |
203 | fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; |
204 | } | |
205 | ||
dfeec247 XL |
206 | #[cfg(target_arch = "aarch64")] |
207 | extern "C" { | |
476ff2be | 208 | fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; |
3dfed10e | 209 | #[allow(clashing_extern_declarations)] |
dfeec247 | 210 | #[link_name = "objc_msgSend"] |
476ff2be SL |
211 | fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; |
212 | } | |
213 | ||
dfeec247 XL |
214 | #[cfg(not(target_arch = "aarch64"))] |
215 | extern "C" { | |
476ff2be | 216 | fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; |
3dfed10e | 217 | #[allow(clashing_extern_declarations)] |
dfeec247 | 218 | #[link_name = "objc_msgSend"] |
476ff2be SL |
219 | fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; |
220 | } | |
221 | ||
c30ab7b3 SL |
222 | type Sel = *const libc::c_void; |
223 | type NsId = *const libc::c_void; | |
224 | ||
225 | let mut res = Vec::new(); | |
226 | ||
227 | unsafe { | |
228 | let process_info_sel = sel_registerName("processInfo\0".as_ptr()); | |
229 | let arguments_sel = sel_registerName("arguments\0".as_ptr()); | |
230 | let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); | |
231 | let count_sel = sel_registerName("count\0".as_ptr()); | |
232 | let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); | |
233 | ||
234 | let klass = objc_getClass("NSProcessInfo\0".as_ptr()); | |
235 | let info = objc_msgSend(klass, process_info_sel); | |
236 | let args = objc_msgSend(info, arguments_sel); | |
237 | ||
238 | let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); | |
239 | for i in 0..cnt { | |
476ff2be | 240 | let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong); |
dfeec247 | 241 | let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel)); |
c30ab7b3 SL |
242 | let bytes = CStr::from_ptr(utf_c_str).to_bytes(); |
243 | res.push(OsString::from(str::from_utf8(bytes).unwrap())) | |
244 | } | |
245 | } | |
246 | ||
cdc7bbd5 | 247 | Args { iter: res.into_iter() } |
c30ab7b3 SL |
248 | } |
249 | } | |
94222f64 XL |
250 | |
251 | #[cfg(target_os = "espidf")] | |
252 | mod imp { | |
253 | use super::Args; | |
254 | ||
255 | #[inline(always)] | |
256 | pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} | |
257 | ||
258 | pub fn args() -> Args { | |
259 | Args { iter: Vec::new().into_iter() } | |
260 | } | |
261 | } |