diff --git a/cursive/src/backends/curses/n.rs b/cursive/src/backends/curses/n.rs index 8da31a1a..4197ec8c 100644 --- a/cursive/src/backends/curses/n.rs +++ b/cursive/src/backends/curses/n.rs @@ -6,6 +6,7 @@ pub use ncurses; use log::{debug, warn}; use ncurses::mmask_t; +use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::ffi::CString; use std::fs::File; @@ -398,22 +399,53 @@ impl backend::Backend for Backend { ncurses::refresh(); } - fn print_at(&self, pos: Vec2, text: &str) { - ncurses::mvaddstr(pos.y as i32, pos.x as i32, text); + fn print_at<'a>(&self, pos: Vec2, text: &'a str) { + // Remove '\0' from &str or else nothing would get printed + // As for why delete instead of replace with eg. space, see: + // https://github.com/gyscos/cursive/pull/778#discussion_r1613859129 + let text = &delete_nuls(text); + let len = text.len() as i32; + // Ignore the value to avoid warning: unused `Result` that must be used + let _ = ncurses::mvaddnstr(pos.y as i32, pos.x as i32, text.as_ref(), len); } fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) { + // Remove '\0' from &str or else nothing would get printed + let text = &delete_nuls(text); + let len = text.len() as i32; if repetitions > 0 { - ncurses::mvaddstr(pos.y as i32, pos.x as i32, text); + let _ = ncurses::mvaddnstr(pos.y as i32, pos.x as i32, text, len); let mut dupes_left = repetitions - 1; while dupes_left > 0 { - ncurses::addstr(text); + let _ = ncurses::addnstr(text, len); dupes_left -= 1; } } } } +#[inline(always)] +fn delete_nuls<'a>(text: &'a str) -> Cow<'a, str> { + let text: Cow<'a, str> = if text.contains('\0') { + Cow::Owned(text.replace('\0', "")) + } else { + Cow::Borrowed(text) + }; + text +} + +#[test] +fn test_print_at_rep_nul_char_in_string() { + let text = "Some\0thing with \0nul\0s\0 in \0it"; + let expected = "Something with nuls in it"; + assert_eq!(expected, delete_nuls(text)); + + let backend = Backend::init().unwrap(); + // These don't panic, they replace the \0-es with nothing + backend.print_at(Vec2::new(10, 10), "abc\0de\0f"); + backend.print_at_rep(Vec2::new(10, 10), 10, "abc\0de\0f"); +} + /// Returns the Key enum corresponding to the given ncurses event. fn get_mouse_button(bare_event: i32) -> MouseButton { match bare_event {