diff --git a/src/builtins/fish_indent.rs b/src/builtins/fish_indent.rs index f8fccbfcc..9afc87f9b 100644 --- a/src/builtins/fish_indent.rs +++ b/src/builtins/fish_indent.rs @@ -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); diff --git a/src/common.rs b/src/common.rs index 6296138b9..f4649a927 100644 --- a/src/common.rs +++ b/src/common.rs @@ -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 { } } +pub trait ReadExt { + /// Like [`read_to_end`], but does not retry on EINTR. + fn read_to_end_interruptible(&mut self, buf: &mut Vec) -> std::io::Result<()>; +} + +impl ReadExt for T { + fn read_to_end_interruptible(&mut self, buf: &mut Vec) -> 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() diff --git a/tests/pexpects/fish_indent_interrupt.py b/tests/pexpects/fish_indent_interrupt.py new file mode 100644 index 000000000..f4bf29936 --- /dev/null +++ b/tests/pexpects/fish_indent_interrupt.py @@ -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()