use std::sync::atomic::{AtomicU8, Ordering};
const PLAIN: u8 = 0;
const LOGIN: u8 = 1;
const TUI: u8 = 2;
const SEEN: u8 = 3;
static STATE: AtomicU8 = AtomicU8::new(PLAIN);
#[cfg(any(unix, test))]
fn message(state: u8) -> Option<&'static [u8]> {
match state {
LOGIN => Some(b"\nlogin cancelled\n"),
_ => None,
}
}
#[cfg(unix)]
extern "C" fn on_sigint(_: libc::c_int) {
let st = STATE.load(Ordering::SeqCst);
if st == TUI || st == SEEN {
STATE.store(SEEN, Ordering::SeqCst);
return;
}
if let Some(m) = message(st) {
unsafe { libc::write(2, m.as_ptr().cast(), m.len()) };
}
unsafe { libc::_exit(0) };
}
pub fn install() {
#[cfg(unix)]
unsafe {
libc::signal(libc::SIGINT, on_sigint as *const () as usize);
}
}
pub fn login_phase() {
STATE.store(LOGIN, Ordering::SeqCst);
}
pub fn plain_phase() {
STATE.store(PLAIN, Ordering::SeqCst);
}
pub fn tui_phase() {
STATE.store(TUI, Ordering::SeqCst);
}
pub fn requested() -> bool {
STATE.load(Ordering::SeqCst) == SEEN
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_login_speaks() {
assert!(message(LOGIN).is_some_and(|m| m.starts_with(b"\nlogin")));
assert!(message(PLAIN).is_none());
}
#[cfg(unix)]
#[test]
fn tui_sigint_sets_flag_not_exit() {
tui_phase();
assert!(!requested());
on_sigint(2);
assert!(requested());
plain_phase();
}
}