summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRory Dudley2024-02-29 01:40:27 -0700
committerRory Dudley2024-02-29 01:40:27 -0700
commit718f45492a4b2c31a67458c13c4cd4b3268703bc (patch)
treed577bc14368319adac8bf24e0a757b5eccd24df8
parent2be80340afbc457f22f8c4cc441ef572b0acfda1 (diff)
downloaddwarvish-718f45492a4b2c31a67458c13c4cd4b3268703bc.tar.gz
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.
Notes
Notes: Changing the process group on the Command is done via CommandExt. More details here: https://doc.rust-lang.org/std/os/unix/process/trait.CommandExt.html#tymethod.process_group
-rw-r--r--src/main.rs21
-rw-r--r--src/recite.rs5
-rw-r--r--src/recite/ps.rs33
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<Mutex<bool>>) {
// Initial path refresh on startup
let mut bins: Vec<String> = 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<Mutex<Vec<i32>>>,
) -> Result<i32, io::Error> {
- 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()?
+ }
+ };
+}