pr: uniformly scan for form feed and newline chars

Fix the way form feed characters are interpreted by changing the way
lines and pages are found. Before this commit, a file comprising two
form feed characters (`/f/f`) would result in too few trailing newlines
at the end of the second page. After this change, each page is produced
with the correct number of lines.

This commit changes the way files are read, replacing complex iterators
with a loop-based approach, iteratively scanning for newline or form
feed characters. The `memchr` library is used to efficiently scan for
these two characters. One downside of this implementation is that it
currently reads the entire input file into memory; this can be improved
in subsequent merge requests.
This commit is contained in:
Jeffrey Finkelstein
2026-01-18 20:26:40 +01:00
committed by Sylvestre Ledru
parent 500604287b
commit 1a81d1bb6b
4 changed files with 231 additions and 217 deletions
Generated
+1
View File
@@ -3690,6 +3690,7 @@ dependencies = [
"clap",
"fluent",
"itertools 0.14.0",
"memchr",
"regex",
"thiserror 2.0.17",
"uucore",
+1
View File
@@ -21,6 +21,7 @@ path = "src/pr.rs"
clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "time"] }
itertools = { workspace = true }
memchr = { workspace = true }
regex = { workspace = true }
thiserror = { workspace = true }
fluent = { workspace = true }
+204 -217
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -606,3 +606,28 @@ fn test_omit_pagination_option() {
.pipe_in("a\nb\n")
.succeeds();
}
#[test]
fn test_form_feed_newlines() {
// Here we define the expected output.
//
// Each page should have the same number of blank lines before the
// form-feed character.
let whitespace = " ".repeat(50);
let datetime_pattern = r"\d\d\d\d-\d\d-\d\d \d\d:\d\d";
let page1 = format!("\n\n{datetime_pattern}{whitespace}Page 1\n\n\n\n\x0c");
let page2 = format!("\n\n{datetime_pattern}{whitespace}Page 2\n\n\n\n\x0c");
let pattern = format!("{page1}{page2}");
let regex = Regex::new(&pattern).unwrap();
// Command line: `printf "\f\f" | pr -f`.
//
// Escape code `\x0c` in a Rust string literal is the ASCII escape
// code `\f` for the "form feed" character (which appears like
// `^L` in the terminal).
new_ucmd!()
.arg("-f")
.pipe_in("\x0c\x0c")
.succeeds()
.stdout_matches(&regex);
}