mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
csplit: detect and report write errors (#9855)
* csplit: detect and report write errors * Add Rust integration tests for csplit write error detection * csplit: fix doc comment for finish_split
This commit is contained in:
+24
-11
@@ -127,7 +127,7 @@ where
|
||||
let ret = do_csplit(&mut split_writer, patterns_vec, &mut input_iter);
|
||||
|
||||
// consume the rest, unless there was an error
|
||||
if ret.is_ok() {
|
||||
let ret = if ret.is_ok() {
|
||||
input_iter.rewind_buffer();
|
||||
if let Some((_, line)) = input_iter.next() {
|
||||
// There is remaining input: create a final split and copy remainder
|
||||
@@ -136,14 +136,18 @@ where
|
||||
for (_, line) in input_iter {
|
||||
split_writer.writeln(&line?)?;
|
||||
}
|
||||
split_writer.finish_split();
|
||||
split_writer.finish_split()
|
||||
} else if all_up_to_line && options.suppress_matched {
|
||||
// GNU semantics for integer patterns with --suppress-matched:
|
||||
// even if no remaining input, create a final (possibly empty) split
|
||||
split_writer.new_writer()?;
|
||||
split_writer.finish_split();
|
||||
split_writer.finish_split()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret
|
||||
};
|
||||
// delete files on error by default
|
||||
if ret.is_err() && !options.keep_files {
|
||||
split_writer.delete_all_splits()?;
|
||||
@@ -305,15 +309,24 @@ impl SplitWriter<'_> {
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Some [`io::Error`] if the split could not be removed in case it should be elided.
|
||||
fn finish_split(&mut self) {
|
||||
/// Returns an error if flushing the writer fails.
|
||||
fn finish_split(&mut self) -> Result<(), CsplitError> {
|
||||
if !self.dev_null {
|
||||
// Flush the writer to ensure all data is written and errors are detected
|
||||
if let Some(ref mut writer) = self.current_writer {
|
||||
let file_name = self.options.split_name.get(self.counter - 1);
|
||||
writer
|
||||
.flush()
|
||||
.map_err_context(|| file_name.clone())
|
||||
.map_err(CsplitError::from)?;
|
||||
}
|
||||
if self.options.elide_empty_files && self.size == 0 {
|
||||
self.counter -= 1;
|
||||
} else if !self.options.quiet {
|
||||
println!("{}", self.size);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes all the split files that were created.
|
||||
@@ -379,7 +392,7 @@ impl SplitWriter<'_> {
|
||||
}
|
||||
self.writeln(&line)?;
|
||||
}
|
||||
self.finish_split();
|
||||
self.finish_split()?;
|
||||
ret
|
||||
}
|
||||
|
||||
@@ -446,7 +459,7 @@ impl SplitWriter<'_> {
|
||||
self.writeln(&line?)?;
|
||||
}
|
||||
None => {
|
||||
self.finish_split();
|
||||
self.finish_split()?;
|
||||
return Err(CsplitError::LineOutOfRange(
|
||||
pattern_as_str.to_string(),
|
||||
));
|
||||
@@ -454,7 +467,7 @@ impl SplitWriter<'_> {
|
||||
}
|
||||
offset -= 1;
|
||||
}
|
||||
self.finish_split();
|
||||
self.finish_split()?;
|
||||
|
||||
// if we have to suppress one line after we take the next and do nothing
|
||||
if next_line_suppress_matched {
|
||||
@@ -495,7 +508,7 @@ impl SplitWriter<'_> {
|
||||
);
|
||||
}
|
||||
|
||||
self.finish_split();
|
||||
self.finish_split()?;
|
||||
if input_iter.buffer_len() < offset_usize {
|
||||
return Err(CsplitError::LineOutOfRange(pattern_as_str.to_string()));
|
||||
}
|
||||
@@ -511,7 +524,7 @@ impl SplitWriter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
self.finish_split();
|
||||
self.finish_split()?;
|
||||
Err(CsplitError::MatchNotFound(pattern_as_str.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1551,3 +1551,35 @@ fn test_csplit_non_utf8_paths() {
|
||||
|
||||
ucmd.arg(&filename).arg("3").succeeds();
|
||||
}
|
||||
|
||||
/// Test write error detection using /dev/full
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_write_error_dev_full() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.symlink_file("/dev/full", "xx01");
|
||||
|
||||
ucmd.args(&["-", "2"])
|
||||
.pipe_in("1\n2\n")
|
||||
.fails_with_code(1)
|
||||
.stderr_contains("xx01: No space left on device");
|
||||
|
||||
// Files cleaned up by default
|
||||
assert!(!at.file_exists("xx00"));
|
||||
}
|
||||
|
||||
/// Test write error with -k keeps files
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_write_error_dev_full_keep_files() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.symlink_file("/dev/full", "xx01");
|
||||
|
||||
ucmd.args(&["-k", "-", "2"])
|
||||
.pipe_in("1\n2\n")
|
||||
.fails_with_code(1)
|
||||
.stderr_contains("xx01: No space left on device");
|
||||
|
||||
assert!(at.file_exists("xx00"));
|
||||
assert_eq!(at.read("xx00"), "1\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user