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:
1 parent
e4cdd8e007
commit
395cff8981
32 files changed
+1464
-333
No files matched your search
@@ -5,9 +5,28 @@
|
||||
//! don't panic).
|
||||
|
||||
/// Returns an iterator over the command line arguments.
|
||||
///
|
||||
/// If [`set_argv`] was called (the embedded `ghostty_init`, Zig:
|
||||
/// `std.os.argv = argv[0..argc]`) those arguments are returned instead of
|
||||
/// the process arguments.
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn iterator() -> std::env::ArgsOs {
|
||||
std::env::args_os()
|
||||
pub fn iterator() -> std::vec::IntoIter<std::ffi::OsString> {
|
||||
match ARGV_OVERRIDE.lock().ok().and_then(|g| g.clone()) {
|
||||
Some(v) => v.into_iter(),
|
||||
None => std::env::args_os().collect::<Vec<_>>().into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
static ARGV_OVERRIDE: std::sync::Mutex<Option<Vec<std::ffi::OsString>>> =
|
||||
std::sync::Mutex::new(None);
|
||||
|
||||
/// Replace the process arguments seen by [`iterator`] (Zig's assignment to
|
||||
/// `std.os.argv` in `ghostty_init`). Has no effect on macOS, where the
|
||||
/// arguments always come from `NSProcessInfo` (as in Zig).
|
||||
pub fn set_argv(args: Vec<std::ffi::OsString>) {
|
||||
if let Ok(mut g) = ARGV_OVERRIDE.lock() {
|
||||
*g = Some(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the command line arguments.
|
||||
@@ -27,15 +46,25 @@ pub fn iterator() -> std::vec::IntoIter<std::ffi::OsString> {
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
/// Collect the process arguments as `String`s without ever panicking on
|
||||
/// non-UTF-8 bytes (invalid sequences become U+FFFD). The CLI/config layers
|
||||
/// are string based; `+action` dispatch only depends on the ASCII prefix.
|
||||
pub fn collect_lossy() -> Vec<String> {
|
||||
lossy(iterator())
|
||||
/// Collect the process arguments as raw OS strings (no conversion, never
|
||||
/// panics on non-UTF-8 bytes).
|
||||
pub fn collect_os() -> Vec<std::ffi::OsString> {
|
||||
iterator().collect()
|
||||
}
|
||||
|
||||
fn lossy(it: impl Iterator<Item = std::ffi::OsString>) -> Vec<String> {
|
||||
it.map(|a| a.to_string_lossy().into_owned()).collect()
|
||||
/// Collect the process arguments as raw byte vectors (Zig `[]const u8`).
|
||||
pub fn collect_bytes() -> Vec<Vec<u8>> {
|
||||
iterator().map(|a| crate::osbytes::os_bytes(&a)).collect()
|
||||
}
|
||||
|
||||
/// Collect the process arguments as `String`s using the reversible
|
||||
/// byte-escape encoding of [`crate::osbytes`]: no bytes are lost, invalid
|
||||
/// UTF-8 bytes are carried as private-use escapes that the config layer
|
||||
/// decodes back to the original bytes.
|
||||
pub fn collect_config_strings() -> Vec<String> {
|
||||
iterator()
|
||||
.map(|a| crate::osbytes::os_to_config_string(&a))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -50,13 +79,13 @@ mod tests {
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn lossy_does_not_panic_on_non_utf8() {
|
||||
fn config_strings_keep_non_utf8_bytes() {
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
let args = vec![
|
||||
std::ffi::OsString::from("ghostty"),
|
||||
std::ffi::OsString::from_vec(vec![b'+', b'x', 0xff]),
|
||||
];
|
||||
let out = lossy(args.into_iter());
|
||||
assert_eq!(out, vec!["ghostty".to_owned(), "+x\u{fffd}".to_owned()]);
|
||||
let a = std::ffi::OsString::from_vec(vec![b'+', b'x', 0xff]);
|
||||
let s = crate::osbytes::os_to_config_string(&a);
|
||||
assert_eq!(
|
||||
crate::osbytes::config_str_to_bytes(&s),
|
||||
vec![b'+', b'x', 0xff]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user