@@ -352,17 +352,24 @@ pub fn eval_entry<'tcx>(
352
352
///
353
353
/// Panics if the zeroth argument contains the `"` character because doublequotes
354
354
/// in argv[0] cannot be encoded using the standard command line parsing rules.
355
+ ///
356
+ /// Further reading:
357
+ /// * [Parsing C++ command-line arguments](https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-160#parsing-c-command-line-arguments)
358
+ /// * [The C/C++ Parameter Parsing Rules](https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES)
355
359
fn args_to_utf16_command_string < I , T > ( mut args : I ) -> Vec < u16 >
356
360
where
357
361
I : Iterator < Item = T > ,
358
362
T : AsRef < str > ,
359
363
{
360
364
// Parse argv[0]. Slashes aren't escaped. Literal double quotes are not allowed.
361
- let mut cmd = if let Some ( arg0) = args. next ( ) {
365
+ let mut cmd = {
366
+ let arg0 = if let Some ( arg0) = args. next ( ) {
367
+ arg0
368
+ } else {
369
+ return vec ! [ 0 ] ;
370
+ } ;
362
371
let arg0 = arg0. as_ref ( ) ;
363
- if arg0. is_empty ( ) {
364
- "\" \" " . into ( )
365
- } else if arg0. contains ( '"' ) {
372
+ if arg0. contains ( '"' ) {
366
373
panic ! ( "argv[0] cannot contain a doublequote (\" ) character" ) ;
367
374
} else {
368
375
// Always surround argv[0] with quotes.
@@ -372,8 +379,6 @@ where
372
379
s. push ( '"' ) ;
373
380
s
374
381
}
375
- } else {
376
- return vec ! [ 0 ] ;
377
382
} ;
378
383
379
384
// Build the other arguments.
@@ -383,8 +388,15 @@ where
383
388
if arg. is_empty ( ) {
384
389
cmd. push_str ( "\" \" " ) ;
385
390
} else if !arg. bytes ( ) . any ( |c| matches ! ( c, b'"' | b'\t' | b' ' ) ) {
391
+ // No quote, tab, or space -- no escaping required.
386
392
cmd. push_str ( arg) ;
387
393
} else {
394
+ // Spaces and tabs are escaped by surrounding them in quotes.
395
+ // Quotes are themselves escaped by using backslashes when in a
396
+ // quoted block.
397
+ // Backslashes only need to be escaped when one or more are directly
398
+ // followed by a quote. Otherwise they are taken literally.
399
+
388
400
cmd. push ( '"' ) ;
389
401
let mut chars = arg. chars ( ) . peekable ( ) ;
390
402
loop {
@@ -418,3 +430,21 @@ where
418
430
}
419
431
cmd. encode_utf16 ( ) . chain ( iter:: once ( 0 ) ) . collect ( )
420
432
}
433
+
434
+ #[ cfg( test) ]
435
+ mod tests {
436
+ use super :: * ;
437
+ #[ test]
438
+ #[ should_panic( expected = "argv[0] cannot contain a doublequote (\" ) character" ) ]
439
+ fn windows_argv0_panic_on_quote ( ) {
440
+ args_to_utf16_command_string ( [ "\" " ] . iter ( ) ) ;
441
+ }
442
+ #[ test]
443
+ fn windows_argv0_no_escape ( ) {
444
+ // Ensure that a trailing backslash in argv[0] is not escaped.
445
+ let cmd = String :: from_utf16_lossy ( & args_to_utf16_command_string (
446
+ [ r"C:\Program Files\" , "arg1" ] . iter ( ) ,
447
+ ) ) ;
448
+ assert_eq ! ( cmd. trim_end_matches( "\0 " ) , r#""C:\Program Files\" arg1"# ) ;
449
+ }
450
+ }
0 commit comments