From bd0d62c33be3b3ff41741950c61a6bb66d6b05e9 Mon Sep 17 00:00:00 2001 From: Javier Sagredo Date: Thu, 18 Jun 2026 00:02:07 +0200 Subject: [PATCH] Raise interrupt exception on Ctrl-C at the read prompt While rustyline holds the terminal in raw mode, Ctrl-C is delivered as `ReadlineError::Interrupted` rather than as a `SIGINT`, so the ctrlc handler never sets the `INTERRUPT` flag. `call_readline` also pushed Interrupted into the generic IO-error arm, which read_term then mis-classified as `end_of_file` so Ctrl-C at a `read/1` prompt unified `end_of_file` instead of interrupting. Handle `ReadlineError::Interrupted` explicitly: set `INTERRUPT` and return an Interrupted IO error. `read_term` now checks the flag in its error arm and raises the interrupt exception instead of falling through to the EOF or syntax-error handling. --- src/machine/machine_state.rs | 10 ++++++++++ src/read.rs | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 0bfb65a7..71cde903 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -865,6 +865,16 @@ impl MachineState { match self.read(stream, &indices.op_dir) { Ok(term_write_result) => return self.read_term_body(term_write_result), Err(err) => { + // A Ctrl-C at the read prompt surfaces as an error here (and, + // under rustyline's raw mode, would otherwise be mis-read as + // end_of_file). If the interrupt flag is set, raise the + // interrupt exception instead. + if crate::machine::INTERRUPT.swap(false, std::sync::atomic::Ordering::Relaxed) { + let interrupt_err = self.interrupt_error(); + let src = functor_stub(atom!("repl"), 0); + return Err(self.error_form(interrupt_err, src)); + } + match &err { CompilationError::ParserError(e) if e.is_unexpected_eof() => { match eof_handler(self, stream)? { diff --git a/src/read.rs b/src/read.rs index f0346ad2..759f5f1a 100644 --- a/src/read.rs +++ b/src/read.rs @@ -209,6 +209,15 @@ impl ReadlineStream { match self.rl.readline(get_prompt()) { Ok(text) => Ok(text), Err(ReadlineError::Eof) => Err(Error::from(ErrorKind::UnexpectedEof)), + Err(ReadlineError::Interrupted) => { + // While rustyline holds the terminal in raw mode, Ctrl-C is + // delivered as ReadlineError::Interrupted rather than as a + // SIGINT, so the ctrlc handler never sets INTERRUPT. Set it + // here so read_term raises the interrupt exception instead of + // mis-reading the error as end_of_file. + crate::machine::INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed); + Err(Error::from(ErrorKind::Interrupted)) + } Err(e) => Err(Error::new(ErrorKind::InvalidInput, e)), } }; -- 2.54.0