]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/args.rs
Rollup merge of #84466 - jyn514:prim-str, r=GuillaumeGomez
[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::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 /// 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 }
30
31 impl !Send for Args {}
32 impl !Sync for Args {}
33
34 impl fmt::Debug for Args {
35     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36         self.iter.as_slice().fmt(f)
37     }
38 }
39
40 impl Iterator for Args {
41     type Item = OsString;
42     fn next(&mut self) -> Option<OsString> {
43         self.iter.next()
44     }
45     fn size_hint(&self) -> (usize, Option<usize>) {
46         self.iter.size_hint()
47     }
48 }
49
50 impl ExactSizeIterator for Args {
51     fn len(&self) -> usize {
52         self.iter.len()
53     }
54 }
55
56 impl DoubleEndedIterator for Args {
57     fn next_back(&mut self) -> Option<OsString> {
58         self.iter.next_back()
59     }
60 }
61
62 #[cfg(any(
63     target_os = "linux",
64     target_os = "android",
65     target_os = "freebsd",
66     target_os = "dragonfly",
67     target_os = "netbsd",
68     target_os = "openbsd",
69     target_os = "solaris",
70     target_os = "illumos",
71     target_os = "emscripten",
72     target_os = "haiku",
73     target_os = "l4re",
74     target_os = "fuchsia",
75     target_os = "redox",
76     target_os = "vxworks"
77 ))]
78 mod imp {
79     use super::Args;
80     use crate::ffi::{CStr, OsString};
81     use crate::os::unix::prelude::*;
82     use crate::ptr;
83     use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
84
85     use crate::sys_common::mutex::StaticMutex;
86
87     static ARGC: AtomicIsize = AtomicIsize::new(0);
88     static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
89     // We never call `ENV_LOCK.init()`, so it is UB to attempt to
90     // acquire this mutex reentrantly!
91     static LOCK: StaticMutex = StaticMutex::new();
92
93     unsafe fn really_init(argc: isize, argv: *const *const u8) {
94         let _guard = LOCK.lock();
95         ARGC.store(argc, Ordering::Relaxed);
96         ARGV.store(argv as *mut _, Ordering::Relaxed);
97     }
98
99     #[inline(always)]
100     pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
101         // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize
102         // `ARGC` and `ARGV`. But in Miri that does not actually happen so we
103         // still initialize here.
104         #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))]
105         really_init(_argc, _argv);
106     }
107
108     /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
109     /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
110     #[cfg(all(target_os = "linux", target_env = "gnu"))]
111     #[used]
112     #[link_section = ".init_array.00099"]
113     static ARGV_INIT_ARRAY: extern "C" fn(
114         crate::os::raw::c_int,
115         *const *const u8,
116         *const *const u8,
117     ) = {
118         extern "C" fn init_wrapper(
119             argc: crate::os::raw::c_int,
120             argv: *const *const u8,
121             _envp: *const *const u8,
122         ) {
123             unsafe {
124                 really_init(argc as isize, argv);
125             }
126         }
127         init_wrapper
128     };
129
130     pub unsafe fn cleanup() {
131         let _guard = LOCK.lock();
132         ARGC.store(0, Ordering::Relaxed);
133         ARGV.store(ptr::null_mut(), Ordering::Relaxed);
134     }
135
136     pub fn args() -> Args {
137         Args { iter: clone().into_iter() }
138     }
139
140     fn clone() -> Vec<OsString> {
141         unsafe {
142             let _guard = LOCK.lock();
143             let argc = ARGC.load(Ordering::Relaxed);
144             let argv = ARGV.load(Ordering::Relaxed);
145             (0..argc)
146                 .map(|i| {
147                     let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char);
148                     OsStringExt::from_vec(cstr.to_bytes().to_vec())
149                 })
150                 .collect()
151         }
152     }
153 }
154
155 #[cfg(any(target_os = "macos", target_os = "ios"))]
156 mod imp {
157     use super::Args;
158     use crate::ffi::CStr;
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() }
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() }
251     }
252 }