From 367e597c6a0e78c36f7c3553c20597962d4608f4 Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Fri, 23 Oct 2026 03:32:12 +0530 Subject: [PATCH 1/2] pr: stop merged output after the last line without a page trailer With -m and -t, -T or -F, the merged files were padded with empty rows to the full page length, so two two-line files gave 56 lines; GNU pr prints the two lines. --- src/uu/pr/src/pr.rs | 9 +++++++++ tests/by-util/test_pr.rs | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index fd9c9cd1603..aa2eb5617e1 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -1502,6 +1502,15 @@ fn write_columns( let blank_line = FileLine::default(); for row in table { + // Merged files are padded with empty cells to fill the page; once + // every file has run out there is nothing left to print without a + // page trailer to pad for. + if merge + && row.iter().all(Option::is_none) + && (feed_line_present || !options.display_header_and_trailer) + { + break; + } // On a page that is not completely filled the last row stops part way // through, and its final cell must not be followed by a column // separator. Merging prints an empty cell for every column instead, so diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index 9f4f050723d..c6c815a3820 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -701,6 +701,19 @@ fn test_join_lines_keeps_column_separator() { .stdout_is("1\t3\n2\t4\n"); } +#[test] +fn test_merge_stops_after_last_line_without_header() { + // without a page trailer there is nothing to pad to: like GNU pr, stop + // after the last merged line (-F still prints the 5 header lines and a form feed) + for (flag, lines) in [("-t", 2), ("-T", 2), ("-F", 8)] { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("one", "a\nb\n"); + at.write("two", "c\nd\n"); + let result = ucmd.args(&["-m", flag, "one", "two"]).succeeds(); + assert_eq!(result.stdout_str().lines().count(), lines, "{flag}"); + } +} + #[test] fn test_value_for_number_lines() { // *5 is of the form [SEP[NUMBER]] so is accepted and succeeds From 8df73852cbc5caeb542380edf17e6d8cb44d3c48 Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Sun, 25 Oct 2026 13:25:22 +0530 Subject: [PATCH 2/2] pr: name the no-trailer condition, check the exact merged output --- src/uu/pr/src/pr.rs | 15 +++++---------- tests/by-util/test_pr.rs | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index aa2eb5617e1..3d830d909a7 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -1501,14 +1501,12 @@ fn write_columns( }; let blank_line = FileLine::default(); + // Without a page trailer there is nothing to pad the page out to. + let no_trailer = feed_line_present || !options.display_header_and_trailer; for row in table { // Merged files are padded with empty cells to fill the page; once - // every file has run out there is nothing left to print without a - // page trailer to pad for. - if merge - && row.iter().all(Option::is_none) - && (feed_line_present || !options.display_header_and_trailer) - { + // every file has run out there is nothing left to print. + if merge && no_trailer && row.iter().all(Option::is_none) { break; } // On a page that is not completely filled the last row stops part way @@ -1541,10 +1539,7 @@ fn write_columns( // The last row of a partly filled page has content in its leading // columns and nothing in the rest, so it still needs to be terminated. // Only a row without any content at all means the page ran out. - if cells_written == 0 - && not_found_break - && (feed_line_present || !options.display_header_and_trailer) - { + if cells_written == 0 && not_found_break && no_trailer { break; } writer.write_all(line_separator)?; diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index c6c815a3820..af32d3d30be 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -705,12 +705,22 @@ fn test_join_lines_keeps_column_separator() { fn test_merge_stops_after_last_line_without_header() { // without a page trailer there is nothing to pad to: like GNU pr, stop // after the last merged line (-F still prints the 5 header lines and a form feed) - for (flag, lines) in [("-t", 2), ("-T", 2), ("-F", 8)] { + // -J keeps the columns from being padded, so the lines are short + let body = "a\tc\nb\td\n"; + let header = format!("\n\nX{}Page 1\n\n\n", " ".repeat(65)); + for (args, expected) in [ + (vec!["-J", "-t"], body.to_string()), + (vec!["-J", "-T"], body.to_string()), + (vec!["-J", "-F", "-D", "X"], format!("{header}{body}\x0c")), + ] { let (at, mut ucmd) = at_and_ucmd!(); at.write("one", "a\nb\n"); at.write("two", "c\nd\n"); - let result = ucmd.args(&["-m", flag, "one", "two"]).succeeds(); - assert_eq!(result.stdout_str().lines().count(), lines, "{flag}"); + ucmd.arg("-m") + .args(&args) + .args(&["one", "two"]) + .succeeds() + .stdout_is(expected); } }