fix: Windows core build, embedded init, byte-exact paths, version info
Closes the remaining core/lib/termio/GTK rows (final-Z2.md): - ghostty-core compiles for Windows (Unix-only exec fields and file modes gated); checked with cargo check --target x86_64-pc-windows-gnu. - Embedded init: ensure_locale, SIGPIPE ignored in ghostty_init for C/Swift hosts, Apple crash tags; the argv passed to ghostty_init feeds ghostty_config_load_cli_args; ghostty_config_load_file accepts non-UTF-8 paths. - OSC 7 pwd, embedded surface cwd/command/env/initial input, ghostty_surface_text and GTK file drops keep raw bytes. - XTVERSION, TERM_PROGRAM_VERSION, the About dialog and crash metadata use ghostty_build_info instead of hardcoded versions. Test Plan: - cargo test --workspace (3327 passed) - cargo clippy --workspace --all-targets -- -D warnings
This commit is contained in:
1 parent
8519f1ba1b
commit
ebefad5031
30 files changed
+863
-80
No files matched your search
@@ -18,7 +18,7 @@ use std::{
|
||||
panic::{AssertUnwindSafe, catch_unwind},
|
||||
};
|
||||
|
||||
use ghostty_os::file::Rlimit;
|
||||
use ghostty_os::{file::Rlimit, osbytes::config_str_to_bytes};
|
||||
use rustix::{
|
||||
io::Errno,
|
||||
process::{self, WaitOptions},
|
||||
@@ -264,13 +264,13 @@ impl Command {
|
||||
}
|
||||
|
||||
fn prepare(&self) -> Result<Prepared, NulError> {
|
||||
let path = CString::new(self.path.as_str())?;
|
||||
let path = CString::new(config_str_to_bytes(&self.path))?;
|
||||
let argv = if self.args.is_empty() {
|
||||
vec![path.clone()]
|
||||
} else {
|
||||
self.args
|
||||
.iter()
|
||||
.map(|a| CString::new(a.as_str()))
|
||||
.map(|a| CString::new(config_str_to_bytes(a)))
|
||||
.collect::<Result<_, _>>()?
|
||||
};
|
||||
let envp = match &self.env {
|
||||
@@ -297,7 +297,7 @@ impl Command {
|
||||
#[cfg(target_vendor = "apple")]
|
||||
let candidates = exec_candidates(&self.path, std::env::var("PATH").ok().as_deref())
|
||||
.into_iter()
|
||||
.map(CString::new)
|
||||
.map(|c| CString::new(config_str_to_bytes(&c)))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(Prepared {
|
||||
path,
|
||||
@@ -315,7 +315,10 @@ impl Command {
|
||||
pub fn start(&mut self) -> Result<(), StartError> {
|
||||
// IMPORTANT: do all allocation prior to the fork().
|
||||
let prep = self.prepare()?;
|
||||
let cwd = self.cwd.as_deref().and_then(|c| CString::new(c).ok());
|
||||
let cwd = self
|
||||
.cwd
|
||||
.as_deref()
|
||||
.and_then(|c| CString::new(config_str_to_bytes(c)).ok());
|
||||
|
||||
let mut argv_ptrs: Vec<*const c_char> = prep.argv.iter().map(|c| c.as_ptr()).collect();
|
||||
argv_ptrs.push(std::ptr::null());
|
||||
@@ -876,6 +879,35 @@ mod tests {
|
||||
assert_eq!("/tmp\n", read_all(stdout));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_utf8_cwd_and_args_are_preserved() {
|
||||
use ghostty_os::osbytes::{bytes_to_config_string, os_from_bytes};
|
||||
let td = TempDir::init().unwrap();
|
||||
let stdout = test_file(&td, "stdout.txt");
|
||||
|
||||
// A directory whose name is not valid UTF-8, handed over with the
|
||||
// reversible byte escape used by the String based layers.
|
||||
let mut raw = ghostty_os::osbytes::os_bytes(td.path().as_os_str());
|
||||
raw.extend_from_slice(b"/d\xff");
|
||||
std::fs::create_dir(os_from_bytes(raw.clone())).unwrap();
|
||||
|
||||
let mut cmd = Command::new("/bin/sh", &["/bin/sh", "-c", "pwd -P; printf %s \"$0\""]);
|
||||
cmd.args.push(bytes_to_config_string(b"a\xffb"));
|
||||
cmd.stdout = Some(stdout.try_clone().unwrap().into());
|
||||
cmd.cwd = Some(bytes_to_config_string(&raw));
|
||||
cmd.start().unwrap();
|
||||
assert_eq!(cmd.wait(true).unwrap(), Exit::Exited(0));
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
std::io::Read::read_to_end(
|
||||
&mut std::fs::File::open(td.path().join("stdout.txt")).unwrap(),
|
||||
&mut bytes,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(bytes.windows(2).any(|w| w == b"d\xff"), "{bytes:?}");
|
||||
assert!(bytes.ends_with(b"a\xffb"), "{bytes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_cwd_is_ignored() {
|
||||
let td = TempDir::init().unwrap();
|
||||
|
||||
@@ -181,7 +181,7 @@ impl Exec {
|
||||
// set that on the terminal now. This allows rapidly initializing
|
||||
// new surfaces to use the proper pwd.
|
||||
if let Some(cwd) = &self.subprocess.cwd
|
||||
&& let Err(err) = term.set_pwd(cwd.as_bytes())
|
||||
&& let Err(err) = term.set_pwd(&ghostty_os::osbytes::config_str_to_bytes(cwd))
|
||||
{
|
||||
log::warn!("error setting initial pwd err={err:?}");
|
||||
}
|
||||
@@ -907,7 +907,10 @@ impl Subprocess {
|
||||
|
||||
// Add the environment variables that override any others.
|
||||
for (k, v) in cfg.env_override.iter() {
|
||||
env.insert(k, v);
|
||||
env.insert(
|
||||
ghostty_os::osbytes::config_str_to_os(k),
|
||||
ghostty_os::osbytes::config_str_to_os(v),
|
||||
);
|
||||
}
|
||||
|
||||
// Build our args list
|
||||
@@ -925,7 +928,7 @@ impl Subprocess {
|
||||
// https://github.com/kovidgoyal/kitty/issues/1595
|
||||
// https://github.com/ghostty-org/ghostty/discussions/7769
|
||||
if let Some(pwd) = &cwd {
|
||||
env.insert("PWD", pwd.clone());
|
||||
env.insert("PWD", ghostty_os::osbytes::config_str_to_os(pwd));
|
||||
}
|
||||
|
||||
Ok(Subprocess {
|
||||
@@ -1037,7 +1040,7 @@ impl Subprocess {
|
||||
}
|
||||
}
|
||||
Some(proposed) => {
|
||||
if std::path::Path::new(proposed).exists() {
|
||||
if std::path::Path::new(&ghostty_os::osbytes::config_str_to_os(proposed)).exists() {
|
||||
Some(proposed.clone())
|
||||
} else {
|
||||
log::warn!("cannot access cwd, ignoring: {proposed}");
|
||||
|
||||
@@ -86,7 +86,7 @@ use crate::{
|
||||
};
|
||||
|
||||
/// `build_config.version_string`.
|
||||
pub const VERSION_STRING: &str = "1.3.1";
|
||||
pub const VERSION_STRING: &str = ghostty_build_info::VERSION_STRING;
|
||||
|
||||
/// The long-lived state of the handler (everything but the terminal).
|
||||
pub struct HandlerState {
|
||||
@@ -1693,7 +1693,11 @@ mod tests {
|
||||
fn xtversion() {
|
||||
let mut h = Harness::new();
|
||||
h.feed(b"\x1b[>q");
|
||||
assert_eq!(b"\x1bP>|ghostty 1.3.1\x1b\\".as_slice(), h.written());
|
||||
let want = format!(
|
||||
"\x1bP>|ghostty {}\x1b\\",
|
||||
ghostty_build_info::VERSION_STRING
|
||||
);
|
||||
assert_eq!(want.as_bytes(), h.written());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -173,7 +173,7 @@ impl ThreadEnterState {
|
||||
let mut out = Vec::with_capacity(self.input.len());
|
||||
for item in &self.input {
|
||||
match item {
|
||||
ReadableIO::Raw(v) => out.push(v.clone().into_bytes()),
|
||||
ReadableIO::Raw(v) => out.push(ghostty_os::osbytes::config_str_to_bytes(v)),
|
||||
ReadableIO::Path(path) => {
|
||||
let f = std::fs::File::open(path).map_err(|err| {
|
||||
log::warn!("failed to open input file={path} err={err}");
|
||||
|
||||
Reference in new issue
Block a user