Skip to content

Commit

Permalink
Merge pull request uutils#5285 from cakebaker/nl_multiple_files
Browse files Browse the repository at this point in the history
nl: make line number and --join-blank-lines work over multiple files
  • Loading branch information
sylvestre authored Sep 23, 2023
2 parents e2561f5 + 1a30a1b commit ba3f266
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 12 deletions.
38 changes: 26 additions & 12 deletions src/uu/nl/src/nl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ impl Default for Settings {
}
}

struct Stats {
line_number: i64,
consecutive_empty_lines: u64,
}

impl Stats {
fn new(starting_line_number: i64) -> Self {
Self {
line_number: starting_line_number,
consecutive_empty_lines: 0,
}
}
}

// NumberingStyle stores which lines are to be numbered.
// The possible options are:
// 1. Number all lines
Expand Down Expand Up @@ -160,6 +174,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
None => vec!["-".to_owned()],
};

let mut stats = Stats::new(settings.starting_line_number);

for file in &files {
if file == "-" {
// If both file names and '-' are specified, we choose to treat first all
Expand All @@ -170,12 +186,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let path = Path::new(file);
let reader = File::open(path).map_err_context(|| file.to_string())?;
let mut buffer = BufReader::new(reader);
nl(&mut buffer, &settings)?;
nl(&mut buffer, &mut stats, &settings)?;
}

if read_stdin {
let mut buffer = BufReader::new(stdin());
nl(&mut buffer, &settings)?;
nl(&mut buffer, &mut stats, &settings)?;
}
Ok(())
}
Expand Down Expand Up @@ -285,18 +301,16 @@ pub fn uu_app() -> Command {
}

// nl implements the main functionality for an individual buffer.
fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
fn nl<T: Read>(reader: &mut BufReader<T>, stats: &mut Stats, settings: &Settings) -> UResult<()> {
let mut current_numbering_style = &settings.body_numbering;
let mut line_no = settings.starting_line_number;
let mut consecutive_empty_lines = 0;

for line in reader.lines() {
let line = line.map_err_context(|| "could not read line".to_string())?;

if line.is_empty() {
consecutive_empty_lines += 1;
stats.consecutive_empty_lines += 1;
} else {
consecutive_empty_lines = 0;
stats.consecutive_empty_lines = 0;
};

// FIXME section delimiters are hardcoded and settings.section_delimiter is ignored
Expand All @@ -312,7 +326,7 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
if let Some(new_style) = new_numbering_style {
current_numbering_style = new_style;
if settings.renumber {
line_no = settings.starting_line_number;
stats.line_number = settings.starting_line_number;
}
println!();
} else {
Expand All @@ -321,7 +335,7 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
// for numbering, and only number the last one
NumberingStyle::All
if line.is_empty()
&& consecutive_empty_lines % settings.join_blank_lines != 0 =>
&& stats.consecutive_empty_lines % settings.join_blank_lines != 0 =>
{
false
}
Expand All @@ -336,13 +350,13 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
"{}{}{}",
settings
.number_format
.format(line_no, settings.number_width),
.format(stats.line_number, settings.number_width),
settings.number_separator,
line
);
// update line number for the potential next line
match line_no.checked_add(settings.line_increment) {
Some(new_line_no) => line_no = new_line_no,
match stats.line_number.checked_add(settings.line_increment) {
Some(new_line_number) => stats.line_number = new_line_number,
None => return Err(USimpleError::new(1, "line number overflow")),
}
} else {
Expand Down
38 changes: 38 additions & 0 deletions tests/by-util/test_nl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,31 @@ fn test_join_blank_lines() {
}
}

#[test]
fn test_join_blank_lines_multiple_files() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

at.write("a.txt", "\n\n");
at.write("b.txt", "\n\n");
at.write("c.txt", "\n\n");

for arg in ["-l3", "--join-blank-lines=3"] {
scene
.ucmd()
.args(&[arg, "--body-numbering=a", "a.txt", "b.txt", "c.txt"])
.succeeds()
.stdout_is(concat!(
" \n",
" \n",
" 1\t\n",
" \n",
" \n",
" 2\t\n",
));
}
}

#[test]
fn test_join_blank_lines_zero() {
for arg in ["-l0", "--join-blank-lines=0"] {
Expand Down Expand Up @@ -311,6 +336,19 @@ fn test_default_body_numbering() {
.stdout_is(" 1\ta\n \n 2\tb\n");
}

#[test]
fn test_default_body_numbering_multiple_files() {
let (at, mut ucmd) = at_and_ucmd!();

at.write("a.txt", "a");
at.write("b.txt", "b");
at.write("c.txt", "c");

ucmd.args(&["a.txt", "b.txt", "c.txt"])
.succeeds()
.stdout_is(" 1\ta\n 2\tb\n 3\tc\n");
}

#[test]
fn test_body_numbering_all_lines_without_delimiter() {
for arg in ["-ba", "--body-numbering=a"] {
Expand Down

0 comments on commit ba3f266

Please sign in to comment.