diff --git a/build.rs b/build.rs index 435b10d1f..f4a5e1f33 100644 --- a/build.rs +++ b/build.rs @@ -30,29 +30,42 @@ fn main() { // Handle case where CMake has found curses for us and where we have to find it ourselves. rsconf::rebuild_if_env_changed("CURSES_LIBRARY_LIST"); - if let Ok(lib_path_list) = env::var("CURSES_LIBRARY_LIST") { + let curses_libraries = if let Ok(lib_path_list) = env::var("CURSES_LIBRARY_LIST") { let lib_paths = lib_path_list.split(',').filter(|s| !s.is_empty()); let curses_libnames: Vec<_> = lib_paths .map(|libpath| { let stem = Path::new(libpath).file_stem().unwrap().to_str().unwrap(); // Ubuntu-32bit-fetched-pcre2's ncurses doesn't have the "lib" prefix. - stem.strip_prefix("lib").unwrap_or(stem) + stem.strip_prefix("lib").unwrap_or(stem).to_string() }) .collect(); // We don't need to test the libs because presumably CMake already did rsconf::link_libraries(&curses_libnames, LinkType::Default); + curses_libnames } else { - let mut curses_found = false; - for lib in ["ncurses", "curses"] { + let mut curses_libraries = vec![]; + let libs = ["ncurses", "curses"]; + for lib in libs { if target.has_library(lib) && target.has_symbol("setupterm", lib) { rsconf::link_library(lib, LinkType::Default); - curses_found = true; + curses_libraries.push(lib.to_string()); break; } } - if !curses_found { - rsconf::warn!("Could not locate a compatible curses library!"); + if curses_libraries.is_empty() { + panic!("Could not locate a compatible curses library (tried {libs:?})"); + } + curses_libraries + }; + + for lib in curses_libraries { + if target.has_symbol("_nc_cur_term", &lib) { + rsconf::enable_cfg("_nc_cur_term"); + if target.has_symbol("cur_term", &lib) { + rsconf::warn!("curses provides both cur_term and _nc_cur_term"); + } + break; } } } diff --git a/docker/opensuse-tumbleweed.docker b/docker/opensuse-tumbleweed.docker new file mode 100644 index 000000000..05b6b11c3 --- /dev/null +++ b/docker/opensuse-tumbleweed.docker @@ -0,0 +1,39 @@ +FROM opensuse/tumbleweed:latest +LABEL org.opencontainers.image.source=https://github.com/fish-shell/fish-shell + +ENV LANG C.UTF-8 +ENV LC_ALL C.UTF-8 + +RUN zypper --non-interactive install \ + bash \ + cmake \ + diffutils \ + gcc-c++ \ + git-core \ + ncurses-devel \ + ninja \ + pcre2-devel \ + python311 \ + python311-pip \ + python311-pexpect \ + openssl \ + procps \ + tmux \ + sudo \ + rust \ + cargo + +RUN usermod -p $(openssl passwd -1 fish) root + +RUN groupadd -g 1000 fishuser \ + && useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \ + && mkdir -p /home/fishuser/fish-build \ + && mkdir /fish-source \ + && chown -R fishuser:fishuser /home/fishuser /fish-source + +USER fishuser +WORKDIR /home/fishuser + +COPY fish_run_tests.sh / + +CMD /fish_run_tests.sh diff --git a/src/curses.rs b/src/curses.rs index e89c31e6d..7bbc13ec9 100644 --- a/src/curses.rs +++ b/src/curses.rs @@ -62,8 +62,10 @@ mod sys { pub type putc_t = extern "C" fn(tputs_arg) -> libc::c_int; extern "C" { - /// The ncurses `cur_term` TERMINAL pointer. + #[cfg(not(have_nc_cur_term))] pub static mut cur_term: *const core::ffi::c_void; + #[cfg(have_nc_cur_term)] + pub fn have_nc_cur_term() -> *const core::ffi::c_void; /// setupterm(3) is a low-level call to begin doing any sort of `term.h`/`curses.h` work. /// It's called internally by ncurses's `initscr()` and `newterm()`, but the C++ code called @@ -77,6 +79,9 @@ pub fn setupterm( /// Frees the `cur_term` TERMINAL pointer. pub fn del_curterm(term: *const core::ffi::c_void) -> libc::c_int; + /// Sets the `cur_term` TERMINAL pointer. + pub fn set_curterm(term: *const core::ffi::c_void) -> *const core::ffi::c_void; + /// Checks for the presence of a termcap flag identified by the first two characters of /// `id`. pub fn tgetflag(id: *const libc::c_char) -> libc::c_int; @@ -95,6 +100,19 @@ pub fn tgetstr( pub fn tputs(str: *const libc::c_char, affcnt: libc::c_int, putc: putc_t) -> libc::c_int; } } + +/// The ncurses `cur_term` TERMINAL pointer. +fn get_curterm() -> *const core::ffi::c_void { + #[cfg(have_nc_cur_term)] + unsafe { + sys::have_nc_cur_term() + } + #[cfg(not(have_nc_cur_term))] + unsafe { + sys::cur_term + } +} + pub use sys::tputs_arg as TputsArg; /// The safe wrapper around curses functionality, initialized by a successful call to [`setup()`] @@ -434,7 +452,7 @@ pub fn setup(term: Option<&CStr>, fd: i32, configure: F) -> Option> let result = unsafe { // If cur_term is already initialized for a different $TERM value, calling setupterm() again // will leak memory. Call del_curterm() first to free previously allocated resources. - let _ = sys::del_curterm(cur_term); + let _ = sys::del_curterm(get_curterm()); let mut err = 0; if let Some(term) = term { @@ -469,8 +487,8 @@ pub fn reset() { unsafe { // Ignore the result of del_curterm() as the only documented error is that // `cur_term` was already null. - let _ = sys::del_curterm(cur_term); - sys::cur_term = core::ptr::null(); + let _ = sys::del_curterm(get_curterm()); + let _ = sys::set_curterm(core::ptr::null()); } *term = None; }