diff --git a/crates/engine/src/file_cmds/tests.rs b/crates/engine/src/file_cmds/tests.rs index 42313d368e..306e5e4035 100644 --- a/crates/engine/src/file_cmds/tests.rs +++ b/crates/engine/src/file_cmds/tests.rs @@ -1532,11 +1532,25 @@ fn reveal_in_finder_on_windows_passes_the_clicked_tabs_saved_path() { let r = s.execute("file.revealInFinder", json!({"document": 0, "dryRun": true})).unwrap(); // The Windows Explorer by full path: a bare name would first find an explorer.exe in the app's folder. - let explorer = std::path::Path::new(&std::env::var_os("SystemRoot").unwrap()).join("explorer.exe"); + let explorer = crate::layer_menu_cmds::windows_root(std::env::var_os("SystemRoot")).join("explorer.exe"); assert_eq!(r["program"], json!(explorer.to_string_lossy())); assert_eq!(r["args"], json!(["/select,", r"C:\项目 图片\海报 🖼.png"])); } +/// Windows tools run from the Windows folder `SystemRoot` names, and from `C:\Windows` when it is +/// unset or relative (which would resolve against the working directory). +#[cfg(windows)] +#[test] +fn the_windows_folder_falls_back_when_system_root_is_unset_or_relative() { + use crate::layer_menu_cmds::windows_root; + use std::path::PathBuf; + assert_eq!(windows_root(Some(r"D:\WinNT".into())), PathBuf::from(r"D:\WinNT")); + assert_eq!(windows_root(None), PathBuf::from(r"C:\Windows")); + assert_eq!(windows_root(Some(r"Windows".into())), PathBuf::from(r"C:\Windows")); + assert_eq!(windows_root(Some(r"\Windows".into())), PathBuf::from(r"C:\Windows")); + assert_eq!(windows_root(Some("".into())), PathBuf::from(r"C:\Windows")); +} + /// "Include All Subfolders" (AUTO-219-5, #3753): Batch and Image Processor take the images in /// subfolders too, each result keeping its subfolder under the output folder (so same-named files /// don't collide), and skip the output folder when it sits inside the input folder. diff --git a/crates/engine/src/layer_menu_cmds.rs b/crates/engine/src/layer_menu_cmds.rs index 0a29fc1c83..d17ba674e4 100644 --- a/crates/engine/src/layer_menu_cmds.rs +++ b/crates/engine/src/layer_menu_cmds.rs @@ -420,9 +420,14 @@ pub fn reveal_command(path: &str) -> Result<(String, Vec)> { /// an `explorer.exe` lying next to a portable PhotoCraft would start instead (#9666). #[cfg(windows)] fn system_program(program: &str) -> String { - let root = std::env::var_os("SystemRoot").map(std::path::PathBuf::from).filter(|p| p.is_absolute()); - let root = root.unwrap_or_else(|| std::path::PathBuf::from(r"C:\Windows")); - root.join(format!("{program}.exe")).to_string_lossy().into_owned() + windows_root(std::env::var_os("SystemRoot")).join(format!("{program}.exe")).to_string_lossy().into_owned() +} + +/// The Windows folder for the `SystemRoot` value `system_root`: `C:\Windows` when it is unset or +/// not an absolute path (a relative one would resolve against the working directory). +#[cfg(windows)] +pub(crate) fn windows_root(system_root: Option) -> std::path::PathBuf { + system_root.map(std::path::PathBuf::from).filter(|p| p.is_absolute()).unwrap_or_else(|| std::path::PathBuf::from(r"C:\Windows")) } #[cfg(not(windows))] diff --git a/crates/text/src/cjk.rs b/crates/text/src/cjk.rs index c5e5ac795e..185aa2f846 100644 --- a/crates/text/src/cjk.rs +++ b/crates/text/src/cjk.rs @@ -128,13 +128,18 @@ fn os_locale() -> Option { fn locale_query() -> std::process::Command { use std::os::windows::process::CommandExt; const CREATE_NO_WINDOW: u32 = 0x0800_0000; - let root = std::env::var_os("SystemRoot").map(std::path::PathBuf::from).filter(|p| p.is_absolute()); - let root = root.unwrap_or_else(|| std::path::PathBuf::from(r"C:\Windows")); - let mut cmd = std::process::Command::new(root.join("System32").join("reg.exe")); + let mut cmd = std::process::Command::new(windows_root(std::env::var_os("SystemRoot")).join("System32").join("reg.exe")); cmd.args(["query", "HKCU\\Control Panel\\International", "/v", "LocaleName"]).creation_flags(CREATE_NO_WINDOW); cmd } +/// The Windows folder for the `SystemRoot` value `system_root`: `C:\Windows` when it is unset or +/// not an absolute path (a relative one would resolve against the working directory). +#[cfg(target_os = "windows")] +fn windows_root(system_root: Option) -> std::path::PathBuf { + system_root.map(std::path::PathBuf::from).filter(|p| p.is_absolute()).unwrap_or_else(|| std::path::PathBuf::from(r"C:\Windows")) +} + #[cfg(not(any(target_os = "macos", target_os = "windows")))] fn os_locale() -> Option { None @@ -574,11 +579,24 @@ mod tests { #[test] fn locale_query_runs_the_system_reg() { let cmd = locale_query(); - let system = std::path::Path::new(&std::env::var_os("SystemRoot").unwrap()).join("System32").join("reg.exe"); + let system = windows_root(std::env::var_os("SystemRoot")).join("System32").join("reg.exe"); assert_eq!(std::path::Path::new(cmd.get_program()), system); assert!(os_locale().is_some_and(|l| !l.is_empty())); } + /// `SystemRoot` unset or relative (which would resolve against the working directory) falls + /// back to `C:\Windows`. + #[cfg(target_os = "windows")] + #[test] + fn the_windows_folder_falls_back_when_system_root_is_unset_or_relative() { + use std::path::PathBuf; + assert_eq!(windows_root(Some(r"D:\WinNT".into())), PathBuf::from(r"D:\WinNT")); + assert_eq!(windows_root(None), PathBuf::from(r"C:\Windows")); + assert_eq!(windows_root(Some(r"Windows".into())), PathBuf::from(r"C:\Windows")); + assert_eq!(windows_root(Some(r"\Windows".into())), PathBuf::from(r"C:\Windows")); + assert_eq!(windows_root(Some("".into())), PathBuf::from(r"C:\Windows")); + } + #[test] fn locale_orders() { assert_eq!(script_order(Some("ja_JP.UTF-8"))[0], Japanese);