Support linking against reentrant-configured curses

NCurses headers contain this conditional "#define cur_term":

	print  "#elif @cf_cv_enable_reentrant@"
	print  "NCURSES_WRAPPED_VAR(TERMINAL *, cur_term);"
	print  "#define cur_term   NCURSES_PUBLIC_VAR(cur_term())"
	print  "#else"

OpenSUSE Tumbleweed uses this configuration option; For reentrancy, cur_term
is a function.  If the NCurses autoconf variable @NCURSES_WRAP_PREFIX@
is not changed from its default, the function is called _nc_cur_term.

I'm not sure if we have a need to support non-default @NCURSES_WRAP_PREFIX@
but if we do there are various ways;
- search for the symbol with the cur_term suffix
- figure out the prefix based on the local curses installation,
  for example by looking at the header files.

Fixes #10243
This commit is contained in:
Johannes Altmanninger 2024-01-21 11:12:03 +01:00
parent 5dfcfa336b
commit 1234c77b15
3 changed files with 81 additions and 11 deletions

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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 @@ mod sys {
/// 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 @@ mod sys {
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 @@ where
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;
}