From 19495aa68bd9574b99b9624f16d9cccc5aff419f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 12 Nov 2024 10:49:52 +0100 Subject: [PATCH 1/2] `bzip2.rs`: test that triggers `isatty` --- tests/quick.rs | 147 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/tests/quick.rs b/tests/quick.rs index 7ef688b13..afbae0ed6 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -73,6 +73,41 @@ macro_rules! expect_success { }; } +#[cfg(unix)] +unsafe fn setup_custom_tty() -> (std::process::Stdio, std::process::Stdio) { + use std::os::fd::FromRawFd; + + // Open a new PTY master device + let master_fd = libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY); + if master_fd == -1 { + panic!("{}", std::io::Error::last_os_error()); + } + + // Grant access to the slave PTY + if unsafe { libc::grantpt(master_fd) } < 0 { + panic!("{}", std::io::Error::last_os_error()); + } + + // Unlock the slave PTY + if unsafe { libc::unlockpt(master_fd) } < 0 { + panic!("{}", std::io::Error::last_os_error()); + } + + // Get the path to the slave PTY + let slave_path_ptr = unsafe { libc::ptsname(master_fd) }; + if slave_path_ptr.is_null() { + panic!("{}", std::io::Error::last_os_error()); + } + + // Open the slave PTY + let slave_fd = unsafe { libc::open(slave_path_ptr, libc::O_RDWR) }; + if slave_fd < 0 { + panic!("{}", std::io::Error::last_os_error()); + } + + unsafe { (Stdio::from_raw_fd(master_fd), Stdio::from_raw_fd(slave_fd)) } +} + fn command() -> Command { match env::var("RUNNER") { Ok(runner) if !runner.is_empty() => { @@ -867,6 +902,22 @@ mod decompress_command { ),), ); } + + #[test] + #[cfg(unix)] + fn stdin_is_terminal() { + let mut cmd = command(); + + let (_master, tty) = unsafe { setup_custom_tty() }; + + expect_failure!( + cmd.arg("-d").arg("-c").stdin(tty), + concat!( + "bzip2: I won't read compressed data from a terminal.\n", + "bzip2: For help, type: `bzip2 --help'.\n", + ), + ); + } } mod test_command { @@ -1041,6 +1092,22 @@ mod test_command { ), ); } + + #[test] + #[cfg(unix)] + fn stdin_is_terminal() { + let mut cmd = command(); + + let (_master, tty) = unsafe { setup_custom_tty() }; + + expect_failure!( + cmd.arg("-t").stdin(tty), + concat!( + "bzip2: I won't read compressed data from a terminal.\n", + "bzip2: For help, type: `bzip2 --help'.\n", + ), + ); + } } mod compress_command { @@ -1481,4 +1548,84 @@ mod compress_command { ), ); } + + #[test] + fn stdout_is_terminal_i2o() { + let mut cmd = command(); + + let (_master, tty) = unsafe { setup_custom_tty() }; + + expect_failure!( + cmd.arg("-z").arg("-c").stdin(Stdio::piped()).stdout(tty), + concat!( + "bzip2: I won't write compressed data to a terminal.\n", + "bzip2: For help, type: `bzip2 --help'.\n", + ), + ); + } + + #[test] + #[cfg(unix)] + fn stdout_is_terminal_f2o() { + let mut cmd = command(); + + let tmpdir = tempfile::tempdir().unwrap(); + + let sample1_ref = tmpdir.path().join("sample1.ref"); + std::fs::copy("tests/input/quick/sample1.ref", &sample1_ref).unwrap(); + + let (_master, tty) = unsafe { setup_custom_tty() }; + + expect_failure!( + cmd.arg("-z") + .arg("-c") + .arg("-k") + .arg(&sample1_ref) + .stdout(tty), + concat!( + "bzip2: I won't write compressed data to a terminal.\n", + "bzip2: For help, type: `bzip2 --help'.\n", + ), + ); + + // the `-k` flag should keep the input file + assert!(sample1_ref.exists()); + } + + #[test] + #[cfg(unix)] + fn io_error() { + let mut cmd = command(); + + let tmpdir = tempfile::tempdir().unwrap(); + + let sample1_ref = tmpdir.path().join("sample1.ref"); + std::fs::copy("tests/input/quick/sample1.ref", &sample1_ref).unwrap(); + + let (master, tty) = unsafe { setup_custom_tty() }; + + // dropping here triggers an IO error down the line + drop(master); + + expect_failure!( + cmd.arg("-z") + .arg("-c") + .arg("-k") + .arg(&sample1_ref) + .stdout(tty), + format!( + concat!( + "\n", + "bzip2: I/O or other error, bailing out. Possible reason follows.\n", + "bzip2: Input/output error\n", + "\tInput file = {in_file}, output file = (stdout)\n", + "" + ), + in_file = sample1_ref.display(), + ) + ); + + // the `-k` flag should keep the input file + assert!(sample1_ref.exists()); + } } From 963bd49f88cb695ed4a886dd44f4f3ffe13d9369 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 12 Nov 2024 11:06:15 +0100 Subject: [PATCH 2/2] `bzip2.rs`: replace `isatty` with rust `std::io::IsTerminal` --- bzip2.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bzip2.rs b/bzip2.rs index 9413a2b4b..1e59cd9b2 100644 --- a/bzip2.rs +++ b/bzip2.rs @@ -3,6 +3,7 @@ #![allow(non_upper_case_globals)] use std::ffi::{c_char, CStr, CString, OsStr}; +use std::io::IsTerminal; use std::mem::zeroed; use std::path::{Path, PathBuf}; use std::ptr; @@ -14,8 +15,8 @@ use libbzip2_rs_sys::{ use libc::{ _exit, close, exit, fclose, fdopen, ferror, fflush, fgetc, fileno, fopen, fprintf, fread, - fwrite, isatty, open, perror, remove, rewind, signal, stat, strcat, strcmp, strlen, strncpy, - ungetc, utimbuf, write, FILE, + fwrite, open, perror, remove, rewind, signal, stat, strcat, strcmp, strlen, strncpy, ungetc, + utimbuf, write, FILE, }; extern "C" { static mut stdin: *mut FILE; @@ -1219,7 +1220,7 @@ unsafe fn compress(name: *mut c_char) { SourceMode::I2O => { inStr = stdin; outStr = stdout; - if isatty(fileno(stdout)) != 0 { + if std::io::stdout().is_terminal() { fprintf( stderr, b"%s: I won't write compressed data to a terminal.\n\0" as *const u8 @@ -1242,7 +1243,7 @@ unsafe fn compress(name: *mut c_char) { b"rb\0" as *const u8 as *const libc::c_char, ); outStr = stdout; - if isatty(fileno(stdout)) != 0 { + if std::io::stdout().is_terminal() { fprintf( stderr, b"%s: I won't write compressed data to a terminal.\n\0" as *const u8 @@ -1494,7 +1495,7 @@ unsafe fn uncompress(name: Option) { SourceMode::I2O => { inStr = stdin; outStr = stdout; - if isatty(fileno(stdin)) != 0 { + if std::io::stdin().is_terminal() { eprint!( concat!( "{program_name}: I won't read compressed data from a terminal.\n", @@ -1688,7 +1689,7 @@ unsafe fn testf(name: Option) { } match srcMode { SourceMode::I2O => { - if isatty(fileno(stdin)) != 0 { + if std::io::stdin().is_terminal() { eprintln!( "{}: I won't read compressed data from a terminal.", get_program_name().display(),