1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

sort: Flush BufWriter, don't panic on write errors

This commit is contained in:
Jan Verbeek 2025-03-31 13:13:23 +02:00
parent bfdde70309
commit d456e90596
4 changed files with 44 additions and 18 deletions

View file

@ -115,9 +115,9 @@ fn reader_writer<
}), }),
settings, settings,
output, output,
); )?;
} else { } else {
print_sorted(chunk.lines().iter(), settings, output); print_sorted(chunk.lines().iter(), settings, output)?;
} }
} }
ReadResult::SortedTwoChunks([a, b]) => { ReadResult::SortedTwoChunks([a, b]) => {
@ -138,9 +138,9 @@ fn reader_writer<
.map(|(line, _)| line), .map(|(line, _)| line),
settings, settings,
output, output,
); )?;
} else { } else {
print_sorted(merged_iter.map(|(line, _)| line), settings, output); print_sorted(merged_iter.map(|(line, _)| line), settings, output)?;
} }
} }
ReadResult::EmptyInput => { ReadResult::EmptyInput => {

View file

@ -25,7 +25,7 @@ use std::{
}; };
use compare::Compare; use compare::Compare;
use uucore::error::UResult; use uucore::error::{FromIo, UResult};
use crate::{ use crate::{
GlobalSettings, Output, SortError, GlobalSettings, Output, SortError,
@ -278,12 +278,19 @@ impl FileMerger<'_> {
} }
fn write_all_to(mut self, settings: &GlobalSettings, out: &mut impl Write) -> UResult<()> { fn write_all_to(mut self, settings: &GlobalSettings, out: &mut impl Write) -> UResult<()> {
while self.write_next(settings, out) {} while self
.write_next(settings, out)
.map_err_context(|| "write failed".into())?
{}
drop(self.request_sender); drop(self.request_sender);
self.reader_join_handle.join().unwrap() self.reader_join_handle.join().unwrap()
} }
fn write_next(&mut self, settings: &GlobalSettings, out: &mut impl Write) -> bool { fn write_next(
&mut self,
settings: &GlobalSettings,
out: &mut impl Write,
) -> std::io::Result<bool> {
if let Some(file) = self.heap.peek() { if let Some(file) = self.heap.peek() {
let prev = self.prev.replace(PreviousLine { let prev = self.prev.replace(PreviousLine {
chunk: file.current_chunk.clone(), chunk: file.current_chunk.clone(),
@ -303,12 +310,12 @@ impl FileMerger<'_> {
file.current_chunk.line_data(), file.current_chunk.line_data(),
); );
if cmp == Ordering::Equal { if cmp == Ordering::Equal {
return; return Ok(());
} }
} }
} }
current_line.print(out, settings); current_line.print(out, settings)
}); })?;
let was_last_line_for_file = file.current_chunk.lines().len() == file.line_idx + 1; let was_last_line_for_file = file.current_chunk.lines().len() == file.line_idx + 1;
@ -335,7 +342,7 @@ impl FileMerger<'_> {
} }
} }
} }
!self.heap.is_empty() Ok(!self.heap.is_empty())
} }
} }

View file

@ -42,7 +42,7 @@ use std::str::Utf8Error;
use thiserror::Error; use thiserror::Error;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::strip_errno; use uucore::error::{FromIo, strip_errno};
use uucore::error::{UError, UResult, USimpleError, UUsageError, set_exit_code}; use uucore::error::{UError, UResult, USimpleError, UUsageError, set_exit_code};
use uucore::line_ending::LineEnding; use uucore::line_ending::LineEnding;
use uucore::parse_size::{ParseSizeError, Parser}; use uucore::parse_size::{ParseSizeError, Parser};
@ -481,13 +481,14 @@ impl<'a> Line<'a> {
Self { line, index } Self { line, index }
} }
fn print(&self, writer: &mut impl Write, settings: &GlobalSettings) { fn print(&self, writer: &mut impl Write, settings: &GlobalSettings) -> std::io::Result<()> {
if settings.debug { if settings.debug {
self.print_debug(settings, writer).unwrap(); self.print_debug(settings, writer)?;
} else { } else {
writer.write_all(self.line.as_bytes()).unwrap(); writer.write_all(self.line.as_bytes())?;
writer.write_all(&[settings.line_ending.into()]).unwrap(); writer.write_all(&[settings.line_ending.into()])?;
} }
Ok(())
} }
/// Writes indicators for the selections this line matched. The original line content is NOT expected /// Writes indicators for the selections this line matched. The original line content is NOT expected
@ -1827,11 +1828,19 @@ fn print_sorted<'a, T: Iterator<Item = &'a Line<'a>>>(
iter: T, iter: T,
settings: &GlobalSettings, settings: &GlobalSettings,
output: Output, output: Output,
) { ) -> UResult<()> {
let output_name = output
.as_output_name()
.unwrap_or("standard output")
.to_owned();
let ctx = || format!("write failed: {}", output_name.maybe_quote());
let mut writer = output.into_write(); let mut writer = output.into_write();
for line in iter { for line in iter {
line.print(&mut writer, settings); line.print(&mut writer, settings).map_err_context(ctx)?;
} }
writer.flush().map_err_context(ctx)?;
Ok(())
} }
fn open(path: impl AsRef<OsStr>) -> UResult<Box<dyn Read + Send>> { fn open(path: impl AsRef<OsStr>) -> UResult<Box<dyn Read + Send>> {

View file

@ -1336,3 +1336,13 @@ fn test_human_blocks_r_and_q() {
fn test_args_check_conflict() { fn test_args_check_conflict() {
new_ucmd!().arg("-c").arg("-C").fails(); new_ucmd!().arg("-c").arg("-C").fails();
} }
#[cfg(target_os = "linux")]
#[test]
fn test_failed_write_is_reported() {
new_ucmd!()
.pipe_in("hello")
.set_stdout(std::fs::File::create("/dev/full").unwrap())
.fails()
.stderr_is("sort: write failed: 'standard output': No space left on device\n");
}