Allow ctrl-C to work in fish_indent builtin

Since fish_indent became a builtin, it cannot be canceled with control-C,
because Rust's `read_to_end` retries on EINTR. Add our own function which
propagates EINTR and use it.

Fixes #12238
This commit is contained in:
Peter Ammon
2025-12-30 10:31:08 -08:00
parent 8d5f5586dc
commit 2fa8c8cd7f
3 changed files with 43 additions and 4 deletions

View File

@@ -11,7 +11,7 @@
use super::prelude::*;
use crate::ast::{self, AsNode, Ast, Kind, Leaf, Node, NodeVisitor, SourceRangeList, Traversal};
use crate::common::{
PROGRAM_NAME, UnescapeFlags, UnescapeStringStyle, bytes2wcstring, get_program_name,
PROGRAM_NAME, ReadExt, UnescapeFlags, UnescapeStringStyle, bytes2wcstring, get_program_name,
unescape_string, wcs2bytes,
};
use crate::env::EnvStack;
@@ -1045,12 +1045,16 @@ enum OutputType {
use std::os::fd::FromRawFd;
let mut fd = unsafe { std::fs::File::from_raw_fd(streams.stdin_fd) };
let mut buf = vec![];
match fd.read_to_end(&mut buf) {
match fd.read_to_end_interruptible(&mut buf) {
Ok(_) => {}
Err(_) => {
Err(err) => {
// Don't close the fd
std::mem::forget(fd);
return Err(STATUS_CMD_ERROR);
return if err.kind() == std::io::ErrorKind::Interrupted {
Err(128 + libc::SIGINT)
} else {
Err(STATUS_CMD_ERROR)
};
}
}
std::mem::forget(fd);

View File

@@ -24,6 +24,7 @@
use std::cell::{Cell, RefCell};
use std::env;
use std::ffi::{CStr, CString, OsString};
use std::io::Read;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::unix::prelude::*;
@@ -1237,6 +1238,23 @@ pub fn read_blocked(fd: RawFd, buf: &mut [u8]) -> nix::Result<usize> {
}
}
pub trait ReadExt {
/// Like [`read_to_end`], but does not retry on EINTR.
fn read_to_end_interruptible(&mut self, buf: &mut Vec<u8>) -> std::io::Result<()>;
}
impl<T: Read + ?Sized> ReadExt for T {
fn read_to_end_interruptible(&mut self, buf: &mut Vec<u8>) -> std::io::Result<()> {
let mut chunk = [0_u8; 4096];
loop {
match self.read(&mut chunk)? {
0 => return Ok(()),
n => buf.extend_from_slice(&chunk[..n]),
}
}
}
}
/// Test if the string is a valid function name.
pub fn valid_func_name(name: &wstr) -> bool {
!(name.is_empty()

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env python3
from pexpect_helper import SpawnedProc
sp = SpawnedProc(timeout=2)
send, sendline, sleep, expect_prompt = (
sp.send,
sp.sendline,
sp.sleep,
sp.expect_prompt,
)
# Verify that the fish_indent builtin can be interrupted with ctrl-c.
expect_prompt()
sendline("fish_indent")
sleep(0.1)
send("\x03") # ctrl-c
expect_prompt()