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