55
66#![ allow( dead_code) ] // runtime init functions not used during testing
77
8- use crate :: ffi:: OsString ;
8+ use crate :: ffi:: { CStr , OsString } ;
99use crate :: fmt;
10+ use crate :: os:: unix:: ffi:: OsStringExt ;
1011use crate :: vec;
1112
1213/// One-time global initialization.
@@ -16,7 +17,46 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
1617
1718/// Returns the command line arguments
1819pub fn args ( ) -> Args {
19- imp:: args ( )
20+ let ( argc, argv) = imp:: argc_argv ( ) ;
21+
22+ let mut vec = Vec :: with_capacity ( argc as usize ) ;
23+
24+ for i in 0 ..argc {
25+ // SAFETY: `argv` is non-null if `argc` is positive, and it is
26+ // guaranteed to be at least as long as `argc`, so reading from it
27+ // should be safe.
28+ let ptr = unsafe { argv. offset ( i) . read ( ) } ;
29+
30+ // Some C commandline parsers (e.g. GLib and Qt) are replacing already
31+ // handled arguments in `argv` with `NULL` and move them to the end.
32+ //
33+ // Since they can't directly ensure updates to `argc` as well, this
34+ // means that `argc` might be bigger than the actual number of
35+ // non-`NULL` pointers in `argv` at this point.
36+ //
37+ // To handle this we simply stop iterating at the first `NULL`
38+ // argument. `argv` is also guaranteed to be `NULL`-terminated so any
39+ // non-`NULL` arguments after the first `NULL` can safely be ignored.
40+ if ptr. is_null ( ) {
41+ // NOTE: On Apple platforms, `-[NSProcessInfo arguments]` does not
42+ // stop iterating here, but instead `continue`, always iterating
43+ // up until it reached `argc`.
44+ //
45+ // This difference will only matter in very specific circumstances
46+ // where `argc`/`argv` have been modified, but in unexpected ways,
47+ // so it likely doesn't really matter which option we choose.
48+ // See the following PR for further discussion:
49+ // <https://github.com/rust-lang/rust/pull/125225>
50+ break ;
51+ }
52+
53+ // SAFETY: Just checked that the pointer is not NULL, and arguments
54+ // are otherwise guaranteed to be valid C strings.
55+ let cstr = unsafe { CStr :: from_ptr ( ptr) } ;
56+ vec. push ( OsStringExt :: from_vec ( cstr. to_bytes ( ) . to_vec ( ) ) ) ;
57+ }
58+
59+ Args { iter : vec. into_iter ( ) }
2060}
2161
2262pub struct Args {
@@ -75,9 +115,7 @@ impl DoubleEndedIterator for Args {
75115 target_os = "hurd" ,
76116) ) ]
77117mod imp {
78- use super :: Args ;
79- use crate :: ffi:: { CStr , OsString } ;
80- use crate :: os:: unix:: prelude:: * ;
118+ use crate :: ffi:: c_char;
81119 use crate :: ptr;
82120 use crate :: sync:: atomic:: { AtomicIsize , AtomicPtr , Ordering } ;
83121
@@ -126,45 +164,19 @@ mod imp {
126164 init_wrapper
127165 } ;
128166
129- pub fn args ( ) -> Args {
130- Args { iter : clone ( ) . into_iter ( ) }
131- }
132-
133- fn clone ( ) -> Vec < OsString > {
134- unsafe {
135- // Load ARGC and ARGV, which hold the unmodified system-provided
136- // argc/argv, so we can read the pointed-to memory without atomics
137- // or synchronization.
138- //
139- // If either ARGC or ARGV is still zero or null, then either there
140- // really are no arguments, or someone is asking for `args()`
141- // before initialization has completed, and we return an empty
142- // list.
143- let argv = ARGV . load ( Ordering :: Relaxed ) ;
144- let argc = if argv. is_null ( ) { 0 } else { ARGC . load ( Ordering :: Relaxed ) } ;
145- let mut args = Vec :: with_capacity ( argc as usize ) ;
146- for i in 0 ..argc {
147- let ptr = * argv. offset ( i) as * const libc:: c_char ;
148-
149- // Some C commandline parsers (e.g. GLib and Qt) are replacing already
150- // handled arguments in `argv` with `NULL` and move them to the end. That
151- // means that `argc` might be bigger than the actual number of non-`NULL`
152- // pointers in `argv` at this point.
153- //
154- // To handle this we simply stop iterating at the first `NULL` argument.
155- //
156- // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments
157- // after the first `NULL` can safely be ignored.
158- if ptr. is_null ( ) {
159- break ;
160- }
161-
162- let cstr = CStr :: from_ptr ( ptr) ;
163- args. push ( OsStringExt :: from_vec ( cstr. to_bytes ( ) . to_vec ( ) ) ) ;
164- }
165-
166- args
167- }
167+ pub fn argc_argv ( ) -> ( isize , * const * const c_char ) {
168+ // Load ARGC and ARGV, which hold the unmodified system-provided
169+ // argc/argv, so we can read the pointed-to memory without atomics or
170+ // synchronization.
171+ //
172+ // If either ARGC or ARGV is still zero or null, then either there
173+ // really are no arguments, or someone is asking for `args()` before
174+ // initialization has completed, and we return an empty list.
175+ let argv = ARGV . load ( Ordering :: Relaxed ) ;
176+ let argc = if argv. is_null ( ) { 0 } else { ARGC . load ( Ordering :: Relaxed ) } ;
177+
178+ // Cast from `*mut *const u8` to `*const *const c_char`
179+ ( argc, argv. cast ( ) )
168180 }
169181}
170182
@@ -183,16 +195,14 @@ mod imp {
183195// of this used `[[NSProcessInfo processInfo] arguments]`.
184196#[ cfg( target_vendor = "apple" ) ]
185197mod imp {
186- use super :: Args ;
187- use crate :: ffi:: { c_char, c_int, CStr } ;
188- use crate :: os:: unix:: prelude:: * ;
198+ use crate :: ffi:: { c_char, c_int} ;
189199
190200 pub unsafe fn init ( _argc : isize , _argv : * const * const u8 ) {
191201 // No need to initialize anything in here, `libdyld.dylib` has already
192202 // done the work for us.
193203 }
194204
195- pub fn args ( ) -> Args {
205+ pub fn argc_argv ( ) -> ( isize , * const * const c_char ) {
196206 extern "C" {
197207 // These functions are in crt_externs.h.
198208 fn _NSGetArgc ( ) -> * mut c_int ;
@@ -212,42 +222,20 @@ mod imp {
212222 // SAFETY: Same as above.
213223 let argv = unsafe { _NSGetArgv ( ) . read ( ) } ;
214224
215- let mut vec = Vec :: with_capacity ( argc as usize ) ;
216-
217- for i in 0 ..argc {
218- // SAFETY: `argv` is at least as long as `argc`, so reading from
219- // it should be safe.
220- let ptr = unsafe { argv. offset ( i as isize ) . read ( ) } ;
221-
222- // Entries may have been removed from `argv` by setting them to
223- // NULL, without updating `argc`.
224- if ptr. is_null ( ) {
225- // We continue instead of break here, as an argument may have
226- // been set to `NULL` in the middle, instead of at the end of
227- // the list.
228- //
229- // This is the same as what `-[NSProcessInfo arguments]` does.
230- continue ;
231- }
232-
233- // SAFETY: Just checked that the pointer is not NULL, and
234- // arguments are otherwise guaranteed to be valid C strings.
235- let cstr = unsafe { CStr :: from_ptr ( ptr) } ;
236- vec. push ( OsStringExt :: from_vec ( cstr. to_bytes ( ) . to_vec ( ) ) ) ;
237- }
238-
239- Args { iter : vec. into_iter ( ) }
225+ // Cast from `*mut *mut c_char` to `*const *const c_char`
226+ ( argc as isize , argv. cast ( ) )
240227 }
241228}
242229
243230#[ cfg( any( target_os = "espidf" , target_os = "vita" ) ) ]
244231mod imp {
245- use super :: Args ;
232+ use crate :: ffi:: c_char;
233+ use crate :: ptr;
246234
247235 #[ inline( always) ]
248236 pub unsafe fn init ( _argc : isize , _argv : * const * const u8 ) { }
249237
250- pub fn args ( ) -> Args {
251- Args { iter : Vec :: new ( ) . into_iter ( ) }
238+ pub fn argc_argv ( ) -> ( isize , * const * const c_char ) {
239+ ( 0 , ptr :: null ( ) )
252240 }
253241}
0 commit comments