mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-14 08:19:10 +08:00
fix(terminal): escape paths for PowerShell and cmd.exe in Windows launcher
This commit is contained in:
@@ -1192,7 +1192,8 @@ del \"%~f0\" >nul 2>&1
|
||||
std::fs::write(&bat_file, &content).map_err(|e| format!("写入批处理文件失败: {e}"))?;
|
||||
|
||||
let bat_path = bat_file.to_string_lossy();
|
||||
let ps_cmd = format!("& '{}'", bat_path);
|
||||
let bat_path_for_cmd = build_windows_cmd_command_str(&bat_path);
|
||||
let ps_cmd = format!("& '{}'", escape_powershell_single_quoted(&bat_path));
|
||||
|
||||
// Try the preferred terminal first
|
||||
let result = match terminal {
|
||||
@@ -1200,8 +1201,10 @@ del \"%~f0\" >nul 2>&1
|
||||
&["powershell", "-NoExit", "-Command", &ps_cmd],
|
||||
"PowerShell",
|
||||
),
|
||||
"wt" => run_windows_start_command(&["wt", "cmd", "/K", &bat_path], "Windows Terminal"),
|
||||
_ => run_windows_start_command(&["cmd", "/K", &bat_path], "cmd"), // "cmd" or default
|
||||
"wt" => {
|
||||
run_windows_start_command(&["wt", "cmd", "/K", &bat_path_for_cmd], "Windows Terminal")
|
||||
}
|
||||
_ => run_windows_start_command(&["cmd", "/K", &bat_path_for_cmd], "cmd"), // "cmd" or default
|
||||
};
|
||||
|
||||
// If preferred terminal fails and it's not the default, try cmd as fallback
|
||||
@@ -1211,7 +1214,7 @@ del \"%~f0\" >nul 2>&1
|
||||
terminal,
|
||||
result.as_ref().err()
|
||||
);
|
||||
return run_windows_start_command(&["cmd", "/K", &bat_path], "cmd");
|
||||
return run_windows_start_command(&["cmd", "/K", &bat_path_for_cmd], "cmd");
|
||||
}
|
||||
|
||||
result
|
||||
@@ -1231,6 +1234,26 @@ fn shell_single_quote(value: &str) -> String {
|
||||
format!("'{}'", value.replace('\'', "'\"'\"'"))
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
fn escape_powershell_single_quoted(value: &str) -> String {
|
||||
value.replace('\'', "''")
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
fn build_windows_cmd_command_str(path: &str) -> String {
|
||||
// Avoid handing `cmd /K` a string that starts with a single quoted path:
|
||||
// per cmd.exe parsing rules, those outer quotes may be stripped when the
|
||||
// quoted text contains shell metacharacters. An explicit `call "..."` form
|
||||
// keeps the command from starting with a quote while still protecting
|
||||
// spaces and other special characters in the batch path.
|
||||
format!("call \"{}\"", escape_windows_cmd_quoted_path(path))
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
fn escape_windows_cmd_quoted_path(value: &str) -> String {
|
||||
value.replace('%', "%%")
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
fn is_windows_unc_path(path: &str) -> bool {
|
||||
path.starts_with(r"\\")
|
||||
@@ -1472,6 +1495,20 @@ mod tests {
|
||||
assert_eq!(command, "cd '/tmp/project O'\"'\"'Brien' || exit 1\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_powershell_single_quoted_doubles_embedded_quotes() {
|
||||
let escaped = escape_powershell_single_quoted(r"C:\Users\O'Brien\AppData\Local\Temp");
|
||||
|
||||
assert_eq!(escaped, r"C:\Users\O''Brien\AppData\Local\Temp");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_windows_cmd_command_str_quotes_and_escapes_metacharacters() {
|
||||
let command = build_windows_cmd_command_str(r"C:\Users\100%&(test)\cc switch.bat");
|
||||
|
||||
assert_eq!(command, "call \"C:\\Users\\100%%&(test)\\cc switch.bat\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_windows_cwd_command_str_uses_cd_for_drive_paths() {
|
||||
let command = build_windows_cwd_command_str(r"C:\work\repo");
|
||||
|
||||
Reference in New Issue
Block a user