Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions src/uu/csplit/src/csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()?;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -379,7 +392,7 @@ impl SplitWriter<'_> {
}
self.writeln(&line)?;
}
self.finish_split();
self.finish_split()?;
ret
}

Expand Down Expand Up @@ -446,15 +459,15 @@ impl SplitWriter<'_> {
self.writeln(&line?)?;
}
None => {
self.finish_split();
self.finish_split()?;
return Err(CsplitError::LineOutOfRange(
pattern_as_str.to_string(),
));
}
}
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 {
Expand Down Expand Up @@ -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()));
}
Expand All @@ -511,7 +524,7 @@ impl SplitWriter<'_> {
}
}

self.finish_split();
self.finish_split()?;
Err(CsplitError::MatchNotFound(pattern_as_str.to_string()))
}
}
Expand Down
32 changes: 32 additions & 0 deletions tests/by-util/test_csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Loading