Skip to content

Commit cfd1316

Browse files
committed
Apply review changes
1 parent f6cedbc commit cfd1316

File tree

1 file changed

+36
-6
lines changed

1 file changed

+36
-6
lines changed

src/eval.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,17 +352,24 @@ pub fn eval_entry<'tcx>(
352352
///
353353
/// Panics if the zeroth argument contains the `"` character because doublequotes
354354
/// 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)
355359
fn args_to_utf16_command_string<I, T>(mut args: I) -> Vec<u16>
356360
where
357361
I: Iterator<Item = T>,
358362
T: AsRef<str>,
359363
{
360364
// 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+
};
362371
let arg0 = arg0.as_ref();
363-
if arg0.is_empty() {
364-
"\"\"".into()
365-
} else if arg0.contains('"') {
372+
if arg0.contains('"') {
366373
panic!("argv[0] cannot contain a doublequote (\") character");
367374
} else {
368375
// Always surround argv[0] with quotes.
@@ -372,8 +379,6 @@ where
372379
s.push('"');
373380
s
374381
}
375-
} else {
376-
return vec![0];
377382
};
378383

379384
// Build the other arguments.
@@ -383,8 +388,15 @@ where
383388
if arg.is_empty() {
384389
cmd.push_str("\"\"");
385390
} else if !arg.bytes().any(|c| matches!(c, b'"' | b'\t' | b' ')) {
391+
// No quote, tab, or space -- no escaping required.
386392
cmd.push_str(arg);
387393
} 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+
388400
cmd.push('"');
389401
let mut chars = arg.chars().peekable();
390402
loop {
@@ -418,3 +430,21 @@ where
418430
}
419431
cmd.encode_utf16().chain(iter::once(0)).collect()
420432
}
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

Comments
 (0)