diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/session.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/session.rs index 4b5cfce5df..5279d78596 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/session.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/session.rs @@ -177,19 +177,18 @@ fn spawn_output_reader( }) } -fn normalize_windows_tty_input(bytes: &[u8]) -> Vec { +fn normalize_windows_tty_input(bytes: &[u8], previous_was_cr: &mut bool) -> Vec { let mut normalized = Vec::with_capacity(bytes.len()); - let mut previous_was_cr = false; for &byte in bytes { if byte == b'\n' { - if !previous_was_cr { + if !*previous_was_cr { normalized.push(b'\r'); } normalized.push(b'\n'); - previous_was_cr = false; + *previous_was_cr = false; } else { normalized.push(byte); - previous_was_cr = byte == b'\r'; + *previous_was_cr = byte == b'\r'; } } normalized @@ -202,12 +201,13 @@ fn spawn_input_writer( normalize_newlines: bool, ) -> tokio::task::JoinHandle<()> { tokio::task::spawn_blocking(move || { + let mut previous_was_cr = false; while let Some(bytes) = writer_rx.blocking_recv() { let Some(handle) = input_write else { continue; }; let bytes = if normalize_newlines { - normalize_windows_tty_input(&bytes) + normalize_windows_tty_input(&bytes, &mut previous_was_cr) } else { bytes }; diff --git a/codex-rs/windows-sandbox-rs/src/winutil.rs b/codex-rs/windows-sandbox-rs/src/winutil.rs index dbf1e42b1f..8adc0633ea 100644 --- a/codex-rs/windows-sandbox-rs/src/winutil.rs +++ b/codex-rs/windows-sandbox-rs/src/winutil.rs @@ -88,7 +88,29 @@ pub fn argv_to_command_line(argv: &[String]) -> String { if args.is_empty() { return quote_windows_arg(program); } - return format!("{} {}", quote_windows_arg(program), args.join(" ")); + + let cmd_switch_index = args + .iter() + .position(|arg| arg.eq_ignore_ascii_case("/c") || arg.eq_ignore_ascii_case("/k")); + let rendered_args = if let Some(index) = cmd_switch_index { + let mut rendered = args[..=index] + .iter() + .map(|arg| quote_windows_arg(arg)) + .collect::>(); + let suffix = &args[index + 1..]; + if suffix.len() == 1 { + rendered.push(suffix[0].clone()); + } else { + rendered.extend(suffix.iter().map(|arg| quote_windows_arg(arg))); + } + rendered.join(" ") + } else { + args.iter() + .map(|arg| quote_windows_arg(arg)) + .collect::>() + .join(" ") + }; + return format!("{} {rendered_args}", quote_windows_arg(program)); } argv.iter() @@ -179,6 +201,21 @@ mod tests { "pwsh.exe -Command \"Write-Output \\\"hello world\\\"\"" ); } + + #[test] + fn argv_to_command_line_quotes_cmd_suffix_tokens_with_spaces() { + let argv = vec![ + "cmd.exe".to_string(), + "/c".to_string(), + "type".to_string(), + "C:\\Program Files\\a.txt".to_string(), + ]; + + assert_eq!( + argv_to_command_line(&argv), + "cmd.exe /c type \"C:\\Program Files\\a.txt\"" + ); + } } const SID_ADMINISTRATORS: &str = "S-1-5-32-544";