fix: close the RV6 verification gaps
Resolves the 7 gap groups in docs/rewrite/notes/review-rv6-codex.md (final-Z8.md): - crash (macOS): request_abort_dump returns false on mach_msg receive timeout/error so the signal-path fallback runs; EXC_SOFT_SIGNAL keeps the signal number in the minidump exception info. - Default SHELL read as an OS string and stored in encoded command form. - macOS executable search works on raw PATH bytes; the default path is used only when PATH is unset. - Windows: config env overrides are decoded; EnvMap compares keys as WTF-16 units with per-unit Unicode simple uppercasing (no lossy conversion, distinct lone-surrogate keys stay distinct). - +show-config writes original bytes for encoded OS data, so export + reload round-trips; UTF-8 configs are byte-identical as before. - Zig's ensureLocale chain lives in ghostty-os for all Unix targets and is used by both the GTK app and the C API init. Also makes the quick-terminal GTK replay test independent of compositor focus: GTK's is-active is read-only, so when niri actually focuses the layer surface the simulated focus-out cannot drive autohide and that assertion is skipped (the production logic is unchanged and matches Zig). Test Plan: - cargo test --workspace (3390 passed, incl. tests/gtk_replay.rs) - just check-macos (both), just check-windows, just check-wasm
This commit is contained in:
1 parent
e57e54cbf2
commit
5c5d916960
12 files changed
+452
-133
No files matched your search
@@ -240,16 +240,19 @@ const DEFAULT_PATH: &str = "/usr/local/bin:/bin/:/usr/bin";
|
||||
/// if it contains a slash, otherwise `<dir>/<file>` per PATH entry (an empty
|
||||
/// entry means the current directory).
|
||||
#[cfg(any(target_vendor = "apple", test))]
|
||||
fn exec_candidates(file: &str, path_env: Option<&str>) -> Vec<String> {
|
||||
if file.contains('/') {
|
||||
return vec![file.to_owned()];
|
||||
fn exec_candidates(file: &[u8], path_env: Option<&[u8]>) -> Vec<Vec<u8>> {
|
||||
if file.contains(&b'/') {
|
||||
return vec![file.to_vec()];
|
||||
}
|
||||
path_env
|
||||
.unwrap_or(DEFAULT_PATH)
|
||||
.split(':')
|
||||
.unwrap_or(DEFAULT_PATH.as_bytes())
|
||||
.split(|b| *b == b':')
|
||||
.map(|dir| {
|
||||
let dir = if dir.is_empty() { "." } else { dir };
|
||||
format!("{dir}/{file}")
|
||||
let dir = if dir.is_empty() { b".".as_slice() } else { dir };
|
||||
let mut c = dir.to_vec();
|
||||
c.push(b'/');
|
||||
c.extend_from_slice(file);
|
||||
c
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -295,10 +298,15 @@ impl Command {
|
||||
.collect::<Result<_, _>>()?,
|
||||
};
|
||||
#[cfg(target_vendor = "apple")]
|
||||
let candidates = exec_candidates(&self.path, std::env::var("PATH").ok().as_deref())
|
||||
.into_iter()
|
||||
.map(|c| CString::new(config_str_to_bytes(&c)))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let candidates = exec_candidates(
|
||||
&config_str_to_bytes(&self.path),
|
||||
std::env::var_os("PATH")
|
||||
.map(|p| ghostty_os::osbytes::os_bytes(&p))
|
||||
.as_deref(),
|
||||
)
|
||||
.into_iter()
|
||||
.map(CString::new)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(Prepared {
|
||||
path,
|
||||
argv,
|
||||
@@ -1035,15 +1043,29 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn exec_candidates_search_path() {
|
||||
assert_eq!(exec_candidates("/bin/sh", Some("/x")), ["/bin/sh"]);
|
||||
assert_eq!(exec_candidates("a/b", None), ["a/b"]);
|
||||
let c = |f: &str, p: Option<&str>| {
|
||||
exec_candidates(f.as_bytes(), p.map(str::as_bytes))
|
||||
.into_iter()
|
||||
.map(|v| String::from_utf8(v).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
assert_eq!(c("/bin/sh", Some("/x")), ["/bin/sh"]);
|
||||
assert_eq!(c("a/b", None), ["a/b"]);
|
||||
assert_eq!(
|
||||
exec_candidates("sh", Some("/usr/bin::/bin")),
|
||||
c("sh", Some("/usr/bin::/bin")),
|
||||
["/usr/bin/sh", "./sh", "/bin/sh"]
|
||||
);
|
||||
assert_eq!(
|
||||
exec_candidates("sh", None),
|
||||
c("sh", None),
|
||||
["/usr/local/bin/sh", "/bin//sh", "/usr/bin/sh"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_candidates_keep_raw_path_bytes() {
|
||||
// Non-UTF-8 PATH bytes and a literal U+FDD0 are used verbatim.
|
||||
let c = exec_candidates(b"sh", Some(b"/a\xff:/b\xef\xb7\x90x"));
|
||||
assert_eq!(c[0], b"/a\xff/sh");
|
||||
assert_eq!(c[1], b"/b\xef\xb7\x90x/sh");
|
||||
}
|
||||
}
|
||||
@@ -583,6 +583,17 @@ fn write_thread_main(handle: &OwnedHandle, rx: &Receiver<Vec<u8>>) {
|
||||
//-------------------------------------------------------------------------
|
||||
// Subprocess
|
||||
|
||||
/// Insert the config `env` overrides, decoding the encoded key/value text
|
||||
/// to OS strings (WTF-16 on Windows) like the Unix implementation.
|
||||
fn apply_env_overrides(env: &mut EnvMap, overrides: &RepeatableStringMap) {
|
||||
for (k, v) in overrides.iter() {
|
||||
env.insert(
|
||||
ghostty_os::osbytes::config_str_to_os(k),
|
||||
ghostty_os::osbytes::config_str_to_os(v),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct Subprocess {
|
||||
cwd: Option<EncodedOs>,
|
||||
env: Option<EnvMap>,
|
||||
@@ -687,9 +698,7 @@ impl Subprocess {
|
||||
};
|
||||
|
||||
// Add the environment variables that override any others.
|
||||
for (k, v) in cfg.env_override.iter() {
|
||||
env.insert(k, v);
|
||||
}
|
||||
apply_env_overrides(&mut env, &cfg.env_override);
|
||||
|
||||
// Build our args list. If we fail to find the shell, we'd still like
|
||||
// to try to run something so Ghostty can launch.
|
||||
@@ -926,6 +935,27 @@ pub fn exec_command(command: &ConfigCommand, windir: Option<&str>) -> Result<Vec
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn env_overrides_are_decoded() {
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
use ghostty_config::cli_args::ConfigField;
|
||||
|
||||
// A lone surrogate (escaped) value and a literal U+FDD0 key.
|
||||
let lone = EncodedOs::from_wide(&[0x61, 0xD800]).into_encoded();
|
||||
let lit = EncodedOs::from_plain_text("K\u{fdd0}").into_encoded();
|
||||
let mut m = RepeatableStringMap::default();
|
||||
m.parse_cli(Some(&format!("A={lone}"))).unwrap();
|
||||
m.parse_cli(Some(&format!("{lit}=v"))).unwrap();
|
||||
let mut env = EnvMap::new();
|
||||
super::apply_env_overrides(&mut env, &m);
|
||||
assert_eq!(
|
||||
env.get("A").unwrap(),
|
||||
std::ffi::OsString::from_wide(&[0x61, 0xD800])
|
||||
);
|
||||
assert_eq!(env.get("K\u{fdd0}").unwrap(), "v");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminfo_dir_is_sibling_of_resources_and_lossless() {
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
|
||||
Reference in new issue
Block a user