From a129b4ba72fb49b14566879944968c72d69860c9 Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Mon, 4 May 2026 19:20:55 -0700 Subject: [PATCH 1/2] fix: handle write errors gracefully instead of panicking on /dev/full Replace println! with writeln!(io::stdout(), ...) and handle BrokenPipe silently, other I/O errors via stderr + exit 1. Matches the uutils convention used in coreutils' true.rs. Fixes #5 Signed-off-by: SAY-5 --- src/main.rs | 11 ++++++++++- tests/by-util/test_awk.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 1852fb6..f06f779 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ mod cli; mod utils; use std::env::args_os; +use std::io::{self, Write}; use bumpalo::Bump; use clap::Parser as _; @@ -45,7 +46,15 @@ fn uu_main() -> Result<()> { return Ok(()); } }; - println!("---\n{ast}"); + // Avoid `println!`, which panics on broken pipe / ENOSPC (e.g. `>/dev/full`). + // Match the uutils convention: silently exit on BrokenPipe, report other I/O + // errors to stderr and exit with failure. + if let Err(e) = writeln!(io::stdout(), "---\n{ast}") { + if e.kind() == io::ErrorKind::BrokenPipe { + return Ok(()); + } + exit_err(Some(format!("awk: error writing to standard output: {e}"))); + } dbg!(arena.chunk_capacity()); // for token in lex { diff --git a/tests/by-util/test_awk.rs b/tests/by-util/test_awk.rs index 5ff71e2..9841b8c 100644 --- a/tests/by-util/test_awk.rs +++ b/tests/by-util/test_awk.rs @@ -14,3 +14,29 @@ fn print_first_field() { fn no_args_fails_code_one() { ucmd().fails_with_code(1); } + +// Regression test for issue #5: writing to /dev/full must not panic. +#[cfg(target_os = "linux")] +#[test] +fn write_to_dev_full_does_not_panic() { + use std::fs::OpenOptions; + use std::process::{Command, Stdio}; + + let dev_full = match OpenOptions::new().write(true).open("/dev/full") { + Ok(f) => f, + Err(_) => return, // /dev/full not available; skip. + }; + let output = Command::new(super::TESTS_BINARY) + .arg("BEGIN { print 1 }") + .stdout(Stdio::from(dev_full)) + .stderr(Stdio::piped()) + .output() + .expect("failed to spawn awk"); + // Must not panic (panic exits with code 2). + assert_ne!(output.status.code(), Some(2)); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + !stderr.contains("panicked"), + "awk panicked on write to /dev/full: stderr={stderr}" + ); +} From 5cd8a3e779bc7626edd8d810012aabf808d80af2 Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Tue, 5 May 2026 22:35:46 -0700 Subject: [PATCH 2/2] address review: replace print macros with structured logging Signed-off-by: SAY-5 --- Cargo.toml | 2 ++ src/main.rs | 10 +++------- src/utils.rs | 3 ++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ef673cb..cec0bb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,8 @@ unused_qualifications = "warn" all = { level = "warn", priority = -1 } cargo = { level = "warn", priority = -1 } pedantic = { level = "warn", priority = -1 } +print_stdout = "warn" # restriction: forbid print/println macros +print_stderr = "warn" # restriction: forbid eprint/eprintln macros use_self = "warn" # nursery lint cargo_common_metadata = "allow" # 3240 multiple_crate_versions = "allow" # 2882 diff --git a/src/main.rs b/src/main.rs index f06f779..437126f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,13 +46,9 @@ fn uu_main() -> Result<()> { return Ok(()); } }; - // Avoid `println!`, which panics on broken pipe / ENOSPC (e.g. `>/dev/full`). - // Match the uutils convention: silently exit on BrokenPipe, report other I/O - // errors to stderr and exit with failure. - if let Err(e) = writeln!(io::stdout(), "---\n{ast}") { - if e.kind() == io::ErrorKind::BrokenPipe { - return Ok(()); - } + if let Err(e) = writeln!(io::stdout(), "---\n{ast}") + && e.kind() != io::ErrorKind::BrokenPipe + { exit_err(Some(format!("awk: error writing to standard output: {e}"))); } dbg!(arena.chunk_capacity()); diff --git a/src/utils.rs b/src/utils.rs index 721aa95..ae92746 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,6 +4,7 @@ // files that was distributed with this source code. use std::fmt::{Debug, Display}; +use std::io::{self, Write}; use std::panic::{UnwindSafe, catch_unwind, set_hook, take_hook}; use std::process::exit; @@ -73,7 +74,7 @@ pub fn exit_with(res: Result>, impl Display + Debug>) pub fn exit_err(err: Option) -> ! { if let Some(err) = err { - eprintln!("{err}"); + let _ = writeln!(io::stderr(), "{err}"); } exit(EXIT_FAILURE) }