fix: preserve non-UTF-8 bytes in OS, config and CLI layers

Zig treats paths, arguments and environment values as byte slices; the
port converted them lossily in many places. Now (final-Z1.md):
- argv, env (incl. present non-UTF-8 values), HOME, PATH lookup,
  passwd, resources dir, TMPDIR, cgroup, config path canonicalization,
  +validate-config, +list-themes and Zig-string escapes (\xFF) keep raw
  bytes. String-based config values carry bytes via ghostty_os::osbytes,
  a bijective escape (noncharacter U+FDD0 + byte) that leaves all valid
  UTF-8 - including Nerd Font private-use icons - unchanged.
- os/file rlimit fallbacks exist on all targets.
- i18n locale canonicalization is an exact port of libintl's tables
  (e.g. pa-Guru -> pa_IN).
- New ghostty-build-info crate is the single source of version, channel,
  commit and build mode for all consumers.

Test Plan:
- cargo test -p ghostty-os -p ghostty-config -p ghostty-cli
  (incl. randomized osbytes round-trip property tests)
This commit is contained in:
ddidderr committed 2026-10-12 08:14:02 +02:00
1 parent e4cdd8e007
commit 395cff8981
32 files changed
+1464 -333

No files matched your search

+40 -6
View File
@@ -70,19 +70,41 @@ pub fn restore_max_files(lim: Rlimit) {
let _ = setrlimit(Resource::Nofile, lim);
}
/// Return the recommended path for temporary files.
pub fn alloc_tmp_dir() -> Option<String> {
/// Resource limit placeholder on platforms without `rlimit` (Zig:
/// `rlimit = if (@hasDecl(posix.system, "rlimit")) posix.rlimit else struct {}`).
#[cfg(not(unix))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Rlimit;
/// Unsupported-platform fallback: returns `None` like Zig.
#[cfg(not(unix))]
pub fn fix_max_files() -> Option<Rlimit> {
None
}
/// Unsupported-platform fallback: no-op like Zig.
#[cfg(not(unix))]
pub fn restore_max_files(_lim: Rlimit) {}
/// Return the recommended path for temporary files (byte-preserving).
pub fn alloc_tmp_dir_os() -> Option<std::path::PathBuf> {
if cfg!(windows) {
// TODO: what is a good fallback path on windows? (same as Zig)
return std::env::var_os("TMP").map(|v| v.to_string_lossy().into_owned());
return std::env::var_os("TMP").map(Into::into);
}
if let Some(v) = std::env::var_os("TMPDIR") {
return Some(v.to_string_lossy().into_owned());
return Some(v.into());
}
if let Some(v) = std::env::var_os("TMP") {
return Some(v.to_string_lossy().into_owned());
return Some(v.into());
}
Some("/tmp".to_owned())
Some("/tmp".into())
}
/// Return the recommended path for temporary files. Non-UTF-8 bytes are
/// carried via [`crate::osbytes`]; see [`alloc_tmp_dir_os`].
pub fn alloc_tmp_dir() -> Option<String> {
alloc_tmp_dir_os().map(|p| crate::osbytes::os_to_config_string(p.as_os_str()))
}
#[cfg(all(test, unix))]
@@ -103,6 +125,18 @@ mod tests {
});
}
#[test]
fn tmp_dir_non_utf8() {
use std::os::unix::ffi::{OsStrExt, OsStringExt};
let v = std::ffi::OsString::from_vec(b"/tmp/\xff".to_vec());
temp_env::with_vars([("TMPDIR", Some(v.clone())), ("TMP", None)], || {
assert_eq!(
alloc_tmp_dir_os().unwrap().as_os_str().as_bytes(),
b"/tmp/\xff"
);
});
}
#[cfg(unix)]
#[test]
fn fix_and_restore_max_files() {