5
5
6
6
#![ allow( dead_code) ] // runtime init functions not used during testing
7
7
8
- use crate :: ffi:: OsString ;
8
+ use crate :: ffi:: { CStr , OsString } ;
9
9
use crate :: fmt;
10
+ use crate :: os:: unix:: ffi:: OsStringExt ;
10
11
use crate :: vec;
11
12
12
13
/// One-time global initialization.
@@ -16,7 +17,46 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
16
17
17
18
/// Returns the command line arguments
18
19
pub 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 ( ) }
20
60
}
21
61
22
62
pub struct Args {
@@ -75,9 +115,7 @@ impl DoubleEndedIterator for Args {
75
115
target_os = "hurd" ,
76
116
) ) ]
77
117
mod imp {
78
- use super :: Args ;
79
- use crate :: ffi:: { CStr , OsString } ;
80
- use crate :: os:: unix:: prelude:: * ;
118
+ use crate :: ffi:: c_char;
81
119
use crate :: ptr;
82
120
use crate :: sync:: atomic:: { AtomicIsize , AtomicPtr , Ordering } ;
83
121
@@ -126,45 +164,19 @@ mod imp {
126
164
init_wrapper
127
165
} ;
128
166
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 ( ) )
168
180
}
169
181
}
170
182
@@ -183,16 +195,14 @@ mod imp {
183
195
// of this used `[[NSProcessInfo processInfo] arguments]`.
184
196
#[ cfg( target_vendor = "apple" ) ]
185
197
mod 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} ;
189
199
190
200
pub unsafe fn init ( _argc : isize , _argv : * const * const u8 ) {
191
201
// No need to initialize anything in here, `libdyld.dylib` has already
192
202
// done the work for us.
193
203
}
194
204
195
- pub fn args ( ) -> Args {
205
+ pub fn argc_argv ( ) -> ( isize , * const * const c_char ) {
196
206
extern "C" {
197
207
// These functions are in crt_externs.h.
198
208
fn _NSGetArgc ( ) -> * mut c_int ;
@@ -212,42 +222,20 @@ mod imp {
212
222
// SAFETY: Same as above.
213
223
let argv = unsafe { _NSGetArgv ( ) . read ( ) } ;
214
224
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 ( ) )
240
227
}
241
228
}
242
229
243
230
#[ cfg( any( target_os = "espidf" , target_os = "vita" ) ) ]
244
231
mod imp {
245
- use super :: Args ;
232
+ use crate :: ffi:: c_char;
233
+ use crate :: ptr;
246
234
247
235
#[ inline( always) ]
248
236
pub unsafe fn init ( _argc : isize , _argv : * const * const u8 ) { }
249
237
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 ( ) )
252
240
}
253
241
}
0 commit comments