]>
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 XL |
8 | use crate::ffi::OsString; |
9 | use crate::marker::PhantomData; | |
10 | use crate::vec; | |
c30ab7b3 SL |
11 | |
12 | /// One-time global initialization. | |
13 | pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) } | |
14 | ||
15 | /// One-time global cleanup. | |
16 | pub unsafe fn cleanup() { imp::cleanup() } | |
17 | ||
18 | /// Returns the command line arguments | |
19 | pub fn args() -> Args { | |
20 | imp::args() | |
21 | } | |
22 | ||
23 | pub struct Args { | |
24 | iter: vec::IntoIter<OsString>, | |
25 | _dont_send_or_sync_me: PhantomData<*mut ()>, | |
26 | } | |
27 | ||
041b39d2 XL |
28 | impl Args { |
29 | pub fn inner_debug(&self) -> &[OsString] { | |
30 | self.iter.as_slice() | |
31 | } | |
32 | } | |
33 | ||
c30ab7b3 SL |
34 | impl Iterator for Args { |
35 | type Item = OsString; | |
36 | fn next(&mut self) -> Option<OsString> { self.iter.next() } | |
37 | fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } | |
38 | } | |
39 | ||
40 | impl ExactSizeIterator for Args { | |
41 | fn len(&self) -> usize { self.iter.len() } | |
42 | } | |
43 | ||
44 | impl DoubleEndedIterator for Args { | |
45 | fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() } | |
46 | } | |
47 | ||
48 | #[cfg(any(target_os = "linux", | |
49 | target_os = "android", | |
50 | target_os = "freebsd", | |
51 | target_os = "dragonfly", | |
c30ab7b3 SL |
52 | target_os = "netbsd", |
53 | target_os = "openbsd", | |
54 | target_os = "solaris", | |
55 | target_os = "emscripten", | |
56 | target_os = "haiku", | |
ea8adc8c | 57 | target_os = "l4re", |
b7449926 | 58 | target_os = "fuchsia", |
416331ca | 59 | target_os = "redox"))] |
c30ab7b3 | 60 | mod imp { |
532ac7d7 XL |
61 | use crate::os::unix::prelude::*; |
62 | use crate::ptr; | |
63 | use crate::ffi::{CStr, OsString}; | |
64 | use crate::marker::PhantomData; | |
c30ab7b3 SL |
65 | use super::Args; |
66 | ||
532ac7d7 | 67 | use crate::sys_common::mutex::Mutex; |
c30ab7b3 | 68 | |
2c00a5a8 XL |
69 | static mut ARGC: isize = 0; |
70 | static mut ARGV: *const *const u8 = ptr::null(); | |
b7449926 XL |
71 | // We never call `ENV_LOCK.init()`, so it is UB to attempt to |
72 | // acquire this mutex reentrantly! | |
c30ab7b3 SL |
73 | static LOCK: Mutex = Mutex::new(); |
74 | ||
60c5eb7d | 75 | unsafe fn really_init(argc: isize, argv: *const *const u8) { |
94b46f34 | 76 | let _guard = LOCK.lock(); |
2c00a5a8 XL |
77 | ARGC = argc; |
78 | ARGV = argv; | |
c30ab7b3 SL |
79 | } |
80 | ||
60c5eb7d XL |
81 | #[inline(always)] |
82 | pub unsafe fn init(_argc: isize, _argv: *const *const u8) { | |
83 | // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize | |
84 | // `ARGC` and `ARGV`. But in Miri that does not actually happen so we | |
85 | // still initialize here. | |
86 | #[cfg(any( | |
87 | miri, | |
88 | not(all(target_os = "linux", target_env = "gnu")) | |
89 | ))] | |
90 | really_init(_argc, _argv); | |
91 | } | |
92 | ||
93 | /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. | |
94 | /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows. | |
95 | #[cfg(all(target_os = "linux", target_env = "gnu"))] | |
96 | #[used] | |
97 | #[link_section = ".init_array.00099"] | |
98 | static ARGV_INIT_ARRAY: extern "C" fn( | |
99 | crate::os::raw::c_int, | |
100 | *const *const u8, | |
101 | *const *const u8, | |
102 | ) = { | |
103 | extern "C" fn init_wrapper( | |
104 | argc: crate::os::raw::c_int, | |
105 | argv: *const *const u8, | |
106 | _envp: *const *const u8, | |
107 | ) { | |
108 | unsafe { | |
109 | really_init(argc as isize, argv); | |
110 | } | |
111 | } | |
112 | init_wrapper | |
113 | }; | |
114 | ||
c30ab7b3 | 115 | pub unsafe fn cleanup() { |
94b46f34 | 116 | let _guard = LOCK.lock(); |
2c00a5a8 XL |
117 | ARGC = 0; |
118 | ARGV = ptr::null(); | |
c30ab7b3 SL |
119 | } |
120 | ||
121 | pub fn args() -> Args { | |
2c00a5a8 XL |
122 | Args { |
123 | iter: clone().into_iter(), | |
124 | _dont_send_or_sync_me: PhantomData | |
125 | } | |
c30ab7b3 SL |
126 | } |
127 | ||
2c00a5a8 | 128 | fn clone() -> Vec<OsString> { |
c30ab7b3 | 129 | unsafe { |
94b46f34 XL |
130 | let _guard = LOCK.lock(); |
131 | (0..ARGC).map(|i| { | |
2c00a5a8 XL |
132 | let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char); |
133 | OsStringExt::from_vec(cstr.to_bytes().to_vec()) | |
94b46f34 | 134 | }).collect() |
c30ab7b3 SL |
135 | } |
136 | } | |
c30ab7b3 SL |
137 | } |
138 | ||
139 | #[cfg(any(target_os = "macos", | |
140 | target_os = "ios"))] | |
141 | mod imp { | |
532ac7d7 XL |
142 | use crate::ffi::CStr; |
143 | use crate::marker::PhantomData; | |
c30ab7b3 SL |
144 | use super::Args; |
145 | ||
146 | pub unsafe fn init(_argc: isize, _argv: *const *const u8) { | |
147 | } | |
148 | ||
149 | pub fn cleanup() { | |
150 | } | |
151 | ||
152 | #[cfg(target_os = "macos")] | |
153 | pub fn args() -> Args { | |
532ac7d7 | 154 | use crate::os::unix::prelude::*; |
c30ab7b3 SL |
155 | extern { |
156 | // These functions are in crt_externs.h. | |
157 | fn _NSGetArgc() -> *mut libc::c_int; | |
158 | fn _NSGetArgv() -> *mut *mut *mut libc::c_char; | |
159 | } | |
160 | ||
161 | let vec = unsafe { | |
162 | let (argc, argv) = (*_NSGetArgc() as isize, | |
163 | *_NSGetArgv() as *const *const libc::c_char); | |
164 | (0.. argc as isize).map(|i| { | |
165 | let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec(); | |
166 | OsStringExt::from_vec(bytes) | |
167 | }).collect::<Vec<_>>() | |
168 | }; | |
169 | Args { | |
170 | iter: vec.into_iter(), | |
171 | _dont_send_or_sync_me: PhantomData, | |
172 | } | |
173 | } | |
174 | ||
175 | // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs | |
176 | // and use underscores in their names - they're most probably | |
177 | // are considered private and therefore should be avoided | |
178 | // Here is another way to get arguments using Objective C | |
179 | // runtime | |
180 | // | |
181 | // In general it looks like: | |
182 | // res = Vec::new() | |
183 | // let args = [[NSProcessInfo processInfo] arguments] | |
184 | // for i in (0..[args count]) | |
185 | // res.push([args objectAtIndex:i]) | |
186 | // res | |
187 | #[cfg(target_os = "ios")] | |
188 | pub fn args() -> Args { | |
532ac7d7 XL |
189 | use crate::ffi::OsString; |
190 | use crate::mem; | |
191 | use crate::str; | |
c30ab7b3 SL |
192 | |
193 | extern { | |
194 | fn sel_registerName(name: *const libc::c_uchar) -> Sel; | |
c30ab7b3 SL |
195 | fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; |
196 | } | |
197 | ||
476ff2be SL |
198 | #[cfg(target_arch="aarch64")] |
199 | extern { | |
200 | fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; | |
201 | #[link_name="objc_msgSend"] | |
202 | fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; | |
203 | } | |
204 | ||
205 | #[cfg(not(target_arch="aarch64"))] | |
206 | extern { | |
207 | fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; | |
208 | #[link_name="objc_msgSend"] | |
209 | fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; | |
210 | } | |
211 | ||
c30ab7b3 SL |
212 | type Sel = *const libc::c_void; |
213 | type NsId = *const libc::c_void; | |
214 | ||
215 | let mut res = Vec::new(); | |
216 | ||
217 | unsafe { | |
218 | let process_info_sel = sel_registerName("processInfo\0".as_ptr()); | |
219 | let arguments_sel = sel_registerName("arguments\0".as_ptr()); | |
220 | let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); | |
221 | let count_sel = sel_registerName("count\0".as_ptr()); | |
222 | let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); | |
223 | ||
224 | let klass = objc_getClass("NSProcessInfo\0".as_ptr()); | |
225 | let info = objc_msgSend(klass, process_info_sel); | |
226 | let args = objc_msgSend(info, arguments_sel); | |
227 | ||
228 | let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); | |
229 | for i in 0..cnt { | |
476ff2be | 230 | let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong); |
c30ab7b3 SL |
231 | let utf_c_str: *const libc::c_char = |
232 | mem::transmute(objc_msgSend(tmp, utf8_sel)); | |
233 | let bytes = CStr::from_ptr(utf_c_str).to_bytes(); | |
234 | res.push(OsString::from(str::from_utf8(bytes).unwrap())) | |
235 | } | |
236 | } | |
237 | ||
238 | Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData } | |
239 | } | |
240 | } |