Skip to content
Open
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
13 changes: 10 additions & 3 deletions src/uu/csplit/src/csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,13 +482,22 @@ impl SplitWriter<'_> {
// The consequence is that the buffer may already be full with lines from a previous
// split, which is taken care of when calling `shrink_buffer_to_size`.
let offset_usize = offset.unsigned_abs() as usize;
// Number of lines belonging to the current split that were read before the
// matching line: the ones already held in the buffer (they are written to the
// current split by `shrink_buffer_to_size`) plus the ones read below. The target
// line of a negative offset may not go back past the start of the current split.
let mut lines_in_split = input_iter.buffer_len();
input_iter.set_size_of_buffer(offset_usize);
while let Some((ln, line)) = input_iter.next() {
let line = line?;
let l = line
.strip_suffix("\r\n")
.unwrap_or_else(|| line.strip_suffix('\n').unwrap_or(&line));
if regex.is_match(l) {
if offset_usize > lines_in_split {
self.finish_split()?;
return Err(CsplitError::LineOutOfRange(pattern_as_str.to_string()));
}
for line in input_iter.shrink_buffer_to_size() {
self.writeln(&line)?;
}
Expand All @@ -507,11 +516,9 @@ impl SplitWriter<'_> {
}

self.finish_split()?;
if input_iter.buffer_len() < offset_usize {
return Err(CsplitError::LineOutOfRange(pattern_as_str.to_string()));
}
return Ok(());
}
lines_in_split += 1;
if let Some(line) = input_iter.add_line_to_buffer(ln, line) {
self.writeln(&line)?;
}
Expand Down
86 changes: 86 additions & 0 deletions tests/by-util/test_csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,92 @@ fn test_up_to_match_negative_offset() {
assert_eq!(at.read("xx01"), generate(6, 51));
}

#[test]
fn test_up_to_match_negative_offset_before_split_start() {
// The target line (match minus the offset) may not precede the start of the
// current split.
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/3$/-3"])
.fails()
.stdout_is("0\n")
.stderr_is("csplit: '/3$/-3': line number out of range\n");

assert_eq!(
glob(&at.plus_as_string("xx*"))
.expect("there should be no splits created")
.count(),
0
);
}

#[test]
fn test_up_to_match_negative_offset_at_split_start() {
// The target line is exactly the first line of the current split: an empty
// split, not an error.
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/3$/-2"])
.succeeds()
.stdout_only("0\n141\n");

assert_eq!(at.read("xx00"), "");
assert_eq!(at.read("xx01"), generate(1, 51));
}

#[test]
fn test_up_to_match_negative_offset_before_second_split_start() {
// The bound is the start of the current split, not the start of the input.
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/10$/", "/12$/-3"])
.fails()
.stdout_is("18\n0\n")
.stderr_is("csplit: '/12$/-3': line number out of range\n");

assert_eq!(
glob(&at.plus_as_string("xx*"))
.expect("there should be no splits created")
.count(),
0
);
}

#[test]
fn test_up_to_match_negative_offset_at_second_split_start() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/10$/", "/12$/-2"])
.succeeds()
.stdout_only("18\n0\n123\n");

assert_eq!(at.read("xx00"), generate(1, 10));
assert_eq!(at.read("xx01"), "");
assert_eq!(at.read("xx02"), generate(10, 51));
}

#[test]
fn test_up_to_match_negative_offset_before_split_start_keep_files() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["-k", "numbers50.txt", "/3$/-3"])
.fails()
.stdout_is("0\n")
.stderr_is("csplit: '/3$/-3': line number out of range\n");

assert_eq!(at.read("xx00"), "");
}

#[test]
fn test_skip_to_match_negative_offset_before_split_start() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%3$%-3"])
.fails()
.stderr_is("csplit: '%3$%-3': line number out of range\n");

assert_eq!(
glob(&at.plus_as_string("xx*"))
.expect("there should be no splits created")
.count(),
0
);
}

#[test]
fn test_up_to_match_negative_offset_min_i32() {
new_ucmd!()
Expand Down
Loading