Skip to content

Commit c1eeed6

Browse files
committed
tr: restore default action of SIGPIPE
1 parent 7e5d365 commit c1eeed6

File tree

3 files changed

+29
-9
lines changed

3 files changed

+29
-9
lines changed

src/uu/tr/src/operation.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -708,12 +708,16 @@ where
708708
let filtered = buf.iter().filter_map(|&c| translator.translate(c));
709709
output_buf.extend(filtered);
710710

711+
#[cfg(not(target_os = "windows"))]
712+
output
713+
.write_all(&output_buf)
714+
.map_err_context(|| get_message("tr-error-write-error"))?;
715+
716+
// SIGPIPE is not available on Windows.
717+
#[cfg(target_os = "windows")]
711718
if let Err(err) = output.write_all(&output_buf) {
712-
// Treat broken pipe as a successful termination, which is the
713-
// expected behavior when stdout is a pipeline and the downstream
714-
// process terminates.
715719
if err.kind() == std::io::ErrorKind::BrokenPipe {
716-
break;
720+
std::process::exit(13);
717721
} else {
718722
return Err(err.map_err_context(|| get_message("tr-error-write-error")));
719723
}

src/uu/tr/src/tr.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ use operation::{
1313
};
1414
use std::collections::HashMap;
1515
use std::ffi::OsString;
16-
use std::io::{BufWriter, ErrorKind, Write, stdin, stdout};
16+
use std::io::{BufWriter, Write, stdin, stdout};
1717
use uucore::display::Quotable;
1818
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
1919
use uucore::fs::is_stdin_directory;
20+
#[cfg(not(target_os = "windows"))]
21+
use uucore::libc;
2022
use uucore::{format_usage, os_str_as_bytes, show};
2123

2224
use uucore::locale::{get_message, get_message_with_args};
@@ -31,6 +33,15 @@ mod options {
3133

3234
#[uucore::main]
3335
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
36+
// When we receive a SIGPIPE signal, we want to terminate the process so
37+
// that we don't print any error messages to stderr. Rust ignores SIGPIPE
38+
// (see https://github.com/rust-lang/rust/issues/62569), so we restore it's
39+
// default action here.
40+
#[cfg(not(target_os = "windows"))]
41+
unsafe {
42+
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
43+
}
44+
3445
let matches = uu_app().try_get_matches_from(args)?;
3546

3647
let delete_flag = matches.get_flag(options::DELETE);
@@ -157,10 +168,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
157168
translate_input(&mut locked_stdin, &mut buffered_stdout, op)?;
158169
}
159170

160-
// Handle broken pipe errors gracefully during flush.
171+
#[cfg(not(target_os = "windows"))]
172+
buffered_stdout
173+
.flush()
174+
.map_err_context(|| get_message("tr-error-write-error"))?;
175+
176+
// SIGPIPE is not available on Windows.
177+
#[cfg(target_os = "windows")]
161178
match buffered_stdout.flush() {
162179
Ok(()) => {}
163-
Err(err) if err.kind() == ErrorKind::BrokenPipe => {}
180+
Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => std::process::exit(13),
164181
Err(err) => return Err(err.map_err_context(|| get_message("tr-error-write-error"))),
165182
}
166183

tests/by-util/test_tr.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,5 @@ fn test_broken_pipe_no_error() {
15611561
.args(&["e", "a"])
15621562
.pipe_in("hello".repeat(100))
15631563
.run_stdout_starts_with(b"")
1564-
.success()
1565-
.stderr_is("");
1564+
.fails_silently();
15661565
}

0 commit comments

Comments
 (0)