supercode-cli 0.4.17

supercode — a lightweight, fully-customizable AI coding agent CLI in Rust. Any model via OpenRouter; natively continues Claude Code and Codex sessions.
//! Exact host-terminal state capture around stock frontend processes.

use std::io;

/// Snapshot of terminal flags inherited by a child frontend. Dropping the
/// snapshot is idempotent best-effort restoration; callers can restore
/// eagerly to surface failures before returning.
pub(crate) struct TerminalSnapshot {
    #[cfg(unix)]
    states: Vec<(libc::c_int, libc::termios)>,
    #[cfg(windows)]
    states: Vec<(windows_sys::Win32::Foundation::HANDLE, u32)>,
    restored: bool,
}

impl TerminalSnapshot {
    pub(crate) fn capture() -> io::Result<Self> {
        #[cfg(unix)]
        {
            Self::capture_unix(&[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO])
        }
        #[cfg(windows)]
        {
            use windows_sys::Win32::System::Console::{GetConsoleMode, GetStdHandle};
            use windows_sys::Win32::System::Console::{
                STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
            };
            let mut states = Vec::new();
            for id in [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE] {
                let handle = unsafe { GetStdHandle(id) };
                let mut mode = 0;
                if !handle.is_null() && unsafe { GetConsoleMode(handle, &mut mode) } != 0 {
                    states.push((handle, mode));
                }
            }
            Ok(Self {
                states,
                restored: false,
            })
        }
        #[cfg(not(any(unix, windows)))]
        {
            Ok(Self { restored: false })
        }
    }

    #[cfg(unix)]
    fn capture_unix(fds: &[libc::c_int]) -> io::Result<Self> {
        let mut states = Vec::new();
        for &fd in fds {
            if unsafe { libc::isatty(fd) } != 1 {
                continue;
            }
            let mut state = unsafe { std::mem::zeroed::<libc::termios>() };
            if unsafe { libc::tcgetattr(fd, &mut state) } != 0 {
                return Err(io::Error::last_os_error());
            }
            states.push((fd, state));
        }
        Ok(Self {
            states,
            restored: false,
        })
    }

    /// Restore the byte-for-byte terminal flags captured before launch.
    pub(crate) fn restore(&mut self) -> io::Result<()> {
        if self.restored {
            return Ok(());
        }
        #[cfg(unix)]
        for (fd, state) in &self.states {
            if unsafe { libc::tcsetattr(*fd, libc::TCSANOW, state) } != 0 {
                return Err(io::Error::last_os_error());
            }
        }
        #[cfg(windows)]
        for (handle, mode) in &self.states {
            if unsafe { windows_sys::Win32::System::Console::SetConsoleMode(*handle, *mode) } == 0 {
                return Err(io::Error::last_os_error());
            }
        }
        self.restored = true;
        Ok(())
    }

    /// A crashed or killed client cannot emit its normal screen-mode cleanup.
    /// Reset only modes that stock fullscreen clients enable, then restore the
    /// exact kernel terminal flags captured by this host.
    pub(crate) fn restore_after_abnormal_exit(&mut self) -> io::Result<()> {
        use std::io::Write;
        if !self.restored {
            let mut output = std::io::stdout().lock();
            output.write_all(b"\x1b[?25h\x1b[?1049l\x1b[?2004l\x1b[?1004l")?;
            output.flush()?;
        }
        self.restore()
    }
}

impl Drop for TerminalSnapshot {
    fn drop(&mut self) {
        let _ = self.restore();
    }
}

/// Give an isolated stock-client process group foreground access to the
/// inherited terminal, then return foreground ownership to the invoking host
/// before it restores modes or prints diagnostics.
pub(crate) struct ForegroundProcessGroup {
    #[cfg(unix)]
    fd: libc::c_int,
    #[cfg(unix)]
    original_group: libc::pid_t,
    #[cfg(unix)]
    original_sigttou: libc::sighandler_t,
    active: bool,
}

impl ForegroundProcessGroup {
    pub(crate) fn transfer_to(child_pid: Option<u32>) -> io::Result<Self> {
        #[cfg(unix)]
        {
            let fd = libc::STDIN_FILENO;
            let Some(child_pid) = child_pid else {
                return Ok(Self {
                    fd,
                    original_group: 0,
                    original_sigttou: libc::SIG_DFL,
                    active: false,
                });
            };
            if unsafe { libc::isatty(fd) } != 1 {
                return Ok(Self {
                    fd,
                    original_group: 0,
                    original_sigttou: libc::SIG_DFL,
                    active: false,
                });
            }
            let original_group = unsafe { libc::tcgetpgrp(fd) };
            if original_group < 0 {
                return Err(io::Error::last_os_error());
            }
            // A background process that restores itself would otherwise be
            // stopped by SIGTTOU. This host owns no concurrent terminal job.
            let original_sigttou = unsafe { libc::signal(libc::SIGTTOU, libc::SIG_IGN) };
            if unsafe { libc::tcsetpgrp(fd, child_pid as libc::pid_t) } != 0 {
                unsafe { libc::signal(libc::SIGTTOU, original_sigttou) };
                return Err(io::Error::last_os_error());
            }
            Ok(Self {
                fd,
                original_group,
                original_sigttou,
                active: true,
            })
        }
        #[cfg(not(unix))]
        {
            let _ = child_pid;
            Ok(Self { active: false })
        }
    }

    pub(crate) fn restore(&mut self) -> io::Result<()> {
        if !self.active {
            return Ok(());
        }
        #[cfg(unix)]
        {
            if unsafe { libc::tcsetpgrp(self.fd, self.original_group) } != 0 {
                return Err(io::Error::last_os_error());
            }
            unsafe { libc::signal(libc::SIGTTOU, self.original_sigttou) };
        }
        self.active = false;
        Ok(())
    }
}

impl Drop for ForegroundProcessGroup {
    fn drop(&mut self) {
        let _ = self.restore();
    }
}

#[cfg(all(test, unix))]
mod tests {
    use super::*;

    #[test]
    fn restores_real_pty_flags_exactly_after_child_style_mutation() {
        let mut master = -1;
        let mut slave = -1;
        let opened = unsafe {
            libc::openpty(
                &mut master,
                &mut slave,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                std::ptr::null_mut(),
            )
        };
        assert_eq!(opened, 0, "openpty: {}", io::Error::last_os_error());
        let mut before = unsafe { std::mem::zeroed::<libc::termios>() };
        assert_eq!(unsafe { libc::tcgetattr(slave, &mut before) }, 0);
        let mut snapshot = TerminalSnapshot::capture_unix(&[slave]).unwrap();
        let mut changed = before;
        changed.c_lflag ^= libc::ECHO;
        assert_eq!(
            unsafe { libc::tcsetattr(slave, libc::TCSANOW, &changed) },
            0
        );
        snapshot.restore().unwrap();
        let mut after = unsafe { std::mem::zeroed::<libc::termios>() };
        assert_eq!(unsafe { libc::tcgetattr(slave, &mut after) }, 0);
        assert_eq!(before.c_iflag, after.c_iflag);
        assert_eq!(before.c_oflag, after.c_oflag);
        assert_eq!(before.c_cflag, after.c_cflag);
        assert_eq!(before.c_lflag, after.c_lflag);
        assert_eq!(before.c_cc, after.c_cc);
        unsafe {
            libc::close(master);
            libc::close(slave);
        }
    }
}