From 94f54147b0bc6e3f9f1449ff678cd7409789f46d Mon Sep 17 00:00:00 2001 From: Jakub Kotur Date: Mon, 21 Dec 2020 17:28:14 +0100 Subject: Initial import of atty-0.2.14 Bug: 155309706 Change-Id: Iebc2b51299744581f1f098359534b286a36a0a79 --- .cargo_vcs_info.json | 5 ++ .gitignore | 3 + CHANGELOG.md | 73 ++++++++++++++++++ Cargo.toml | 34 +++++++++ Cargo.toml.orig | 26 +++++++ README.md | 74 ++++++++++++++++++ examples/atty.rs | 9 +++ rustfmt.toml | 4 + src/lib.rs | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 438 insertions(+) create mode 100644 .cargo_vcs_info.json create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 100644 README.md create mode 100644 examples/atty.rs create mode 100644 rustfmt.toml create mode 100644 src/lib.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..b409616 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "7b5df17888997d57c2c1c8f91da1db5691f49953" + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afc9901 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target +Cargo.lock +*.bk diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4e9673f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,73 @@ +# 0.2.14 + +* add support for [RustyHermit](https://github.com/hermitcore/libhermit-rs), a Rust-based unikernel [#41](https://github.com/softprops/atty/pull/41) + +# 0.2.13 + +* support older versions of rust that do now support 2018 edition + +# 0.2.12 + +* Redox is now in the unix family so redox cfg is no longer needed [#35](https://github.com/softprops/atty/pull/35) + +# 0.2.11 + +* fix msys detection with `winapi@0.3.5` [#28](https://github.com/softprops/atty/pull/28) + +# 0.2.10 + +* fix wasm regression [#27](https://github.com/softprops/atty/pull/27) + +# 0.2.9 + +* Fix fix pty detection [#25](https://github.com/softprops/atty/pull/25) + +# 0.2.8 + +* Fix an inverted condition on MinGW [#22](https://github.com/softprops/atty/pull/22) + +# 0.2.7 + +* Change `||` to `&&` for whether MSYS is a tty [#24](https://github.com/softprops/atty/pull/24/) + +# 0.2.6 + +* updated winapi dependency to [0.3](https://retep998.github.io/blog/winapi-0.3/) [#18](https://github.com/softprops/atty/pull/18) + +# 0.2.5 + +* added support for Wasm compile targets [#17](https://github.com/softprops/atty/pull/17) + +# 0.2.4 + +* added support for Wasm compile targets [#17](https://github.com/softprops/atty/pull/17) + +# 0.2.3 + +* added support for Redox OS [#14](https://github.com/softprops/atty/pull/14) + +# 0.2.2 + +* use target specific dependencies [#11](https://github.com/softprops/atty/pull/11) +* Add tty detection for MSYS terminals [#12](https://github.com/softprops/atty/pull/12) + +# 0.2.1 + +* fix windows bug + +# 0.2.0 + +* support for various stream types + +# 0.1.2 + +* windows support (with automated testing) +* automated code coverage + +# 0.1.1 + +* bumped libc dep from `0.1` to `0.2` + +# 0.1.0 + +* initial release diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d6bf2d0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,34 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "atty" +version = "0.2.14" +authors = ["softprops "] +exclude = ["/.travis.yml", "/appveyor.yml"] +description = "A simple interface for querying atty" +homepage = "https://github.com/softprops/atty" +documentation = "http://softprops.github.io/atty" +readme = "README.md" +keywords = ["terminal", "tty", "isatty"] +license = "MIT" +repository = "https://github.com/softprops/atty" +[target."cfg(target_os = \"hermit\")".dependencies.hermit-abi] +version = "0.1.6" +[target."cfg(unix)".dependencies.libc] +version = "0.2" +default-features = false +[target."cfg(windows)".dependencies.winapi] +version = "0.3" +features = ["consoleapi", "processenv", "minwinbase", "minwindef", "winbase"] +[badges.travis-ci] +repository = "softprops/atty" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..aa2cbb7 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,26 @@ +[package] +name = "atty" +version = "0.2.14" +authors = ["softprops "] +description = "A simple interface for querying atty" +documentation = "http://softprops.github.io/atty" +homepage = "https://github.com/softprops/atty" +repository = "https://github.com/softprops/atty" +keywords = ["terminal", "tty", "isatty"] +license = "MIT" +readme = "README.md" +exclude = ["/.travis.yml", "/appveyor.yml"] + +[badges] +travis-ci = { repository = "softprops/atty" } + +[target.'cfg(unix)'.dependencies] +libc = { version = "0.2", default-features = false } + +[target.'cfg(target_os = "hermit")'.dependencies] +hermit-abi = "0.1.6" + +[target.'cfg(windows)'.dependencies.winapi] +version = "0.3" +features = ["consoleapi", "processenv", "minwinbase", "minwindef", "winbase"] + diff --git a/README.md b/README.md new file mode 100644 index 0000000..cd85593 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# atty + +[![Build Status](https://travis-ci.org/softprops/atty.svg?branch=master)](https://travis-ci.org/softprops/atty) [![Build status](https://ci.appveyor.com/api/projects/status/geggrsnsjsuse8cv?svg=true)](https://ci.appveyor.com/project/softprops/atty) [![Coverage Status](https://coveralls.io/repos/softprops/atty/badge.svg?branch=master&service=github)](https://coveralls.io/github/softprops/atty?branch=master) [![crates.io](https://img.shields.io/crates/v/atty.svg)](https://crates.io/crates/atty) [![Released API docs](https://docs.rs/atty/badge.svg)](http://docs.rs/atty) [![Master API docs](https://img.shields.io/badge/docs-master-green.svg)](https://softprops.github.io/atty) + +> are you or are you not a tty? + + +## install + +Add the following to your `Cargo.toml` + +```toml +[dependencies] +atty = "0.2" +``` + +## usage + +```rust +use atty::Stream; + +fn main() { + if atty::is(Stream::Stdout) { + println!("I'm a terminal"); + } else { + println!("I'm not"); + } +} +``` + +## testing + +This library has been unit tested on both unix and windows platforms (via appveyor). + + +A simple example program is provided in this repo to test various tty's. By default. + +It prints + +```bash +$ cargo run --example atty +stdout? true +stderr? true +stdin? true +``` + +To test std in, pipe some text to the program + +```bash +$ echo "test" | cargo run --example atty +stdout? true +stderr? true +stdin? false +``` + +To test std out, pipe the program to something + +```bash +$ cargo run --example atty | grep std +stdout? false +stderr? true +stdin? true +``` + +To test std err, pipe the program to something redirecting std err + +```bash +$ cargo run --example atty 2>&1 | grep std +stdout? false +stderr? false +stdin? true +``` + +Doug Tangren (softprops) 2015-2019 diff --git a/examples/atty.rs b/examples/atty.rs new file mode 100644 index 0000000..3b3635e --- /dev/null +++ b/examples/atty.rs @@ -0,0 +1,9 @@ +extern crate atty; + +use atty::{is, Stream}; + +fn main() { + println!("stdout? {}", is(Stream::Stdout)); + println!("stderr? {}", is(Stream::Stderr)); + println!("stdin? {}", is(Stream::Stdin)); +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..899a094 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +# https://github.com/rust-lang/rustfmt/blob/master/Configurations.md#fn_args_layout +fn_args_layout = "Vertical" +# https://github.com/rust-lang/rustfmt/blob/master/Configurations.md#merge_imports +merge_imports = true \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..501cad6 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,210 @@ +//! atty is a simple utility that answers one question +//! > is this a tty? +//! +//! usage is just as simple +//! +//! ``` +//! if atty::is(atty::Stream::Stdout) { +//! println!("i'm a tty") +//! } +//! ``` +//! +//! ``` +//! if atty::isnt(atty::Stream::Stdout) { +//! println!("i'm not a tty") +//! } +//! ``` + +#![cfg_attr(unix, no_std)] + +#[cfg(unix)] +extern crate libc; +#[cfg(windows)] +extern crate winapi; + +#[cfg(windows)] +use winapi::shared::minwindef::DWORD; +#[cfg(windows)] +use winapi::shared::ntdef::WCHAR; + +/// possible stream sources +#[derive(Clone, Copy, Debug)] +pub enum Stream { + Stdout, + Stderr, + Stdin, +} + +/// returns true if this is a tty +#[cfg(all(unix, not(target_arch = "wasm32")))] +pub fn is(stream: Stream) -> bool { + extern crate libc; + + let fd = match stream { + Stream::Stdout => libc::STDOUT_FILENO, + Stream::Stderr => libc::STDERR_FILENO, + Stream::Stdin => libc::STDIN_FILENO, + }; + unsafe { libc::isatty(fd) != 0 } +} + +/// returns true if this is a tty +#[cfg(target_os = "hermit")] +pub fn is(stream: Stream) -> bool { + extern crate hermit_abi; + + let fd = match stream { + Stream::Stdout => hermit_abi::STDOUT_FILENO, + Stream::Stderr => hermit_abi::STDERR_FILENO, + Stream::Stdin => hermit_abi::STDIN_FILENO, + }; + hermit_abi::isatty(fd) +} + +/// returns true if this is a tty +#[cfg(windows)] +pub fn is(stream: Stream) -> bool { + use winapi::um::winbase::{ + STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT, + STD_OUTPUT_HANDLE as STD_OUTPUT, + }; + + let (fd, others) = match stream { + Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]), + Stream::Stderr => (STD_ERROR, [STD_INPUT, STD_OUTPUT]), + Stream::Stdout => (STD_OUTPUT, [STD_INPUT, STD_ERROR]), + }; + if unsafe { console_on_any(&[fd]) } { + // False positives aren't possible. If we got a console then + // we definitely have a tty on stdin. + return true; + } + + // At this point, we *could* have a false negative. We can determine that + // this is true negative if we can detect the presence of a console on + // any of the other streams. If another stream has a console, then we know + // we're in a Windows console and can therefore trust the negative. + if unsafe { console_on_any(&others) } { + return false; + } + + // Otherwise, we fall back to a very strange msys hack to see if we can + // sneakily detect the presence of a tty. + unsafe { msys_tty_on(fd) } +} + +/// returns true if this is _not_ a tty +pub fn isnt(stream: Stream) -> bool { + !is(stream) +} + +/// Returns true if any of the given fds are on a console. +#[cfg(windows)] +unsafe fn console_on_any(fds: &[DWORD]) -> bool { + use winapi::um::{consoleapi::GetConsoleMode, processenv::GetStdHandle}; + + for &fd in fds { + let mut out = 0; + let handle = GetStdHandle(fd); + if GetConsoleMode(handle, &mut out) != 0 { + return true; + } + } + false +} + +/// Returns true if there is an MSYS tty on the given handle. +#[cfg(windows)] +unsafe fn msys_tty_on(fd: DWORD) -> bool { + use std::{mem, slice}; + + use winapi::{ + ctypes::c_void, + shared::minwindef::MAX_PATH, + um::{ + fileapi::FILE_NAME_INFO, minwinbase::FileNameInfo, processenv::GetStdHandle, + winbase::GetFileInformationByHandleEx, + }, + }; + + let size = mem::size_of::(); + let mut name_info_bytes = vec![0u8; size + MAX_PATH * mem::size_of::()]; + let res = GetFileInformationByHandleEx( + GetStdHandle(fd), + FileNameInfo, + &mut *name_info_bytes as *mut _ as *mut c_void, + name_info_bytes.len() as u32, + ); + if res == 0 { + return false; + } + let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO); + let s = slice::from_raw_parts( + name_info.FileName.as_ptr(), + name_info.FileNameLength as usize / 2, + ); + let name = String::from_utf16_lossy(s); + // This checks whether 'pty' exists in the file name, which indicates that + // a pseudo-terminal is attached. To mitigate against false positives + // (e.g., an actual file name that contains 'pty'), we also require that + // either the strings 'msys-' or 'cygwin-' are in the file name as well.) + let is_msys = name.contains("msys-") || name.contains("cygwin-"); + let is_pty = name.contains("-pty"); + is_msys && is_pty +} + +/// returns true if this is a tty +#[cfg(target_arch = "wasm32")] +pub fn is(_stream: Stream) -> bool { + false +} + +#[cfg(test)] +mod tests { + use super::{is, Stream}; + + #[test] + #[cfg(windows)] + fn is_err() { + // appveyor pipes its output + assert!(!is(Stream::Stderr)) + } + + #[test] + #[cfg(windows)] + fn is_out() { + // appveyor pipes its output + assert!(!is(Stream::Stdout)) + } + + #[test] + #[cfg(windows)] + fn is_in() { + assert!(is(Stream::Stdin)) + } + + #[test] + #[cfg(unix)] + fn is_err() { + assert!(is(Stream::Stderr)) + } + + #[test] + #[cfg(unix)] + fn is_out() { + assert!(is(Stream::Stdout)) + } + + #[test] + #[cfg(target_os = "macos")] + fn is_in() { + // macos on travis seems to pipe its input + assert!(is(Stream::Stdin)) + } + + #[test] + #[cfg(all(not(target_os = "macos"), unix))] + fn is_in() { + assert!(is(Stream::Stdin)) + } +} -- cgit v1.2.3