Skip to content

Commit ba3f266

Browse files
authored
Merge pull request uutils#5285 from cakebaker/nl_multiple_files
nl: make line number and --join-blank-lines work over multiple files
2 parents e2561f5 + 1a30a1b commit ba3f266

File tree

2 files changed

+64
-12
lines changed

2 files changed

+64
-12
lines changed

src/uu/nl/src/nl.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ impl Default for Settings {
5555
}
5656
}
5757

58+
struct Stats {
59+
line_number: i64,
60+
consecutive_empty_lines: u64,
61+
}
62+
63+
impl Stats {
64+
fn new(starting_line_number: i64) -> Self {
65+
Self {
66+
line_number: starting_line_number,
67+
consecutive_empty_lines: 0,
68+
}
69+
}
70+
}
71+
5872
// NumberingStyle stores which lines are to be numbered.
5973
// The possible options are:
6074
// 1. Number all lines
@@ -160,6 +174,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
160174
None => vec!["-".to_owned()],
161175
};
162176

177+
let mut stats = Stats::new(settings.starting_line_number);
178+
163179
for file in &files {
164180
if file == "-" {
165181
// If both file names and '-' are specified, we choose to treat first all
@@ -170,12 +186,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
170186
let path = Path::new(file);
171187
let reader = File::open(path).map_err_context(|| file.to_string())?;
172188
let mut buffer = BufReader::new(reader);
173-
nl(&mut buffer, &settings)?;
189+
nl(&mut buffer, &mut stats, &settings)?;
174190
}
175191

176192
if read_stdin {
177193
let mut buffer = BufReader::new(stdin());
178-
nl(&mut buffer, &settings)?;
194+
nl(&mut buffer, &mut stats, &settings)?;
179195
}
180196
Ok(())
181197
}
@@ -285,18 +301,16 @@ pub fn uu_app() -> Command {
285301
}
286302

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

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

296310
if line.is_empty() {
297-
consecutive_empty_lines += 1;
311+
stats.consecutive_empty_lines += 1;
298312
} else {
299-
consecutive_empty_lines = 0;
313+
stats.consecutive_empty_lines = 0;
300314
};
301315

302316
// FIXME section delimiters are hardcoded and settings.section_delimiter is ignored
@@ -312,7 +326,7 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
312326
if let Some(new_style) = new_numbering_style {
313327
current_numbering_style = new_style;
314328
if settings.renumber {
315-
line_no = settings.starting_line_number;
329+
stats.line_number = settings.starting_line_number;
316330
}
317331
println!();
318332
} else {
@@ -321,7 +335,7 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
321335
// for numbering, and only number the last one
322336
NumberingStyle::All
323337
if line.is_empty()
324-
&& consecutive_empty_lines % settings.join_blank_lines != 0 =>
338+
&& stats.consecutive_empty_lines % settings.join_blank_lines != 0 =>
325339
{
326340
false
327341
}
@@ -336,13 +350,13 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
336350
"{}{}{}",
337351
settings
338352
.number_format
339-
.format(line_no, settings.number_width),
353+
.format(stats.line_number, settings.number_width),
340354
settings.number_separator,
341355
line
342356
);
343357
// update line number for the potential next line
344-
match line_no.checked_add(settings.line_increment) {
345-
Some(new_line_no) => line_no = new_line_no,
358+
match stats.line_number.checked_add(settings.line_increment) {
359+
Some(new_line_number) => stats.line_number = new_line_number,
346360
None => return Err(USimpleError::new(1, "line number overflow")),
347361
}
348362
} else {

tests/by-util/test_nl.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,31 @@ fn test_join_blank_lines() {
284284
}
285285
}
286286

287+
#[test]
288+
fn test_join_blank_lines_multiple_files() {
289+
let scene = TestScenario::new(util_name!());
290+
let at = &scene.fixtures;
291+
292+
at.write("a.txt", "\n\n");
293+
at.write("b.txt", "\n\n");
294+
at.write("c.txt", "\n\n");
295+
296+
for arg in ["-l3", "--join-blank-lines=3"] {
297+
scene
298+
.ucmd()
299+
.args(&[arg, "--body-numbering=a", "a.txt", "b.txt", "c.txt"])
300+
.succeeds()
301+
.stdout_is(concat!(
302+
" \n",
303+
" \n",
304+
" 1\t\n",
305+
" \n",
306+
" \n",
307+
" 2\t\n",
308+
));
309+
}
310+
}
311+
287312
#[test]
288313
fn test_join_blank_lines_zero() {
289314
for arg in ["-l0", "--join-blank-lines=0"] {
@@ -311,6 +336,19 @@ fn test_default_body_numbering() {
311336
.stdout_is(" 1\ta\n \n 2\tb\n");
312337
}
313338

339+
#[test]
340+
fn test_default_body_numbering_multiple_files() {
341+
let (at, mut ucmd) = at_and_ucmd!();
342+
343+
at.write("a.txt", "a");
344+
at.write("b.txt", "b");
345+
at.write("c.txt", "c");
346+
347+
ucmd.args(&["a.txt", "b.txt", "c.txt"])
348+
.succeeds()
349+
.stdout_is(" 1\ta\n 2\tb\n 3\tc\n");
350+
}
351+
314352
#[test]
315353
fn test_body_numbering_all_lines_without_delimiter() {
316354
for arg in ["-ba", "--body-numbering=a"] {

0 commit comments

Comments
 (0)