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