From 718f45492a4b2c31a67458c13c4cd4b3268703bc Mon Sep 17 00:00:00 2001 From: Rory Dudley Date: Thu, 29 Feb 2024 01:40:27 -0700 Subject: Fix handling of SIGINT Keep track of a new atomic variable: at_prompt, which is set to true just before blocking on io::stdin.read_line, and set to false just calling Poem::read. Additionally, for background tasks, there is a new ps macro called btask, which changes the process group of commands that are forked into the background, so that they don't receive SIGINT from the keyboard. --- src/main.rs | 21 +++++++++++++++++---- src/recite.rs | 5 +++-- src/recite/ps.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3cb2a74..04d58eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use recite::path::prefresh; use recite::Poem; use std::io::{self, Write}; use std::path::Path; +use std::sync::{Arc, Mutex}; /// Starts the main shell loop /// @@ -20,7 +21,7 @@ use std::path::Path; /// repl(&path, prompt); /// } /// ``` -fn repl(path: &Vec<&Path>, prompt: &str) { +fn repl(path: &Vec<&Path>, prompt: &str, at_prompt: &mut Arc>) { // Initial path refresh on startup let mut bins: Vec = prefresh(path); @@ -30,6 +31,9 @@ fn repl(path: &Vec<&Path>, prompt: &str) { print!("{}", prompt); io::stdout().flush().unwrap(); + // At the prompt + *at_prompt.lock().unwrap() = true; + // Wait for user input let mut poetry = String::new(); let bytes = io::stdin() @@ -50,6 +54,9 @@ fn repl(path: &Vec<&Path>, prompt: &str) { continue; } + // Not at the prompt + *at_prompt.lock().unwrap() = false; + // Parse a poem let poem = Poem::read(poetry); match poem { @@ -80,16 +87,22 @@ fn main() { // Set the prompt let prompt = "|> "; + let mut at_prompt = Arc::new(Mutex::new(false)); // Handle signals unsafe { + let at_prompt = Arc::clone(&at_prompt); signal_hook::low_level::register(signal_hook::consts::SIGINT, move || { - print!("\n{}", prompt); - io::stdout().flush().unwrap(); + if *at_prompt.lock().unwrap() { + print!("\n{}", prompt); + io::stdout().flush().unwrap(); + } else { + println!(); + } }) .unwrap(); }; // Begin evaluating commands - repl(&path, prompt); + repl(&path, prompt, &mut at_prompt); } diff --git a/src/recite.rs b/src/recite.rs index bd72b67..1aa1a62 100644 --- a/src/recite.rs +++ b/src/recite.rs @@ -1,10 +1,11 @@ pub mod path; mod ps; -use crate::{ctask, task}; +use crate::{btask, ctask, task}; use core::fmt; use libc::{waitpid, WNOHANG}; use path::prefresh; use std::io::{self, Write}; +use std::os::unix::process::CommandExt; use std::path::Path; use std::process::{exit, Command, Stdio}; use std::sync::{Arc, Mutex}; @@ -125,7 +126,7 @@ impl Meter { out: &mut String, pids: &mut Arc>>, ) -> Result { - let child = task!(verse, out); + let child = btask!(verse, out); println!("[&] {}", child.id()); pids.lock().unwrap().push(child.id() as i32); diff --git a/src/recite/ps.rs b/src/recite/ps.rs index d0e2435..bb18b3f 100644 --- a/src/recite/ps.rs +++ b/src/recite/ps.rs @@ -60,3 +60,36 @@ macro_rules! ctask { } }; } + +#[macro_export] +/// Fork into a background process from a Verse +/// +/// Figures out whether or not the given Verse is a couplet. If it is, fork +/// into a backgournd process, and pipe the contents of out `out` into STDIN. +/// If not, then simply fork into the background process. +/// +/// # Arguments +/// * `$verse: &Verse` - The verse to fork into +/// * `$out: &mut String` - If the $verse is a couplet, the contents of STDOUT from the last verse +macro_rules! btask { + ($verse:expr, $out:expr) => { + if $verse.couplet { + let mut child = Command::new($verse.verb()) + .args($verse.clause()) + .stdin(Stdio::piped()) + .process_group(0) + .spawn()?; + + let stdin = child.stdin.as_mut().ok_or(io::ErrorKind::BrokenPipe)?; + stdin.write_all(&$out.as_bytes())?; + $out.clear(); + + child + } else { + Command::new($verse.verb()) + .args($verse.clause()) + .process_group(0) + .spawn()? + } + }; +} -- cgit v1.2.3