refactor(config): parse config values from raw bytes like Zig
Zig parses config values (cli/args.zig, Config.zig field parsers, config/string.zig) from raw byte slices, and each field type decides whether it requires UTF-8 (InvalidValue otherwise) or stores bytes. The port escape-encoded raw bytes into Strings at the line reader and let fields parse the escaped text, so fields could observe encoding artifacts; successive audits kept finding such leaks (RV5-RV8). Field parsers now receive the original bytes: text fields validate UTF-8 where Zig does, byte/path fields store bytes (EncodedOs/OsString). This resolves all 9 RV8 findings (docs/rewrite/notes/review-rv8-codex.md), each with a regression test: Windows path conversion fails with BadPathName instead of substituting U+FFFD; Windows process launch uses strict UTF-8 like Zig's Command; the WTF-8 decoder accepts paired- surrogate encodings like Zig; literal U+FDD0 Unicode key triggers; selection-word-chars and clipboard-codepoint-map reject invalid UTF-8; font style / codepoint-map family names use original bytes; C title actions forward bytes; D-Bus new-window/Flatpak conversions match Zig. Design, sweep and validation: docs/rewrite/notes/final-bytes-refactor.md. Also moves the bundled iTerm2-Color-Schemes license out of resources/themes/ so +list-themes no longer lists it as a theme (output is now byte-identical to Ghostty 1.3.1). Done by Codex (gpt-6-astra), reviewed and verified by the orchestrator. Test Plan: - cargo test --workspace --no-fail-fast (3428 passed, exit 0) - just check-macos (both), just check-windows, just check-wasm - +show-config --default --docs, +list-keybinds, +list-actions --docs, +list-themes --plain compared with /usr/bin/ghostty
This commit is contained in:
1 parent
37a1646611
commit
672e8a4ae1
100 files changed
+2310
-1054
No files matched your search
@@ -45,6 +45,7 @@ struct Rec {
|
||||
/// (action tag, target tag, surface pointer)
|
||||
actions: Vec<(i32, i32, usize)>,
|
||||
titles: Vec<String>,
|
||||
raw_titles: Vec<Vec<u8>>,
|
||||
cell_size: Option<(u32, u32)>,
|
||||
writes: Vec<(i32, String, bool)>,
|
||||
reads: Vec<(i32, usize)>,
|
||||
@@ -86,6 +87,7 @@ unsafe extern "C" fn on_action(
|
||||
// set_title
|
||||
let t = unsafe { CStr::from_ptr(action.action.set_title.title) };
|
||||
r.titles.push(t.to_string_lossy().into_owned());
|
||||
r.raw_titles.push(t.to_bytes().to_vec());
|
||||
}
|
||||
25 => {
|
||||
let c = unsafe { action.action.cell_size };
|
||||
@@ -167,7 +169,7 @@ fn cs(s: &str) -> CString {
|
||||
}
|
||||
|
||||
impl Host {
|
||||
fn new(extra_config: &str) -> Option<Host> {
|
||||
fn new(extra_config: impl AsRef<[u8]>) -> Option<Host> {
|
||||
rec(|r| *r = Rec::default());
|
||||
let gl = match Headless::new() {
|
||||
Ok(g) => Rc::new(g),
|
||||
@@ -186,9 +188,11 @@ impl Host {
|
||||
write!(
|
||||
conf,
|
||||
"shell-integration = none\nwindow-padding-x = 0\nwindow-padding-y = 0\n\
|
||||
font-size = 12\nscrollback-limit = 100000\n{extra_config}\n"
|
||||
font-size = 12\nscrollback-limit = 100000\n"
|
||||
)
|
||||
.unwrap();
|
||||
conf.write_all(extra_config.as_ref()).unwrap();
|
||||
conf.write_all(b"\n").unwrap();
|
||||
let cfg = ghostty_config_new();
|
||||
let path = cs(conf.path().to_str().unwrap());
|
||||
unsafe {
|
||||
@@ -856,3 +860,26 @@ fn selection_clipboard_support_is_reported() {
|
||||
});
|
||||
unsafe { ghostty_surface_free(s) };
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_and_binding_titles_reach_c_as_original_bytes() {
|
||||
let Some(h) = Host::new(b"title=config\xff\xef\xb7\x90\xc3\xa9") else {
|
||||
return;
|
||||
};
|
||||
let surface = h.surface("sleep 30");
|
||||
assert!(rec(|r| r
|
||||
.raw_titles
|
||||
.iter()
|
||||
.any(|v| v == b"config\xff\xef\xb7\x90\xc3\xa9")));
|
||||
let action = b"set_surface_title:binding\xfe\xef\xb7\x90\xc3\xa9";
|
||||
// SAFETY: a live surface and the action slice is valid for the entire call.
|
||||
assert!(unsafe {
|
||||
ghostty_surface_binding_action(surface, action.as_ptr().cast(), action.len())
|
||||
});
|
||||
assert_eq!(
|
||||
rec(|r| r.raw_titles.last().cloned()).unwrap(),
|
||||
b"binding\xfe\xef\xb7\x90\xc3\xa9"
|
||||
);
|
||||
// SAFETY: the surface is live and owned by this test.
|
||||
unsafe { ghostty_surface_free(surface) };
|
||||
}
|
||||
Reference in new issue
Block a user