Skip to content

Commit 2a8da35

Browse files
committed
use is_terminal to implement isatty
1 parent c426bc8 commit 2a8da35

File tree

3 files changed

+42
-29
lines changed

3 files changed

+42
-29
lines changed

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#![feature(is_some_and)]
1010
#![feature(nonzero_ops)]
1111
#![feature(local_key_cell_methods)]
12+
#![feature(is_terminal)]
1213
// Configure clippy and other lints
1314
#![allow(
1415
clippy::collapsible_else_if,

src/shims/unix/foreign_items.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
452452
"isatty" => {
453453
let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
454454
let result = this.isatty(fd)?;
455-
this.write_scalar(Scalar::from_i32(result), dest)?;
455+
this.write_scalar(result, dest)?;
456456
}
457457
"pthread_atfork" => {
458458
let [prepare, parent, child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

src/shims/unix/fs.rs

+40-28
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::convert::TryInto;
44
use std::fs::{
55
read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir,
66
};
7-
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
7+
use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write};
88
use std::path::{Path, PathBuf};
99
use std::time::SystemTime;
1010

@@ -65,6 +65,8 @@ trait FileDescriptor: std::fmt::Debug {
6565

6666
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>>;
6767

68+
fn is_tty(&self) -> bool;
69+
6870
#[cfg(unix)]
6971
fn as_unix_host_fd(&self) -> Option<i32> {
7072
None
@@ -143,6 +145,10 @@ impl FileDescriptor for FileHandle {
143145
use std::os::unix::io::AsRawFd;
144146
Some(self.file.as_raw_fd())
145147
}
148+
149+
fn is_tty(&self) -> bool {
150+
self.file.is_terminal()
151+
}
146152
}
147153

148154
impl FileDescriptor for io::Stdin {
@@ -170,6 +176,10 @@ impl FileDescriptor for io::Stdin {
170176
fn as_unix_host_fd(&self) -> Option<i32> {
171177
Some(libc::STDIN_FILENO)
172178
}
179+
180+
fn is_tty(&self) -> bool {
181+
self.is_terminal()
182+
}
173183
}
174184

175185
impl FileDescriptor for io::Stdout {
@@ -202,6 +212,10 @@ impl FileDescriptor for io::Stdout {
202212
fn as_unix_host_fd(&self) -> Option<i32> {
203213
Some(libc::STDOUT_FILENO)
204214
}
215+
216+
fn is_tty(&self) -> bool {
217+
self.is_terminal()
218+
}
205219
}
206220

207221
impl FileDescriptor for io::Stderr {
@@ -227,12 +241,16 @@ impl FileDescriptor for io::Stderr {
227241
fn as_unix_host_fd(&self) -> Option<i32> {
228242
Some(libc::STDERR_FILENO)
229243
}
244+
245+
fn is_tty(&self) -> bool {
246+
self.is_terminal()
247+
}
230248
}
231249

232250
#[derive(Debug)]
233-
struct DummyOutput;
251+
struct NullOutput;
234252

235-
impl FileDescriptor for DummyOutput {
253+
impl FileDescriptor for NullOutput {
236254
fn name(&self) -> &'static str {
237255
"stderr and stdout"
238256
}
@@ -247,7 +265,11 @@ impl FileDescriptor for DummyOutput {
247265
}
248266

249267
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
250-
Ok(Box::new(DummyOutput))
268+
Ok(Box::new(NullOutput))
269+
}
270+
271+
fn is_tty(&self) -> bool {
272+
false
251273
}
252274
}
253275

@@ -267,8 +289,8 @@ impl FileHandler {
267289
let mut handles: BTreeMap<_, Box<dyn FileDescriptor>> = BTreeMap::new();
268290
handles.insert(0i32, Box::new(io::stdin()));
269291
if mute_stdout_stderr {
270-
handles.insert(1i32, Box::new(DummyOutput));
271-
handles.insert(2i32, Box::new(DummyOutput));
292+
handles.insert(1i32, Box::new(NullOutput));
293+
handles.insert(2i32, Box::new(NullOutput));
272294
} else {
273295
handles.insert(1i32, Box::new(io::stdout()));
274296
handles.insert(2i32, Box::new(io::stderr()));
@@ -1662,35 +1684,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
16621684
}
16631685

16641686
#[cfg_attr(not(unix), allow(unused))]
1665-
fn isatty(&mut self, miri_fd: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
1687+
fn isatty(
1688+
&mut self,
1689+
miri_fd: &OpTy<'tcx, Provenance>,
1690+
) -> InterpResult<'tcx, Scalar<Provenance>> {
16661691
let this = self.eval_context_mut();
1667-
#[cfg(unix)]
1692+
// "returns 1 if fd is an open file descriptor referring to a terminal;
1693+
// otherwise 0 is returned, and errno is set to indicate the error"
16681694
if matches!(this.machine.isolated_op, IsolatedOp::Allow) {
1669-
let miri_fd = this.read_scalar(miri_fd)?.to_i32()?;
1670-
if let Some(host_fd) =
1671-
this.machine.file_handler.handles.get(&miri_fd).and_then(|fd| fd.as_unix_host_fd())
1672-
{
1673-
// "returns 1 if fd is an open file descriptor referring to a terminal;
1674-
// otherwise 0 is returned, and errno is set to indicate the error"
1675-
// SAFETY: isatty has no preconditions
1676-
let is_tty = unsafe { libc::isatty(host_fd) };
1677-
if is_tty == 0 {
1678-
let errno = std::io::Error::last_os_error()
1679-
.raw_os_error()
1680-
.map(Scalar::from_i32)
1681-
.unwrap();
1682-
this.set_last_error(errno)?;
1695+
let fd = this.read_scalar(miri_fd)?.to_i32()?;
1696+
if let Some(is_tty) = this.machine.file_handler.handles.get(&fd).map(|fd| fd.is_tty()) {
1697+
if is_tty {
1698+
return Ok(Scalar::from_i32(1));
16831699
}
1684-
return Ok(is_tty);
16851700
}
16861701
}
1687-
// We are attemping to use a Unix interface on a non-Unix platform, or we are on a Unix
1688-
// platform and the passed file descriptor is not open, or isolation is enabled
1689-
// FIXME: It should be possible to emulate this at least on Windows by using
1690-
// GetConsoleMode.
1702+
// Fallback when the FD was not found or isolation is enabled.
16911703
let enotty = this.eval_libc("ENOTTY")?;
16921704
this.set_last_error(enotty)?;
1693-
Ok(0)
1705+
Ok(Scalar::from_i32(0))
16941706
}
16951707

16961708
fn realpath(

0 commit comments

Comments
 (0)