diff --git a/build/fish/build.sh b/build/fish/build.sh new file mode 100755 index 000000000..2345caa51 --- /dev/null +++ b/build/fish/build.sh @@ -0,0 +1,100 @@ +#!/usr/bin/bash +# +# {{{ CDDL HEADER +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# }}} + +# Copyright 2026 OmniOS Community Edition (OmniOSce) Association. + +. ../../lib/build.sh + +PROG=fish +VER=4.5.0 +PKG=ooce/shell/fish +SUMMARY="Fish is a smart and user-friendly command line shell" +DESC="friendly interactive shell" + +SKIP_SSP_CHECK=1 +SKIP_LICENCES=COPYING + +BUILD_DEPENDS_IPS=" + ooce/developer/rust + ooce/developer/cmake + library/pcre2 + runtime/python-$PYTHONPKGVER +" + +RUN_DEPENDS_IPS=" + library/pcre2 + runtime/python-$PYTHONPKGVER +" + +XFORM_ARGS+=" -DPREFIX=${PREFIX#/}" + +set_arch 64 +set_clangver + +CONFIGURE_OPTS+=" + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_INSTALL_PREFIX=$PREFIX + -DCMAKE_VERBOSE_MAKEFILE=1 + -DCMAKE_INSTALL_SYSCONFDIR="/etc" + -DSYS_PCRE2_INCLUDE_DIR="/usr/include/pcre" + -DFISH_USE_SYSTEM_PCRE2=ON + -DWITH_MESSAGE_LOCALIZATION=ON + -DWITH_DOCS=OFF +" + +# use GNU msgfmt; otherwise the build fails +PATH="$GNUBIN:$PATH:$OOCEBIN" + +crate_patch() { + logmsg "Fetching crates" + logcmd $CARGO fetch --manifest-path $TMPDIR/$BUILDDIR/Cargo.toml \ + || logerr "Fetching crates failed" + logmsg "Patching crates" + pushd $CARGO_HOME >/dev/null + for patchfile in $SRCDIR/patches-crate/*.patch; do \ + gpatch --backup --version-control=numbered -p0 < $patchfile ; \ + done ; + popd >/dev/null +} + +pre_configure() { + typeset arch=$1 + + CONFIGURE_OPTS[$arch]=" + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_INSTALL_PREFIX=$PREFIX + -DCMAKE_VERBOSE_MAKEFILE=1 + -DCMAKE_INSTALL_SYSCONFDIR="/etc" + -DSYS_PCRE2_INCLUDE_DIR="/usr/include/pcre" + -DFISH_USE_SYSTEM_PCRE2=ON + -DWITH_GETTEXT=ON + -DBUILD_DOCS=OFF + " +} + +init +download_source $PROG fish-$VER "" +patch_source +export CARGO_HOME=$TMPDIR/cargo_crates +mkdir -p $CARGO_HOME +crate_patch +prep_build cmake +build -noctf +#run_testsuite check +strip_install +make_package +clean_up + +# Vim hints +# vim:ts=4:sw=4:et:fdm=marker diff --git a/build/fish/local.mog b/build/fish/local.mog new file mode 100644 index 000000000..fb1cfe141 --- /dev/null +++ b/build/fish/local.mog @@ -0,0 +1,16 @@ +# {{{ CDDL HEADER +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# }}} + +# Copyright 2026 OmniOS Community Edition (OmniOSce) Association. + +license COPYING license=GPLv2 + diff --git a/build/fish/patches-crate/01-libc.patch b/build/fish/patches-crate/01-libc.patch new file mode 100644 index 000000000..1b276b134 --- /dev/null +++ b/build/fish/patches-crate/01-libc.patch @@ -0,0 +1,20 @@ +--- registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.180/src/unix/solarish/mod.rs.~1~ 2006-07-23 21:21:28.000000000 -0400 ++++ registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.180/src/unix/solarish/mod.rs 2025-11-11 09:06:45.275025555 -050 +@@ -2390,6 +2390,9 @@ + // sys/sendfile.h + pub const SFV_FD_SELF: c_int = -2; + ++// command names for confstr sys/unistd.h ++pub const _CS_PATH: c_int = 65; ++ + const fn _CMSG_HDR_ALIGN(p: usize) -> usize { + (p + _CMSG_HDR_ALIGNMENT - 1) & !(_CMSG_HDR_ALIGNMENT - 1) + } +@@ -2756,6 +2759,7 @@ + addrlen: *mut crate::socklen_t, + ) -> ssize_t; + pub fn mkstemps(template: *mut c_char, suffixlen: c_int) -> c_int; ++ pub fn mkostemp(template: *mut c_char, flags: c_int) -> c_int; + pub fn futimesat(fd: c_int, path: *const c_char, times: *const crate::timeval) -> c_int; + pub fn futimens(dirfd: c_int, times: *const crate::timespec) -> c_int; + pub fn utimensat( diff --git a/build/fish/patches/01-revert-termios-use-nix-wrapper-bb7dce941acf51b11ee49994f799ba90911872ac.patch b/build/fish/patches/01-revert-termios-use-nix-wrapper-bb7dce941acf51b11ee49994f799ba90911872ac.patch new file mode 100644 index 000000000..714014155 --- /dev/null +++ b/build/fish/patches/01-revert-termios-use-nix-wrapper-bb7dce941acf51b11ee49994f799ba90911872ac.patch @@ -0,0 +1,525 @@ +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fg.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fg.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fg.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fg.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -6,9 +6,7 @@ + use crate::tokenizer::tok_command; + use crate::wutil::perror; + use crate::{env::EnvMode, tty_handoff::TtyHandoff}; +-use libc::STDIN_FILENO; +-use nix::sys::termios::{self, tcsetattr}; +-use std::os::fd::BorrowedFd; ++use libc::{STDIN_FILENO, TCSADRAIN}; + + use super::prelude::*; + +@@ -144,14 +142,9 @@ + } + let tmodes = job_group.tmodes.borrow(); + if job_group.wants_terminal() && tmodes.is_some() { +- let tmodes = tmodes.as_ref().unwrap(); +- if tcsetattr( +- unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) }, +- termios::SetArg::TCSADRAIN, +- tmodes, +- ) +- .is_err() +- { ++ let termios = tmodes.as_ref().unwrap(); ++ let res = unsafe { libc::tcsetattr(STDIN_FILENO, TCSADRAIN, termios) }; ++ if res < 0 { + perror("tcsetattr"); + } + } +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fish_key_reader.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fish_key_reader.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fish_key_reader.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fish_key_reader.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -47,7 +47,7 @@ + + for evt in [VINTR, VEOF] { + let modes = shell_modes(); +- let cc = Key::from_single_byte(modes.control_chars[evt]); ++ let cc = Key::from_single_byte(modes.c_cc[evt]); + + if match_key_event_to_key(&key_evt, &cc).is_some() { + if recent_keys +@@ -61,7 +61,7 @@ + } + streams.err.appendln(&wgettext_fmt!( + "Press ctrl-%c again to exit", +- char::from(modes.control_chars[evt] + 0x60) ++ char::from(modes.c_cc[evt] + 0x60) + )); + return false; + } +@@ -162,8 +162,8 @@ + let modes = shell_modes(); + streams.err.appendln(&wgettext_fmt!( + "or press ctrl-%c or ctrl-%c twice in a row.", +- char::from(modes.control_chars[VINTR] + 0x60), +- char::from(modes.control_chars[VEOF] + 0x60) ++ char::from(modes.c_cc[VINTR] + 0x60), ++ char::from(modes.c_cc[VEOF] + 0x60) + )); + streams.err.appendln(L!("\n")); + } +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/common.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/common.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/common.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/common.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -19,7 +19,6 @@ + use fish_widestring::{ + ENCODE_DIRECT_END, decode_byte_from_char, encode_byte_to_char, subslice_position, + }; +-use nix::sys::termios::Termios; + use std::env; + use std::ffi::{CStr, CString, OsStr, OsString}; + use std::os::unix::prelude::*; +@@ -885,7 +884,7 @@ + Some(in_pos) + } + +-pub fn shell_modes() -> MutexGuard<'static, Termios> { ++pub fn shell_modes() -> MutexGuard<'static, libc::termios> { + crate::reader::SHELL_MODES.lock().unwrap() + } + +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/input_common.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/input_common.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/input_common.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/input_common.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -878,7 +878,7 @@ + self.push_back(evt); + } + }); +- let vintr = shell_modes().control_chars[libc::VINTR]; ++ let vintr = shell_modes().c_cc[libc::VINTR]; + if vintr != 0 + && key.is_some_and(|key| { + match_key_event_to_key(&key, &Key::from_single_byte(vintr)) +@@ -1619,7 +1619,7 @@ + fn select_interrupted(&mut self) {} + + fn enqueue_interrupt_key(&mut self) { +- let vintr = shell_modes().control_chars[libc::VINTR]; ++ let vintr = shell_modes().c_cc[libc::VINTR]; + if vintr != 0 { + let interrupt_evt = CharEvent::from_key(KeyEvent::from_single_byte(vintr)); + if stop_query(self.blocking_query()) { +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/job_group.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/job_group.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/job_group.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/job_group.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -2,7 +2,6 @@ + use crate::prelude::*; + use crate::proc::{JobGroupRef, Pid}; + use crate::signal::Signal; +-use nix::sys::termios::Termios; + use std::cell::RefCell; + use std::num::NonZeroU32; + use std::sync::atomic::{AtomicI32, Ordering}; +@@ -61,7 +60,7 @@ + pub struct JobGroup { + /// If set, the saved terminal modes of this job. This needs to be saved so that we can restore + /// the terminal to the same state when resuming a stopped job. +- pub tmodes: RefCell>, ++ pub tmodes: RefCell>, + /// Whether job control is enabled in this `JobGroup` or not. + /// + /// If this is set, then the first process in the root job must be external, as it will become +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/reader/reader.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/reader/reader.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/reader/reader.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/reader/reader.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -127,10 +127,10 @@ + }; + use fish_wcstringutil::{IsPrefix, is_prefix}; + use libc::{ +- _POSIX_VDISABLE, EIO, EISDIR, ENOTTY, EPERM, ESRCH, O_NONBLOCK, O_RDONLY, SIGINT, +- STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, VMIN, VQUIT, VSUSP, VTIME, c_char, ++ _POSIX_VDISABLE, ECHO, EINTR, EIO, EISDIR, ENOTTY, EPERM, ESRCH, FLUSHO, ICANON, ICRNL, IEXTEN, ++ INLCR, IXOFF, IXON, O_NONBLOCK, O_RDONLY, ONLCR, OPOST, SIGINT, STDERR_FILENO, STDIN_FILENO, ++ STDOUT_FILENO, TCSANOW, VMIN, VQUIT, VSUSP, VTIME, c_char, + }; +-use nix::sys::termios::{self, SetArg, Termios, tcgetattr, tcsetattr}; + use nix::{ + fcntl::OFlag, + sys::{ +@@ -144,6 +144,7 @@ + cell::UnsafeCell, + cmp, + io::BufReader, ++ mem::MaybeUninit, + num::NonZeroUsize, + ops::{ControlFlow, Range}, + os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}, +@@ -168,20 +169,16 @@ + + static EXIT_STATE: AtomicU8 = AtomicU8::new(ExitState::None as u8); + +-fn zeroed_termios() -> Termios { +- let termios: libc::termios = unsafe { std::mem::zeroed() }; +- termios.into() +-} +- +-pub static SHELL_MODES: LazyLock> = LazyLock::new(|| Mutex::new(zeroed_termios())); ++pub static SHELL_MODES: LazyLock> = ++ LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() })); + + /// The valid terminal modes on startup. + /// Warning: this is read from the SIGTERM handler! Hence the raw global. + static TERMINAL_MODE_ON_STARTUP: OnceLock = OnceLock::new(); + + /// Mode we use to execute programs. +-static TTY_MODES_FOR_EXTERNAL_CMDS: LazyLock> = +- LazyLock::new(|| Mutex::new(zeroed_termios())); ++static TTY_MODES_FOR_EXTERNAL_CMDS: LazyLock> = ++ LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() })); + + static RUN_COUNT: AtomicU64 = AtomicU64::new(0); + +@@ -226,11 +223,13 @@ + }; + let fd = devnull.as_raw_fd(); + for stdfd in [STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO] { +- if matches!( +- tcgetattr(unsafe { BorrowedFd::borrow_raw(stdfd) }), +- Err(nix::Error::EIO | nix::Error::ENOTTY) +- ) { +- unsafe { libc::dup2(fd, stdfd) }; ++ let mut t = std::mem::MaybeUninit::uninit(); ++ unsafe { ++ if libc::tcgetattr(stdfd, t.as_mut_ptr()) != 0 ++ && matches!(errno::errno().0, EIO | ENOTTY) ++ { ++ libc::dup2(fd, stdfd); ++ } + } + } + } +@@ -987,24 +986,18 @@ + } + } + +-const FLOW_CONTROL_FLAGS: termios::InputFlags = { +- use termios::InputFlags; +- InputFlags::IXON.union(InputFlags::IXOFF) +-}; ++const FLOW_CONTROL_FLAGS: libc::tcflag_t = IXON | IXOFF; + + /// Initialize the reader. + pub fn reader_init(will_restore_foreground_pgroup: bool) { +- let terminal_mode_on_startup = match tcgetattr(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) }) +- { +- Ok(modes) => { +- // Save the initial terminal mode. +- // Note this field is read by a signal handler, so do it atomically, with a leaked mode. +- // TODO: rationalize behavior if initial tcgetattr() fails. +- TERMINAL_MODE_ON_STARTUP.get_or_init(|| libc::termios::from(modes.clone())); +- modes +- } +- Err(_) => zeroed_termios(), +- }; ++ // Save the initial terminal mode. ++ // Note this field is read by a signal handler, so do it atomically, with a leaked mode. ++ let mut terminal_mode_on_startup = unsafe { std::mem::zeroed::() }; ++ let ret = unsafe { libc::tcgetattr(libc::STDIN_FILENO, &mut terminal_mode_on_startup) }; ++ // TODO: rationalize behavior if initial tcgetattr() fails. ++ if ret == 0 { ++ TERMINAL_MODE_ON_STARTUP.get_or_init(|| terminal_mode_on_startup); ++ } + + if !cfg!(test) { + assert!(AT_EXIT.get().is_none()); +@@ -1016,12 +1009,12 @@ + term_fix_external_modes(&mut external_modes); + + // Disable flow control by default. +- external_modes.input_flags &= !FLOW_CONTROL_FLAGS; ++ external_modes.c_iflag &= !FLOW_CONTROL_FLAGS; + + // Set the mode used for the terminal, initialized to the current mode. + { + let mut shell_modes = shell_modes(); +- *shell_modes = external_modes.clone(); ++ *shell_modes = external_modes; + term_fix_shell_modes(&mut shell_modes); + } + +@@ -1051,7 +1044,7 @@ + return; + } + if let Some(modes) = safe_get_terminal_mode_on_startup() { +- unsafe { libc::tcsetattr(STDIN_FILENO, libc::TCSANOW, modes) }; ++ unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, modes) }; + } + } + +@@ -1213,7 +1206,7 @@ + /// commandline. + pub fn reader_readline( + parser: &Parser, +- old_modes: Option, ++ old_modes: Option, + nchars: Option, + ) -> Option { + let data = current_data().unwrap(); +@@ -2542,7 +2535,7 @@ + /// Return the command, or none if we were asked to cancel (e.g. SIGHUP). + fn readline( + &mut self, +- old_modes: Option, ++ old_modes: Option, + nchars: Option, + ) -> Option { + let mut tty = TtyHandoff::new(reader_save_screen_state); +@@ -2640,12 +2633,7 @@ + // The order of the two conditions below is important. Try to restore the mode + // in all cases, but only complain if interactive. + if let Some(old_modes) = old_modes { +- if tcsetattr( +- unsafe { BorrowedFd::borrow_raw(self.conf.inputfd) }, +- SetArg::TCSANOW, +- &old_modes, +- ) +- .is_err() ++ if unsafe { libc::tcsetattr(self.conf.inputfd, TCSANOW, &old_modes) } == -1 + && is_interactive_session() + { + perror("tcsetattr"); +@@ -4714,39 +4702,32 @@ + + // Turning off OPOST or ONLCR breaks output (staircase effect), we don't allow it. + // See #7133. +-fn term_fix_oflag(modes: &mut Termios) { +- modes.output_flags |= { +- use termios::OutputFlags; ++fn term_fix_oflag(modes: &mut libc::termios) { ++ modes.c_oflag |= { + // turn on "implementation-defined post processing" - this often changes how line breaks work. +- OutputFlags::OPOST ++ OPOST + // "translate newline to carriage return-newline" - without you see staircase output. +- | OutputFlags::ONLCR ++ | ONLCR + }; + } + + /// Restore terminal settings we care about, to prevent a broken shell. +-fn term_fix_shell_modes(modes: &mut Termios) { +- modes.input_flags &= { +- use termios::InputFlags; ++fn term_fix_shell_modes(modes: &mut libc::termios) { ++ modes.c_iflag &= { + // disable mapping CR (\cM) to NL (\cJ) +- !InputFlags::ICRNL ++ !ICRNL + // disable mapping NL (\cJ) to CR (\cM) +- & !InputFlags::INLCR ++ & !INLCR + }; +- modes.local_flags &= { +- use termios::LocalFlags; +- let echo = LocalFlags::ECHO; +- let flusho = LocalFlags::FLUSHO; +- let icanon = LocalFlags::ICANON; +- let iexten = LocalFlags::IEXTEN; +- !echo +- & !icanon +- & !iexten // turn off handling of discard and lnext characters +- & !flusho ++ modes.c_lflag &= { ++ !ECHO ++ & !ICANON ++ & !IEXTEN // turn off handling of discard and lnext characters ++ & !FLUSHO + }; + term_fix_oflag(modes); + +- let c_cc = &mut modes.control_chars; ++ let c_cc = &mut modes.c_cc; + c_cc[VMIN] = 1; + c_cc[VTIME] = 0; + +@@ -4760,76 +4741,57 @@ + c_cc[VQUIT] = disabling_char; + } + +-fn term_fix_external_modes(modes: &mut Termios) { ++fn term_fix_external_modes(modes: &mut libc::termios) { + term_fix_oflag(modes); + // These cause other ridiculous behaviors like input not being shown. +- modes.local_flags = { +- use termios::LocalFlags; +- let echo = LocalFlags::ECHO; +- let flusho = LocalFlags::FLUSHO; +- let icanon = LocalFlags::ICANON; +- let iexten = LocalFlags::IEXTEN; +- (modes.local_flags | echo | icanon | iexten) & !flusho +- }; +- modes.input_flags = { +- use termios::InputFlags; +- let icrnl = InputFlags::ICRNL; +- let inlcr = InputFlags::INLCR; +- (modes.input_flags | icrnl) & !inlcr +- }; ++ modes.c_lflag = (modes.c_lflag | ECHO | ICANON | IEXTEN) & !FLUSHO; ++ modes.c_iflag = (modes.c_iflag | ICRNL) & !INLCR; + } + + /// Give up control of terminal. + fn term_donate(quiet: bool /* = false */) { +- loop { +- match tcsetattr( +- unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) }, +- SetArg::TCSANOW, +- &TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap(), +- ) { +- Ok(_) => (), +- Err(nix::Error::EINTR) => continue, +- Err(_) => { +- if !quiet { +- flog!( +- warning, +- wgettext!("Could not set terminal mode for new job") +- ); +- perror("tcsetattr"); +- } +- break; ++ while unsafe { ++ libc::tcsetattr( ++ STDIN_FILENO, ++ TCSANOW, ++ &*TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap(), ++ ) ++ } == -1 ++ { ++ if errno().0 != EINTR { ++ if !quiet { ++ flog!( ++ warning, ++ wgettext!("Could not set terminal mode for new job") ++ ); ++ perror("tcsetattr"); + } ++ break; + } +- break; + } + } + + /// Copy the (potentially changed) terminal modes and use them from now on. + pub fn term_copy_modes() { +- let mut external_modes = tcgetattr(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) }) +- .unwrap_or_else(|_| zeroed_termios()); ++ let mut modes = MaybeUninit::uninit(); ++ unsafe { libc::tcgetattr(STDIN_FILENO, modes.as_mut_ptr()) }; ++ let mut external_modes = unsafe { modes.assume_init() }; + // We still want to fix most egregious breakage. + // E.g. OPOST is *not* something that should be set globally, + // and 99% triggered by a crashed program. + term_fix_external_modes(&mut external_modes); +- let external_flow_control = external_modes.input_flags & FLOW_CONTROL_FLAGS; + *TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap() = external_modes; + + let mut shell_modes = shell_modes(); +- shell_modes.input_flags = +- (shell_modes.input_flags & !FLOW_CONTROL_FLAGS) | external_flow_control; ++ shell_modes.c_iflag = ++ (shell_modes.c_iflag & !FLOW_CONTROL_FLAGS) | (external_modes.c_iflag & FLOW_CONTROL_FLAGS); + } + + pub fn set_shell_modes(fd: RawFd, whence: &str) -> bool { + let ok = loop { +- match tcsetattr( +- unsafe { BorrowedFd::borrow_raw(fd) }, +- SetArg::TCSANOW, +- &shell_modes(), +- ) { +- Ok(_) => break true, +- Err(nix::Error::EINTR) => continue, +- Err(_) => break false, ++ let ok = unsafe { libc::tcsetattr(fd, TCSANOW, &*shell_modes()) } != -1; ++ if ok || errno().0 != EINTR { ++ break ok; + } + }; + if !ok { +@@ -4842,14 +4804,18 @@ + ok + } + +-pub fn set_shell_modes_temporarily(inputfd: RawFd) -> Option { ++pub fn set_shell_modes_temporarily(inputfd: RawFd) -> Option { + // It may happen that a command we ran when job control was disabled nevertheless stole the tty + // from us. In that case when we read from our fd, it will trigger SIGTTIN. So just + // unconditionally reclaim the tty. See #9181. + unsafe { libc::tcsetpgrp(inputfd, libc::getpgrp()) }; + + // Get the current terminal modes. These will be restored when the function returns. +- let old_modes = tcgetattr(unsafe { BorrowedFd::borrow_raw(inputfd) }).ok(); ++ let old_modes = { ++ let mut old_modes = MaybeUninit::uninit(); ++ let ok = unsafe { libc::tcgetattr(inputfd, old_modes.as_mut_ptr()) } == 0; ++ ok.then(|| unsafe { old_modes.assume_init() }) ++ }; + + // Set the new modes. + set_shell_modes(inputfd, "readline"); +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/screen.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/screen.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/screen.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/screen.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -19,8 +19,7 @@ + use std::sync::atomic::AtomicU32; + use std::time::SystemTime; + +-use libc::{STDERR_FILENO, STDOUT_FILENO}; +-use nix::sys::termios; ++use libc::{ONLCR, STDERR_FILENO, STDOUT_FILENO}; + + use crate::common::{ + get_ellipsis_char, get_omitted_newline_str, has_working_tty_timestamps, shell_modes, wcs2bytes, +@@ -891,9 +891,7 @@ + let s = if y_steps < 0 { + Some(CursorUp) + } else if y_steps > 0 { +- if shell_modes() +- .output_flags +- .contains(termios::OutputFlags::ONLCR) ++ if (shell_modes().c_oflag & ONLCR) != 0 + { + // See GitHub issue #4505. + // Most consoles use a simple newline as the cursor down escape. +diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/tty_handoff.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/tty_handoff.rs +--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/tty_handoff.rs 2026-02-01 04:13:13.000000000 -0500 ++++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/tty_handoff.rs 2026-02-01 03:58:51.000000000 -0500 +@@ -20,9 +20,8 @@ + use crate::wutil::{perror, wcstoi}; + use fish_widestring::ToWString; + use libc::{EINVAL, ENOTTY, EPERM, STDIN_FILENO, WNOHANG}; +-use nix::sys::termios::tcgetattr; + use nix::unistd::getpgrp; +-use std::os::fd::BorrowedFd; ++use std::mem::MaybeUninit; + use std::sync::{ + OnceLock, + atomic::{AtomicBool, AtomicPtr, Ordering}, +@@ -409,17 +408,12 @@ + + /// Save the current tty modes into the owning job group, if we are transferred. + pub fn save_tty_modes(&mut self) { +- let Some(ref mut owner) = self.owner else { +- return; +- }; +- match tcgetattr(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) }) { +- Ok(modes) => { +- owner.tmodes.replace(Some(modes)); +- } +- Err(err) => { +- if err != nix::Error::ENOTTY { +- perror("tcgetattr"); +- } ++ if let Some(ref mut owner) = self.owner { ++ let mut tmodes = MaybeUninit::uninit(); ++ if unsafe { libc::tcgetattr(STDIN_FILENO, tmodes.as_mut_ptr()) } == 0 { ++ owner.tmodes.replace(Some(unsafe { tmodes.assume_init() })); ++ } else if errno::errno().0 != ENOTTY { ++ perror("tcgetattr"); + } + } + } diff --git a/build/fish/patches/04-use-gcc-test.patch b/build/fish/patches/04-use-gcc-test.patch new file mode 100644 index 000000000..5d431cd51 --- /dev/null +++ b/build/fish/patches/04-use-gcc-test.patch @@ -0,0 +1,11 @@ +--- fish-4.1.2/tests/test_driver.py.old 2025-10-10 08:36:52.795200178 -0400 ++++ fish-4.1.2/tests/test_driver.py 2025-10-10 08:37:08.394307944 -0400 +@@ -96,7 +96,7 @@ + def compile_test_helper(source_path: Path, binary_path: Path) -> None: + subprocess.run( + [ +- "cc", ++ "gcc", + source_path, + "-o", + binary_path, diff --git a/build/fish/patches/series b/build/fish/patches/series new file mode 100644 index 000000000..1f1638f20 --- /dev/null +++ b/build/fish/patches/series @@ -0,0 +1,2 @@ +01-revert-termios-use-nix-wrapper-bb7dce941acf51b11ee49994f799ba90911872ac.patch +04-use-gcc-test.patch diff --git a/doc/baseline b/doc/baseline index 90d8fae26..44766e2e7 100644 --- a/doc/baseline +++ b/doc/baseline @@ -269,6 +269,7 @@ extra.omnios ooce/server/nginx extra.omnios ooce/server/nginx-128 extra.omnios ooce/server/nginx-common extra.omnios ooce/server/webservd-common +extra.omnios.ooce/shell/fish extra.omnios ooce/storage/minio extra.omnios ooce/storage/minio-mc extra.omnios ooce/system/clamav diff --git a/doc/packages.md b/doc/packages.md index fb7201ff3..71d36ad4d 100644 --- a/doc/packages.md +++ b/doc/packages.md @@ -221,6 +221,7 @@ | ooce/server/haproxy | 3.2.10 | https://www.haproxy.org/ | [omniosorg](https://github.com/omniosorg) | ooce/server/nginx | 1.29.4 | https://nginx.org/en/download.html | [omniosorg](https://github.com/omniosorg) | ooce/server/nginx-128 | 1.28.1 | https://nginx.org/en/download.html | [omniosorg](https://github.com/omniosorg) +| ooce/shell/fish | 4.5.0 | https://github.com/fish-shell/fish-shell | [omniosorg](http://github.com/omniosorg) | ooce/storage/minio | 2025-10-15T17-29-55Z | https://github.com/minio/minio/releases | [omniosorg](https://github.com/omniosorg) | ooce/storage/minio-mc | 2025-08-13T08-35-41Z | https://github.com/minio/mc/releases | [omniosorg](https://github.com/omniosorg) | ooce/system/file-system/ntfs-3g | 2022.10.3 | https://github.com/tuxera/ntfs-3g/releases | [omniosorg](https://github.com/omniosorg)