This commit is contained in:
mattsu 2025-12-22 20:42:03 +09:00 committed by GitHub
commit fce25ec9fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 73 additions and 5 deletions

View file

@ -133,6 +133,7 @@ setfattr
setlocale
shortcode
shortcodes
sigaction
siginfo
sigusr
strcasecmp

1
Cargo.lock generated
View file

@ -3782,6 +3782,7 @@ dependencies = [
"clap",
"codspeed-divan-compat",
"fluent",
"nix",
"num-bigint",
"num-traits",
"tempfile",

1
fuzz/Cargo.lock generated
View file

@ -1653,6 +1653,7 @@ dependencies = [
"bigdecimal",
"clap",
"fluent",
"nix",
"num-bigint",
"num-traits",
"thiserror",

View file

@ -21,6 +21,7 @@ path = "src/seq.rs"
[dependencies]
bigdecimal = { workspace = true }
clap = { workspace = true }
nix = { workspace = true }
num-bigint = { workspace = true }
num-traits = { workspace = true }
thiserror = { workspace = true }

View file

@ -5,6 +5,8 @@
// spell-checker:ignore (ToDO) bigdecimal extendedbigdecimal numberparse hexadecimalfloat biguint
use std::ffi::{OsStr, OsString};
use std::io::{BufWriter, Write, stdout};
#[cfg(unix)]
use std::sync::atomic::{AtomicBool, Ordering};
use clap::{Arg, ArgAction, Command};
use num_bigint::BigUint;
@ -209,16 +211,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
padding,
);
match result {
Ok(()) => Ok(()),
Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => {
let sigpipe_ignored = sigpipe_is_ignored();
if let Err(err) = result {
if err.kind() == std::io::ErrorKind::BrokenPipe {
// GNU seq prints the Broken pipe message but still exits with status 0
// unless SIGPIPE was explicitly ignored, in which case it should fail.
let err = err.map_err_context(|| "write error".into());
uucore::show_error!("{err}");
Ok(())
return if sigpipe_ignored { Err(err) } else { Ok(()) };
}
Err(err) => Err(err.map_err_context(|| "write error".into())),
return Err(err.map_err_context(|| "write error".into()));
}
Ok(())
}
pub fn uu_app() -> Command {
@ -332,6 +336,44 @@ fn fast_print_seq(
Ok(())
}
#[cfg(unix)]
static SIGPIPE_WAS_IGNORED: AtomicBool = AtomicBool::new(false);
#[cfg(unix)]
/// # Safety
/// This function runs once at process initialization and only observes the
/// current `SIGPIPE` handler, so there are no extra safety requirements for callers.
unsafe extern "C" fn capture_sigpipe_state() {
use nix::libc;
use std::{mem::MaybeUninit, ptr};
let mut current = MaybeUninit::<libc::sigaction>::uninit();
if unsafe { libc::sigaction(libc::SIGPIPE, ptr::null(), current.as_mut_ptr()) } == 0 {
let ignored = unsafe { current.assume_init() }.sa_sigaction == libc::SIG_IGN;
SIGPIPE_WAS_IGNORED.store(ignored, Ordering::Relaxed);
}
}
#[cfg(all(unix, not(target_os = "macos")))]
#[used]
#[unsafe(link_section = ".init_array")]
static CAPTURE_SIGPIPE_STATE: unsafe extern "C" fn() = capture_sigpipe_state;
#[cfg(all(unix, target_os = "macos"))]
#[used]
#[unsafe(link_section = "__DATA,__mod_init_func")]
static CAPTURE_SIGPIPE_STATE_APPLE: unsafe extern "C" fn() = capture_sigpipe_state;
#[cfg(unix)]
fn sigpipe_is_ignored() -> bool {
SIGPIPE_WAS_IGNORED.load(Ordering::Relaxed)
}
#[cfg(not(unix))]
const fn sigpipe_is_ignored() -> bool {
false
}
fn done_printing<T: Zero + PartialOrd>(next: &T, increment: &T, last: &T) -> bool {
if increment >= &T::zero() {
next > last

View file

@ -4,6 +4,10 @@
// file that was distributed with this source code.
// spell-checker:ignore lmnop xlmnop
use uutests::new_ucmd;
#[cfg(unix)]
use uutests::util::TestScenario;
#[cfg(unix)]
use uutests::util_name;
#[test]
fn test_invalid_arg() {
@ -203,6 +207,24 @@ fn test_width_invalid_float() {
.usage_error("invalid floating point argument: '1e2.3'");
}
#[test]
#[cfg(unix)]
fn test_sigpipe_ignored_reports_write_error() {
let scene = TestScenario::new(util_name!());
let seq_bin = scene.bin_path.clone().into_os_string();
let script = "trap '' PIPE; { \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1";
let result = scene.cmd_shell(script).env("SEQ_BIN", &seq_bin).succeeds();
assert_eq!(result.stdout_str(), "1\n");
let err_contents = scene.fixtures.read("err");
assert!(
err_contents.contains("seq: write error: Broken pipe"),
"stderr missing write error message: {err_contents:?}"
);
assert_eq!(scene.fixtures.read("code"), "1\n");
}
// ---- Tests for the big integer based path ----
#[test]