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