mirror of
https://github.com/uutils/coreutils
synced 2024-12-13 14:52:41 +00:00
stty: print special terminal information
This commit is contained in:
parent
cc147a7c8d
commit
f861fc0854
3 changed files with 214 additions and 18 deletions
|
@ -9,7 +9,9 @@
|
|||
// spell-checker:ignore isig icanon iexten echoe crterase echok echonl noflsh xcase tostop echoprt prterase echoctl ctlecho echoke crtkill flusho extproc
|
||||
|
||||
use crate::Flag;
|
||||
use nix::sys::termios::{ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O};
|
||||
use nix::sys::termios::{
|
||||
BaudRate, ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O,
|
||||
};
|
||||
|
||||
pub const CONTROL_FLAGS: [Flag<C>; 12] = [
|
||||
Flag::new("parenb", C::PARENB),
|
||||
|
@ -19,7 +21,7 @@ pub const CONTROL_FLAGS: [Flag<C>; 12] = [
|
|||
Flag::new("cs6", C::CS6).group(C::CSIZE),
|
||||
Flag::new("cs7", C::CS7).group(C::CSIZE),
|
||||
Flag::new("cs8", C::CS8).group(C::CSIZE).sane(),
|
||||
Flag::new("hupcl", C::HUPCL).sane(),
|
||||
Flag::new("hupcl", C::HUPCL),
|
||||
Flag::new("cstopb", C::CSTOPB),
|
||||
Flag::new("cread", C::CREAD).sane(),
|
||||
Flag::new("clocal", C::CLOCAL),
|
||||
|
@ -95,3 +97,103 @@ pub const LOCAL_FLAGS: [Flag<L>; 18] = [
|
|||
Flag::new("flusho", L::FLUSHO),
|
||||
Flag::new("extproc", L::EXTPROC),
|
||||
];
|
||||
|
||||
pub const BAUD_RATES: &[(&str, BaudRate)] = &[
|
||||
("0", BaudRate::B0),
|
||||
("50", BaudRate::B50),
|
||||
("75", BaudRate::B75),
|
||||
("110", BaudRate::B110),
|
||||
("134", BaudRate::B134),
|
||||
("150", BaudRate::B150),
|
||||
("200", BaudRate::B200),
|
||||
("300", BaudRate::B300),
|
||||
("600", BaudRate::B600),
|
||||
("1200", BaudRate::B1200),
|
||||
("1800", BaudRate::B1800),
|
||||
("2400", BaudRate::B2400),
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
("4800", BaudRate::B4800),
|
||||
("9600", BaudRate::B9600),
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
("14400", BaudRate::B14400),
|
||||
("19200", BaudRate::B19200),
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
("28800", BaudRate::B28800),
|
||||
("38400", BaudRate::B38400),
|
||||
("57600", BaudRate::B57600),
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
("76800", BaudRate::B76800),
|
||||
("115200", BaudRate::B115200),
|
||||
("230400", BaudRate::B230400),
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
("460800", BaudRate::B460800),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("500000", BaudRate::B500000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("576000", BaudRate::B576000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd"
|
||||
))]
|
||||
("921600", BaudRate::B921600),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("1000000", BaudRate::B1000000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("1152000", BaudRate::B1152000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("1500000", BaudRate::B1500000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("2000000", BaudRate::B2000000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("2500000", BaudRate::B2500000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("3000000", BaudRate::B3000000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("3500000", BaudRate::B3500000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("4000000", BaudRate::B4000000),
|
||||
];
|
||||
|
|
|
@ -3,21 +3,23 @@
|
|||
// * For the full copyright and license information, please view the LICENSE file
|
||||
// * that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore tcgetattr tcsetattr tcsanow
|
||||
// spell-checker:ignore tcgetattr tcsetattr tcsanow tiocgwinsz tiocswinsz cfgetospeed ushort
|
||||
|
||||
mod flags;
|
||||
|
||||
use clap::{crate_version, Arg, ArgMatches, Command};
|
||||
use nix::libc::{c_ushort, TIOCGWINSZ, TIOCSWINSZ};
|
||||
use nix::sys::termios::{
|
||||
tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, Termios,
|
||||
cfgetospeed, tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, Termios,
|
||||
};
|
||||
use nix::{ioctl_read_bad, ioctl_write_ptr_bad};
|
||||
use std::io::{self, stdout};
|
||||
use std::ops::ControlFlow;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
use uucore::{format_usage, InvalidEncodingHandling};
|
||||
|
||||
use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS};
|
||||
use flags::{BAUD_RATES, CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS};
|
||||
|
||||
const NAME: &str = "stty";
|
||||
const USAGE: &str = "\
|
||||
|
@ -104,6 +106,30 @@ impl<'a> Options<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// Needs to be repr(C) because we pass it to the ioctl calls.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TermSize {
|
||||
rows: c_ushort,
|
||||
columns: c_ushort,
|
||||
x: c_ushort,
|
||||
y: c_ushort,
|
||||
}
|
||||
|
||||
ioctl_read_bad!(
|
||||
/// Get terminal window size
|
||||
tiocgwinsz,
|
||||
TIOCGWINSZ,
|
||||
TermSize
|
||||
);
|
||||
|
||||
ioctl_write_ptr_bad!(
|
||||
/// Set terminal window size
|
||||
tiocswinsz,
|
||||
TIOCSWINSZ,
|
||||
TermSize
|
||||
);
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let args = args
|
||||
|
@ -118,17 +144,24 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
}
|
||||
|
||||
fn stty(opts: &Options) -> UResult<()> {
|
||||
// TODO: Figure out the right error message
|
||||
if opts.save && opts.all {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
"the options for verbose and stty-readable output styles are mutually exclusive",
|
||||
));
|
||||
}
|
||||
|
||||
if opts.settings.is_some() && (opts.save || opts.all) {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
"when specifying an output style, modes may not be set",
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: Figure out the right error message for when tcgetattr fails
|
||||
let mut termios = tcgetattr(opts.file).expect("Could not get terminal attributes");
|
||||
|
||||
if let Some(settings) = &opts.settings {
|
||||
if opts.save || opts.all {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
"when specifying an output style, modes may not be set",
|
||||
));
|
||||
}
|
||||
|
||||
for setting in settings {
|
||||
if let ControlFlow::Break(false) = apply_setting(&mut termios, setting) {
|
||||
return Err(USimpleError::new(
|
||||
|
@ -141,16 +174,42 @@ fn stty(opts: &Options) -> UResult<()> {
|
|||
tcsetattr(opts.file, nix::sys::termios::SetArg::TCSANOW, &termios)
|
||||
.expect("Could not write terminal attributes");
|
||||
} else {
|
||||
print_settings(&termios, opts);
|
||||
print_settings(&termios, opts).expect("TODO: make proper error here from nix error");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_settings(termios: &Termios, opts: &Options) {
|
||||
fn print_terminal_size(termios: &Termios, opts: &Options) -> nix::Result<()> {
|
||||
let speed = cfgetospeed(termios);
|
||||
for (text, baud_rate) in BAUD_RATES {
|
||||
if *baud_rate == speed {
|
||||
print!("speed {} baud; ", text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if opts.all {
|
||||
let mut size = TermSize::default();
|
||||
unsafe { tiocgwinsz(opts.file, &mut size as *mut _)? };
|
||||
print!("rows {}; columns {}; ", size.rows, size.columns);
|
||||
}
|
||||
|
||||
// For some reason the normal nix Termios struct does not expose the line,
|
||||
// so we get the underlying libc::termios struct to get that information.
|
||||
let libc_termios: nix::libc::termios = termios.clone().into();
|
||||
let line = libc_termios.c_line;
|
||||
print!("line = {};", line);
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_settings(termios: &Termios, opts: &Options) -> nix::Result<()> {
|
||||
print_terminal_size(termios, opts)?;
|
||||
print_flags(termios, opts, &CONTROL_FLAGS);
|
||||
print_flags(termios, opts, &INPUT_FLAGS);
|
||||
print_flags(termios, opts, &OUTPUT_FLAGS);
|
||||
print_flags(termios, opts, &LOCAL_FLAGS);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_flags<T: TermiosFlag>(termios: &Termios, opts: &Options, flags: &[Flag<T>]) {
|
||||
|
@ -169,14 +228,14 @@ fn print_flags<T: TermiosFlag>(termios: &Termios, opts: &Options, flags: &[Flag<
|
|||
let val = flag.is_in(termios, group);
|
||||
if group.is_some() {
|
||||
if val && (!sane || opts.all) {
|
||||
print!("{name} ");
|
||||
print!("{} ", name);
|
||||
printed = true;
|
||||
}
|
||||
} else if opts.all || val != sane {
|
||||
if !val {
|
||||
print!("-");
|
||||
}
|
||||
print!("{name} ");
|
||||
print!("{} ", name);
|
||||
printed = true;
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +317,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_name("DEVICE")
|
||||
.help("open and use the specified DEVICE instead of stdin")
|
||||
.help("open and use the specified DEVICE instead of stdin"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::SETTINGS)
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
use crate::common::util::*;
|
||||
|
||||
#[test]
|
||||
#[ignore = "Fails because cargo test does not run in a tty"]
|
||||
fn runs() {
|
||||
new_ucmd!().succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "Fails because cargo test does not run in a tty"]
|
||||
fn print_all() {
|
||||
let res = new_ucmd!().succeeds();
|
||||
|
||||
|
@ -18,3 +20,36 @@ fn print_all() {
|
|||
res.stdout_contains(flag);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_and_setting() {
|
||||
new_ucmd!()
|
||||
.args(&["--save", "nl0"])
|
||||
.fails()
|
||||
.stderr_contains("when specifying an output style, modes may not be set");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_and_setting() {
|
||||
new_ucmd!()
|
||||
.args(&["--all", "nl0"])
|
||||
.fails()
|
||||
.stderr_contains("when specifying an output style, modes may not be set");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_and_all() {
|
||||
new_ucmd!()
|
||||
.args(&["--save", "--all"])
|
||||
.fails()
|
||||
.stderr_contains(
|
||||
"the options for verbose and stty-readable output styles are mutually exclusive",
|
||||
);
|
||||
|
||||
new_ucmd!()
|
||||
.args(&["--all", "--save"])
|
||||
.fails()
|
||||
.stderr_contains(
|
||||
"the options for verbose and stty-readable output styles are mutually exclusive",
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue