From f779baa15f152fb5cdfc3af6bd8e680666ba1b8b Mon Sep 17 00:00:00 2001 From: RelunSec Date: Tue, 30 Jun 2026 11:14:30 +0000 Subject: [PATCH 1/3] fix(pr): error messages to align with gnu --- src/uu/pr/src/pr.rs | 50 +++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 88e281c6805..c47a535efea 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -190,6 +190,19 @@ impl From for PrError { enum PrError { #[error("pr: {msg}")] EncounteredErrors { msg: String }, + + // New variant that correctly formats the file path error like GNU pr + #[error("pr: {path}: {msg}")] + PathError { path: String, msg: String }, +} + +// Helper function to match GNU pr output styling by cleaning up standard OS strings +fn strip_errno(err_msg: String) -> String { + if let Some(idx) = err_msg.find(" (os error") { + err_msg[..idx].to_string() + } else { + err_msg + } } pub fn uu_app() -> Command { @@ -973,17 +986,28 @@ fn apply_expand_tab(chunk: &mut Vec, byte: u8, expand_options: &ExpandTabsOp fn pr(path: &str, options: &OutputOptions) -> Result { // Read the entire contents of the file into a buffer. - // - // TODO Read incrementally. - let buf = read_to_end(path)?; + // If it fails, map the error to include the filename context + // and strip the OS error number to match GNU behavior. + let buf = read_to_end(path).map_err(|e| PrError::PathError { + path: path.to_string(), + msg: strip_errno(e.to_string()), + })?; + + let start_page = options.start_page; + let mut page_counter = start_page; let pages = get_pages(options, 0, &buf); - // Split the text into pages, and then print each line in each page. - for page_with_page_number in pages { - let page_number = page_with_page_number.0 + 1; - let page = page_with_page_number.1; - print_page(&page, options, page_number)?; + if pages.is_empty() { + return Ok(0); + } + + for (page_num, lines) in pages { + let new_page_number = page_num + 1; + if page_counter != new_page_number { + page_counter = new_page_number; + } + print_page(&lines, options, page_counter)?; } Ok(0) @@ -1137,12 +1161,12 @@ fn get_file_line_groups( let mut all_lines = vec![]; for (file_id, path) in paths.iter().enumerate() { // Read the entire contents of the file into a buffer. - // - // TODO Read incrementally. - let buf = read_to_end(path)?; + let buf = read_to_end(path).map_err(|e| PrError::PathError { + path: (*path).to_string(), + msg: strip_errno(e.to_string()), + })?; - // Split the text into pages and collect each line for - // subsequent grouping. + // Split the text into pages and collect each line for subsequent grouping. for (_, mut page) in get_pages(options, file_id, &buf) { all_lines.append(&mut page); } From 490e300aa696caed02c8ca8ed381d0cfb357a79b Mon Sep 17 00:00:00 2001 From: RelunSec Date: Tue, 30 Jun 2026 11:19:38 +0000 Subject: [PATCH 2/3] Add tests Add test for file not found error message in test_pr.rs --- tests/by-util/test_pr.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index 6c636843ffa..6d5c88bb7cf 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -1019,3 +1019,11 @@ fn test_merge_empty_input() { .succeeds() .no_output(); } + +#[test] +fn test_file_not_found_error_message() { + new_ucmd!() + .arg("aha") + .fails_with_code(1) + .stderr_is("pr: aha: No such file or directory\n"); +} From 2d46008397fad2cbbc286d6778cc4f44f4a8ecca Mon Sep 17 00:00:00 2001 From: RelunSec Date: Tue, 30 Jun 2026 11:30:45 +0000 Subject: [PATCH 3/3] Update error handling in pr.rs to use uucore strip_errno --- src/uu/pr/src/pr.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index c47a535efea..a0e6d89337c 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -19,7 +19,7 @@ use std::time::SystemTime; use thiserror::Error; use uucore::display::Quotable; -use uucore::error::UResult; +use uucore::error::{UResult, strip_errno}; use uucore::format_usage; use uucore::time::{FormatSystemTimeFallback, format, format_system_time}; use uucore::translate; @@ -196,15 +196,6 @@ enum PrError { PathError { path: String, msg: String }, } -// Helper function to match GNU pr output styling by cleaning up standard OS strings -fn strip_errno(err_msg: String) -> String { - if let Some(idx) = err_msg.find(" (os error") { - err_msg[..idx].to_string() - } else { - err_msg - } -} - pub fn uu_app() -> Command { Command::new("pr") .version(uucore::crate_version!()) @@ -990,7 +981,7 @@ fn pr(path: &str, options: &OutputOptions) -> Result { // and strip the OS error number to match GNU behavior. let buf = read_to_end(path).map_err(|e| PrError::PathError { path: path.to_string(), - msg: strip_errno(e.to_string()), + msg: strip_errno(&e), })?; let start_page = options.start_page; @@ -1163,7 +1154,7 @@ fn get_file_line_groups( // Read the entire contents of the file into a buffer. let buf = read_to_end(path).map_err(|e| PrError::PathError { path: (*path).to_string(), - msg: strip_errno(e.to_string()), + msg: strip_errno(&e), })?; // Split the text into pages and collect each line for subsequent grouping.