From 600cab0bd8e6ed55e7f0ab091796db098063197f Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Fri, 24 Jun 2022 20:01:02 +0200 Subject: [PATCH 01/17] starting work on stty --- Cargo.lock | 10 ++ Cargo.toml | 2 + src/uu/stty/Cargo.toml | 24 +++ src/uu/stty/LICENSE | 1 + src/uu/stty/src/flags.rs | 332 +++++++++++++++++++++++++++++++++++++++ src/uu/stty/src/main.rs | 1 + src/uu/stty/src/stty.rs | 240 ++++++++++++++++++++++++++++ 7 files changed, 610 insertions(+) create mode 100644 src/uu/stty/Cargo.toml create mode 120000 src/uu/stty/LICENSE create mode 100644 src/uu/stty/src/flags.rs create mode 100644 src/uu/stty/src/main.rs create mode 100644 src/uu/stty/src/stty.rs diff --git a/Cargo.lock b/Cargo.lock index ba035a72c..3c37e0ce6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,6 +445,7 @@ dependencies = [ "uu_split", "uu_stat", "uu_stdbuf", + "uu_stty", "uu_sum", "uu_sync", "uu_tac", @@ -2908,6 +2909,15 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_stty" +version = "0.0.14" +dependencies = [ + "clap 3.1.18", + "nix", + "uucore", +] + [[package]] name = "uu_sum" version = "0.0.14" diff --git a/Cargo.toml b/Cargo.toml index 134f73925..4d71d2c51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -190,6 +190,7 @@ feat_require_unix = [ "nohup", "pathchk", "stat", + "stty", "timeout", "tty", "uname", @@ -348,6 +349,7 @@ sort = { optional=true, version="0.0.14", package="uu_sort", path="src/uu/so split = { optional=true, version="0.0.14", package="uu_split", path="src/uu/split" } stat = { optional=true, version="0.0.14", package="uu_stat", path="src/uu/stat" } stdbuf = { optional=true, version="0.0.14", package="uu_stdbuf", path="src/uu/stdbuf" } +stty = { optional=true, version="0.0.14", package="uu_stty", path="src/uu/stty" } sum = { optional=true, version="0.0.14", package="uu_sum", path="src/uu/sum" } sync = { optional=true, version="0.0.14", package="uu_sync", path="src/uu/sync" } tac = { optional=true, version="0.0.14", package="uu_tac", path="src/uu/tac" } diff --git a/src/uu/stty/Cargo.toml b/src/uu/stty/Cargo.toml new file mode 100644 index 000000000..d39786494 --- /dev/null +++ b/src/uu/stty/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "uu_stty" +version = "0.0.14" +authors = ["uutils developers"] +license = "MIT" +description = "stty ~ (uutils) print or change terminal characteristics" + +homepage = "https://github.com/uutils/coreutils" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/stty" +keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] +categories = ["command-line-utilities"] +edition = "2021" + +[lib] +path = "src/stty.rs" + +[dependencies] +clap = { version = "3.1", features = ["wrap_help", "cargo"] } +uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } +nix = { version="0.24.1", features = ["term"] } + +[[bin]] +name = "stty" +path = "src/main.rs" diff --git a/src/uu/stty/LICENSE b/src/uu/stty/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/stty/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/stty/src/flags.rs b/src/uu/stty/src/flags.rs new file mode 100644 index 000000000..b2688cb8d --- /dev/null +++ b/src/uu/stty/src/flags.rs @@ -0,0 +1,332 @@ +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE file +// * that was distributed with this source code. + +// spell-checker:ignore parenb parodd cmspar hupcl cstopb cread clocal crtscts +// spell-checker:ignore ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl ixoff ixon iuclc ixany imaxbel iutf +// spell-checker:ignore opost olcuc ocrnl onlcr onocr onlret ofill ofdel +// 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, InputFlags, LocalFlags, OutputFlags}; + +pub const CONTROL_FLAGS: [Flag; 8] = [ + Flag { + name: "parenb", + flag: ControlFlags::PARENB, + show: true, + sane: false, + }, + Flag { + name: "parodd", + flag: ControlFlags::PARODD, + show: true, + sane: false, + }, + Flag { + name: "cmspar", + flag: ControlFlags::CMSPAR, + show: true, + sane: false, + }, + Flag { + name: "hupcl", + flag: ControlFlags::HUPCL, + show: true, + sane: true, + }, + Flag { + name: "cstopb", + flag: ControlFlags::CSTOPB, + show: true, + sane: false, + }, + Flag { + name: "cread", + flag: ControlFlags::CREAD, + show: true, + sane: true, + }, + Flag { + name: "clocal", + flag: ControlFlags::CLOCAL, + show: true, + sane: false, + }, + Flag { + name: "crtscts", + flag: ControlFlags::CRTSCTS, + show: true, + sane: false, + }, +]; + +pub const INPUT_FLAGS: [Flag; 15] = [ + Flag { + name: "ignbrk", + flag: InputFlags::IGNBRK, + show: true, + sane: false, + }, + Flag { + name: "brkint", + flag: InputFlags::BRKINT, + show: true, + sane: true, + }, + Flag { + name: "ignpar", + flag: InputFlags::IGNPAR, + show: true, + sane: false, + }, + Flag { + name: "parmrk", + flag: InputFlags::PARMRK, + show: true, + sane: false, + }, + Flag { + name: "inpck", + flag: InputFlags::INPCK, + show: true, + sane: false, + }, + Flag { + name: "istrip", + flag: InputFlags::ISTRIP, + show: true, + sane: false, + }, + Flag { + name: "inlcr", + flag: InputFlags::INLCR, + show: true, + sane: false, + }, + Flag { + name: "igncr", + flag: InputFlags::IGNCR, + show: true, + sane: false, + }, + Flag { + name: "icrnl", + flag: InputFlags::ICRNL, + show: true, + sane: true, + }, + Flag { + name: "ixoff", + flag: InputFlags::IXOFF, + show: true, + sane: false, + }, + Flag { + name: "tandem", + flag: InputFlags::IXOFF, + show: false, + sane: false, + }, + Flag { + name: "ixon", + flag: InputFlags::IXON, + show: true, + sane: false, + }, + // not supported by nix + // Flag { + // name: "iuclc", + // flag: InputFlags::IUCLC, + // show: true, + // default: false, + // }, + Flag { + name: "ixany", + flag: InputFlags::IXANY, + show: true, + sane: false, + }, + Flag { + name: "imaxbel", + flag: InputFlags::IMAXBEL, + show: true, + sane: true, + }, + Flag { + name: "iutf8", + flag: InputFlags::IUTF8, + show: true, + sane: false, + }, +]; + +pub const OUTPUT_FLAGS: [Flag; 8] = [ + Flag { + name: "opost", + flag: OutputFlags::OPOST, + show: true, + sane: true, + }, + Flag { + name: "olcuc", + flag: OutputFlags::OLCUC, + show: true, + sane: false, + }, + Flag { + name: "ocrnl", + flag: OutputFlags::OCRNL, + show: true, + sane: false, + }, + Flag { + name: "onlcr", + flag: OutputFlags::ONLCR, + show: true, + sane: true, + }, + Flag { + name: "onocr", + flag: OutputFlags::ONOCR, + show: true, + sane: false, + }, + Flag { + name: "onlret", + flag: OutputFlags::ONLRET, + show: true, + sane: false, + }, + Flag { + name: "ofill", + flag: OutputFlags::OFILL, + show: true, + sane: false, + }, + Flag { + name: "ofdel", + flag: OutputFlags::OFDEL, + show: true, + sane: false, + }, +]; + +pub const LOCAL_FLAGS: [Flag; 18] = [ + Flag { + name: "isig", + flag: LocalFlags::ISIG, + show: true, + sane: true, + }, + Flag { + name: "icanon", + flag: LocalFlags::ICANON, + show: true, + sane: true, + }, + Flag { + name: "iexten", + flag: LocalFlags::IEXTEN, + show: true, + sane: true, + }, + Flag { + name: "echo", + flag: LocalFlags::ECHO, + show: true, + sane: true, + }, + Flag { + name: "echoe", + flag: LocalFlags::ECHOE, + show: true, + sane: true, + }, + Flag { + name: "crterase", + flag: LocalFlags::ECHOE, + show: false, + sane: true, + }, + Flag { + name: "echok", + flag: LocalFlags::ECHOK, + show: true, + sane: true, + }, + Flag { + name: "echonl", + flag: LocalFlags::ECHONL, + show: true, + sane: false, + }, + Flag { + name: "noflsh", + flag: LocalFlags::NOFLSH, + show: true, + sane: false, + }, + // Not supported by nix + // Flag { + // name: "xcase", + // flag: LocalFlags::XCASE, + // show: true, + // sane: false, + // }, + Flag { + name: "tostop", + flag: LocalFlags::TOSTOP, + show: true, + sane: false, + }, + Flag { + name: "echoprt", + flag: LocalFlags::ECHOPRT, + show: true, + sane: false, + }, + Flag { + name: "prterase", + flag: LocalFlags::ECHOPRT, + show: false, + sane: false, + }, + Flag { + name: "echoctl", + flag: LocalFlags::ECHOCTL, + show: true, + sane: true, + }, + Flag { + name: "ctlecho", + flag: LocalFlags::ECHOCTL, + show: false, + sane: true, + }, + Flag { + name: "echoke", + flag: LocalFlags::ECHOKE, + show: true, + sane: true, + }, + Flag { + name: "crtkill", + flag: LocalFlags::ECHOKE, + show: false, + sane: true, + }, + Flag { + name: "flusho", + flag: LocalFlags::FLUSHO, + show: true, + sane: false, + }, + Flag { + name: "extproc", + flag: LocalFlags::EXTPROC, + show: true, + sane: false, + }, +]; diff --git a/src/uu/stty/src/main.rs b/src/uu/stty/src/main.rs new file mode 100644 index 000000000..4f9b9799a --- /dev/null +++ b/src/uu/stty/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_stty); diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs new file mode 100644 index 000000000..52cd5995b --- /dev/null +++ b/src/uu/stty/src/stty.rs @@ -0,0 +1,240 @@ +// * This file is part of the uutils coreutils package. +// * +// * 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 + +mod flags; + +use clap::{crate_version, Arg, ArgMatches, Command}; +use nix::sys::termios::{ + tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, Termios, +}; +use std::io::{self, stdout}; +use std::os::unix::io::{AsRawFd, RawFd}; +use uucore::error::UResult; +use uucore::{format_usage, InvalidEncodingHandling}; + +use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS}; + +const NAME: &str = "stty"; +const USAGE: &str = "\ +{} [-F DEVICE | --file=DEVICE] [SETTING]... +{} [-F DEVICE | --file=DEVICE] [-a|--all] +{} [-F DEVICE | --file=DEVICE] [-g|--save]"; +const SUMMARY: &str = "Print or change terminal characteristics."; + +pub struct Flag { + name: &'static str, + flag: T, + show: bool, + sane: bool, +} + +trait TermiosFlag { + fn is_in(&self, termios: &Termios) -> bool; + fn apply(&self, termios: &mut Termios, val: bool); +} + +mod options { + pub const ALL: &str = "all"; + pub const SAVE: &str = "save"; + pub const FILE: &str = "file"; + pub const SETTINGS: &str = "settings"; +} + +struct Options<'a> { + all: bool, + _save: bool, + file: RawFd, + settings: Option>, +} + +impl<'a> Options<'a> { + fn from(matches: &'a ArgMatches) -> io::Result { + Ok(Self { + all: matches.is_present(options::ALL), + _save: matches.is_present(options::SAVE), + file: match matches.value_of(options::FILE) { + Some(_f) => todo!(), + None => stdout().as_raw_fd(), + }, + settings: matches.values_of(options::SETTINGS).map(|v| v.collect()), + }) + } +} + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let args = args + .collect_str(InvalidEncodingHandling::ConvertLossy) + .accept_any(); + + let matches = uu_app().get_matches_from(args); + + let opts = Options::from(&matches)?; + + stty(&opts) +} + +fn stty(opts: &Options) -> UResult<()> { + // TODO: Figure out the right error message + let mut termios = tcgetattr(opts.file).expect("Could not get terminal attributes"); + if let Some(settings) = &opts.settings { + for setting in settings { + apply_setting(&mut termios, setting); + } + + tcsetattr(opts.file, nix::sys::termios::SetArg::TCSANOW, &termios) + .expect("Could not write terminal attributes"); + } else { + print_settings(&termios, opts); + } + Ok(()) +} + +fn print_settings(termios: &Termios, opts: &Options) { + if print_flags(termios, opts, &CONTROL_FLAGS) { + println!(); + } + if print_flags(termios, opts, &INPUT_FLAGS) { + println!(); + } + if print_flags(termios, opts, &OUTPUT_FLAGS) { + println!(); + } + if print_flags(termios, opts, &LOCAL_FLAGS) { + println!(); + } +} + +fn print_flags(termios: &Termios, opts: &Options, flags: &[Flag]) -> bool +where + Flag: TermiosFlag, +{ + let mut printed = false; + for flag in flags { + if !flag.show { + continue; + } + let val = flag.is_in(termios); + if opts.all || val != flag.sane { + if !val { + print!("-"); + } + print!("{} ", flag.name); + printed = true; + } + } + printed +} + +fn apply_setting(termios: &mut Termios, s: &str) -> Option<()> { + if let Some(()) = apply_flag(termios, &CONTROL_FLAGS, s) { + return Some(()); + } + if let Some(()) = apply_flag(termios, &INPUT_FLAGS, s) { + return Some(()); + } + if let Some(()) = apply_flag(termios, &OUTPUT_FLAGS, s) { + return Some(()); + } + if let Some(()) = apply_flag(termios, &LOCAL_FLAGS, s) { + return Some(()); + } + None +} + +fn apply_flag(termios: &mut Termios, flags: &[Flag], name: &str) -> Option<()> +where + T: Copy, + Flag: TermiosFlag, +{ + let (remove, name) = strip_hyphen(name); + find(flags, name)?.apply(termios, !remove); + Some(()) +} + +fn strip_hyphen(s: &str) -> (bool, &str) { + match s.strip_prefix('-') { + Some(s) => (true, s), + None => (false, s), + } +} + +pub fn uu_app<'a>() -> Command<'a> { + Command::new(uucore::util_name()) + .name(NAME) + .version(crate_version!()) + .override_usage(format_usage(USAGE)) + .about(SUMMARY) + .infer_long_args(true) + .arg(Arg::new(options::ALL).short('a').long(options::ALL)) + .arg(Arg::new(options::SAVE).short('g').long(options::SAVE)) + .arg( + Arg::new(options::FILE) + .short('F') + .long(options::FILE) + .takes_value(true) + .value_hint(clap::ValueHint::FilePath), + ) + .arg( + Arg::new(options::SETTINGS) + .takes_value(true) + .multiple_values(true), + ) +} + +impl TermiosFlag for Flag { + fn is_in(&self, termios: &Termios) -> bool { + termios.control_flags.contains(self.flag) + } + + fn apply(&self, termios: &mut Termios, val: bool) { + termios.control_flags.set(self.flag, val) + } +} + +impl TermiosFlag for Flag { + fn is_in(&self, termios: &Termios) -> bool { + termios.input_flags.contains(self.flag) + } + + fn apply(&self, termios: &mut Termios, val: bool) { + termios.input_flags.set(self.flag, val) + } +} + +impl TermiosFlag for Flag { + fn is_in(&self, termios: &Termios) -> bool { + termios.output_flags.contains(self.flag) + } + + fn apply(&self, termios: &mut Termios, val: bool) { + termios.output_flags.set(self.flag, val) + } +} + +impl TermiosFlag for Flag { + fn is_in(&self, termios: &Termios) -> bool { + termios.local_flags.contains(self.flag) + } + + fn apply(&self, termios: &mut Termios, val: bool) { + termios.local_flags.set(self.flag, val) + } +} + +fn find<'a, T>(flags: &'a [Flag], flag_name: &str) -> Option<&'a Flag> +where + T: Copy, +{ + flags.iter().find_map(|flag| { + if flag.name == flag_name { + Some(flag) + } else { + None + } + }) +} From a23702caebb68ce3c89a5845d3db1571ee2456b6 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Sat, 25 Jun 2022 22:37:28 +0200 Subject: [PATCH 02/17] add some simple tests to stty (still failing) --- tests/by-util/test_stty.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/by-util/test_stty.rs diff --git a/tests/by-util/test_stty.rs b/tests/by-util/test_stty.rs new file mode 100644 index 000000000..544d20afe --- /dev/null +++ b/tests/by-util/test_stty.rs @@ -0,0 +1,18 @@ +use crate::common::util::*; + +#[test] +fn runs() { + new_ucmd!().succeeds(); +} + +#[test] +fn print_all() { + let res = new_ucmd!().succeeds(); + + // Random selection of flags to check for + for flag in [ + "parenb", "parmrk", "ixany", "iuclc", "onlcr", "ofdel", "icanon", "noflsh", + ] { + res.stdout_contains(flag); + } +} From 4cda8f33e70d68688045ae816b25c2f2f21e50e8 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 27 Jun 2022 13:00:31 +0200 Subject: [PATCH 03/17] stty: add grouped flags --- src/uu/stty/src/flags.rs | 391 ++++++++------------------------------- src/uu/stty/src/stty.rs | 226 +++++++++++++--------- 2 files changed, 218 insertions(+), 399 deletions(-) diff --git a/src/uu/stty/src/flags.rs b/src/uu/stty/src/flags.rs index b2688cb8d..7a985a04b 100644 --- a/src/uu/stty/src/flags.rs +++ b/src/uu/stty/src/flags.rs @@ -3,330 +3,95 @@ // * For the full copyright and license information, please view the LICENSE file // * that was distributed with this source code. -// spell-checker:ignore parenb parodd cmspar hupcl cstopb cread clocal crtscts +// spell-checker:ignore parenb parodd cmspar hupcl cstopb cread clocal crtscts CSIZE // spell-checker:ignore ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl ixoff ixon iuclc ixany imaxbel iutf -// spell-checker:ignore opost olcuc ocrnl onlcr onocr onlret ofill ofdel +// spell-checker:ignore opost olcuc ocrnl onlcr onocr onlret ofill ofdel nldly crdly tabdly bsdly vtdly ffdly // 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, InputFlags, LocalFlags, OutputFlags}; +use nix::sys::termios::{ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O}; -pub const CONTROL_FLAGS: [Flag; 8] = [ - Flag { - name: "parenb", - flag: ControlFlags::PARENB, - show: true, - sane: false, - }, - Flag { - name: "parodd", - flag: ControlFlags::PARODD, - show: true, - sane: false, - }, - Flag { - name: "cmspar", - flag: ControlFlags::CMSPAR, - show: true, - sane: false, - }, - Flag { - name: "hupcl", - flag: ControlFlags::HUPCL, - show: true, - sane: true, - }, - Flag { - name: "cstopb", - flag: ControlFlags::CSTOPB, - show: true, - sane: false, - }, - Flag { - name: "cread", - flag: ControlFlags::CREAD, - show: true, - sane: true, - }, - Flag { - name: "clocal", - flag: ControlFlags::CLOCAL, - show: true, - sane: false, - }, - Flag { - name: "crtscts", - flag: ControlFlags::CRTSCTS, - show: true, - sane: false, - }, +pub const CONTROL_FLAGS: [Flag; 12] = [ + Flag::new("parenb", C::PARENB), + Flag::new("parodd", C::PARODD), + Flag::new("cmspar", C::CMSPAR), + Flag::new("cs5", C::CS5).group(C::CSIZE), + 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("cstopb", C::CSTOPB), + Flag::new("cread", C::CREAD).sane(), + Flag::new("clocal", C::CLOCAL), + Flag::new("crtscts", C::CRTSCTS), ]; -pub const INPUT_FLAGS: [Flag; 15] = [ - Flag { - name: "ignbrk", - flag: InputFlags::IGNBRK, - show: true, - sane: false, - }, - Flag { - name: "brkint", - flag: InputFlags::BRKINT, - show: true, - sane: true, - }, - Flag { - name: "ignpar", - flag: InputFlags::IGNPAR, - show: true, - sane: false, - }, - Flag { - name: "parmrk", - flag: InputFlags::PARMRK, - show: true, - sane: false, - }, - Flag { - name: "inpck", - flag: InputFlags::INPCK, - show: true, - sane: false, - }, - Flag { - name: "istrip", - flag: InputFlags::ISTRIP, - show: true, - sane: false, - }, - Flag { - name: "inlcr", - flag: InputFlags::INLCR, - show: true, - sane: false, - }, - Flag { - name: "igncr", - flag: InputFlags::IGNCR, - show: true, - sane: false, - }, - Flag { - name: "icrnl", - flag: InputFlags::ICRNL, - show: true, - sane: true, - }, - Flag { - name: "ixoff", - flag: InputFlags::IXOFF, - show: true, - sane: false, - }, - Flag { - name: "tandem", - flag: InputFlags::IXOFF, - show: false, - sane: false, - }, - Flag { - name: "ixon", - flag: InputFlags::IXON, - show: true, - sane: false, - }, +pub const INPUT_FLAGS: [Flag; 15] = [ + Flag::new("ignbrk", I::IGNBRK), + Flag::new("brkint", I::BRKINT).sane(), + Flag::new("ignpar", I::IGNPAR), + Flag::new("parmrk", I::PARMRK), + Flag::new("inpck", I::INPCK), + Flag::new("istrip", I::ISTRIP), + Flag::new("inlcr", I::INLCR), + Flag::new("igncr", I::IGNCR), + Flag::new("icrnl", I::ICRNL).sane(), + Flag::new("ixoff", I::IXOFF), + Flag::new("tandem", I::IXOFF), + Flag::new("ixon", I::IXON), // not supported by nix - // Flag { - // name: "iuclc", - // flag: InputFlags::IUCLC, - // show: true, - // default: false, - // }, - Flag { - name: "ixany", - flag: InputFlags::IXANY, - show: true, - sane: false, - }, - Flag { - name: "imaxbel", - flag: InputFlags::IMAXBEL, - show: true, - sane: true, - }, - Flag { - name: "iutf8", - flag: InputFlags::IUTF8, - show: true, - sane: false, - }, + // Flag::new("iuclc", I::IUCLC), + Flag::new("ixany", I::IXANY).sane(), + Flag::new("imaxbel", I::IMAXBEL).sane(), + Flag::new("iutf8", I::IUTF8).sane(), ]; -pub const OUTPUT_FLAGS: [Flag; 8] = [ - Flag { - name: "opost", - flag: OutputFlags::OPOST, - show: true, - sane: true, - }, - Flag { - name: "olcuc", - flag: OutputFlags::OLCUC, - show: true, - sane: false, - }, - Flag { - name: "ocrnl", - flag: OutputFlags::OCRNL, - show: true, - sane: false, - }, - Flag { - name: "onlcr", - flag: OutputFlags::ONLCR, - show: true, - sane: true, - }, - Flag { - name: "onocr", - flag: OutputFlags::ONOCR, - show: true, - sane: false, - }, - Flag { - name: "onlret", - flag: OutputFlags::ONLRET, - show: true, - sane: false, - }, - Flag { - name: "ofill", - flag: OutputFlags::OFILL, - show: true, - sane: false, - }, - Flag { - name: "ofdel", - flag: OutputFlags::OFDEL, - show: true, - sane: false, - }, +pub const OUTPUT_FLAGS: [Flag; 24] = [ + Flag::new("opost", O::OPOST).sane(), + Flag::new("olcuc", O::OLCUC), + Flag::new("ocrnl", O::OCRNL), + Flag::new("onlcr", O::ONLCR).sane(), + Flag::new("onocr", O::ONOCR), + Flag::new("onlret", O::ONLRET), + Flag::new("ofill", O::OFILL), + Flag::new("ofdel", O::OFDEL), + Flag::new("nl0", O::NL0).group(O::NLDLY).sane(), + Flag::new("nl1", O::NL1).group(O::NLDLY), + Flag::new("cr0", O::CR0).group(O::CRDLY).sane(), + Flag::new("cr1", O::CR1).group(O::CRDLY), + Flag::new("cr2", O::CR2).group(O::CRDLY), + Flag::new("cr3", O::CR3).group(O::CRDLY), + Flag::new("tab0", O::TAB0).group(O::TABDLY).sane(), + Flag::new("tab1", O::TAB1).group(O::TABDLY), + Flag::new("tab2", O::TAB2).group(O::TABDLY), + Flag::new("tab3", O::TAB3).group(O::TABDLY), + Flag::new("bs0", O::BS0).group(O::BSDLY).sane(), + Flag::new("bs1", O::BS1).group(O::BSDLY), + Flag::new("vt0", O::VT0).group(O::VTDLY).sane(), + Flag::new("vt1", O::VT1).group(O::VTDLY), + Flag::new("ff0", O::FF0).group(O::FFDLY).sane(), + Flag::new("ff1", O::FF1).group(O::FFDLY), ]; -pub const LOCAL_FLAGS: [Flag; 18] = [ - Flag { - name: "isig", - flag: LocalFlags::ISIG, - show: true, - sane: true, - }, - Flag { - name: "icanon", - flag: LocalFlags::ICANON, - show: true, - sane: true, - }, - Flag { - name: "iexten", - flag: LocalFlags::IEXTEN, - show: true, - sane: true, - }, - Flag { - name: "echo", - flag: LocalFlags::ECHO, - show: true, - sane: true, - }, - Flag { - name: "echoe", - flag: LocalFlags::ECHOE, - show: true, - sane: true, - }, - Flag { - name: "crterase", - flag: LocalFlags::ECHOE, - show: false, - sane: true, - }, - Flag { - name: "echok", - flag: LocalFlags::ECHOK, - show: true, - sane: true, - }, - Flag { - name: "echonl", - flag: LocalFlags::ECHONL, - show: true, - sane: false, - }, - Flag { - name: "noflsh", - flag: LocalFlags::NOFLSH, - show: true, - sane: false, - }, +pub const LOCAL_FLAGS: [Flag; 18] = [ + Flag::new("isig", L::ISIG).sane(), + Flag::new("icanon", L::ICANON).sane(), + Flag::new("iexten", L::IEXTEN).sane(), + Flag::new("echo", L::ECHO).sane(), + Flag::new("echoe", L::ECHOE).sane(), + Flag::new("crterase", L::ECHOE).hidden().sane(), + Flag::new("echok", L::ECHOK).sane(), + Flag::new("echonl", L::ECHONL), + Flag::new("noflsh", L::NOFLSH), // Not supported by nix - // Flag { - // name: "xcase", - // flag: LocalFlags::XCASE, - // show: true, - // sane: false, - // }, - Flag { - name: "tostop", - flag: LocalFlags::TOSTOP, - show: true, - sane: false, - }, - Flag { - name: "echoprt", - flag: LocalFlags::ECHOPRT, - show: true, - sane: false, - }, - Flag { - name: "prterase", - flag: LocalFlags::ECHOPRT, - show: false, - sane: false, - }, - Flag { - name: "echoctl", - flag: LocalFlags::ECHOCTL, - show: true, - sane: true, - }, - Flag { - name: "ctlecho", - flag: LocalFlags::ECHOCTL, - show: false, - sane: true, - }, - Flag { - name: "echoke", - flag: LocalFlags::ECHOKE, - show: true, - sane: true, - }, - Flag { - name: "crtkill", - flag: LocalFlags::ECHOKE, - show: false, - sane: true, - }, - Flag { - name: "flusho", - flag: LocalFlags::FLUSHO, - show: true, - sane: false, - }, - Flag { - name: "extproc", - flag: LocalFlags::EXTPROC, - show: true, - sane: false, - }, + // Flag::new("xcase", L::XCASE), + Flag::new("tostop", L::TOSTOP), + Flag::new("echoprt", L::ECHOPRT), + Flag::new("prterase", L::ECHOPRT).hidden(), + Flag::new("echoctl", L::ECHOCTL).sane(), + Flag::new("ctlecho", L::ECHOCTL).sane().hidden(), + Flag::new("echoke", L::ECHOKE).sane(), + Flag::new("crtkill", L::ECHOKE).sane().hidden(), + Flag::new("flusho", L::FLUSHO), + Flag::new("extproc", L::EXTPROC), ]; diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 52cd5995b..fa29f761b 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -12,8 +12,9 @@ use nix::sys::termios::{ tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, Termios, }; use std::io::{self, stdout}; +use std::ops::ControlFlow; use std::os::unix::io::{AsRawFd, RawFd}; -use uucore::error::UResult; +use uucore::error::{UResult, USimpleError}; use uucore::{format_usage, InvalidEncodingHandling}; use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS}; @@ -25,15 +26,53 @@ const USAGE: &str = "\ {} [-F DEVICE | --file=DEVICE] [-g|--save]"; const SUMMARY: &str = "Print or change terminal characteristics."; +#[derive(Clone, Copy, Debug)] pub struct Flag { name: &'static str, flag: T, show: bool, sane: bool, + group: Option, } -trait TermiosFlag { - fn is_in(&self, termios: &Termios) -> bool; +impl Flag +where + T: Copy, +{ + pub const fn new(name: &'static str, flag: T) -> Self { + Flag { + name, + flag, + show: true, + sane: false, + group: None, + } + } + + pub const fn hidden(&self) -> Self { + Self { + show: false, + ..*self + } + } + + pub const fn sane(&self) -> Self { + Self { + sane: true, + ..*self + } + } + + pub const fn group(&self, group: T) -> Self { + Self { + group: Some(group), + ..*self + } + } +} + +trait TermiosFlag: Copy { + fn is_in(&self, termios: &Termios, group: Option) -> bool; fn apply(&self, termios: &mut Termios, val: bool); } @@ -83,7 +122,12 @@ fn stty(opts: &Options) -> UResult<()> { let mut termios = tcgetattr(opts.file).expect("Could not get terminal attributes"); if let Some(settings) = &opts.settings { for setting in settings { - apply_setting(&mut termios, setting); + if let ControlFlow::Break(false) = apply_setting(&mut termios, setting) { + return Err(USimpleError::new( + 1, + format!("invalid argument '{}'", setting), + )); + } } tcsetattr(opts.file, nix::sys::termios::SetArg::TCSANOW, &termios) @@ -95,72 +139,91 @@ fn stty(opts: &Options) -> UResult<()> { } fn print_settings(termios: &Termios, opts: &Options) { - if print_flags(termios, opts, &CONTROL_FLAGS) { - println!(); - } - if print_flags(termios, opts, &INPUT_FLAGS) { - println!(); - } - if print_flags(termios, opts, &OUTPUT_FLAGS) { - println!(); - } - if print_flags(termios, opts, &LOCAL_FLAGS) { - println!(); - } + print_flags(termios, opts, &CONTROL_FLAGS); + print_flags(termios, opts, &INPUT_FLAGS); + print_flags(termios, opts, &OUTPUT_FLAGS); + print_flags(termios, opts, &LOCAL_FLAGS); } -fn print_flags(termios: &Termios, opts: &Options, flags: &[Flag]) -> bool -where - Flag: TermiosFlag, -{ +fn print_flags(termios: &Termios, opts: &Options, flags: &[Flag]) { let mut printed = false; - for flag in flags { - if !flag.show { + for &Flag { + name, + flag, + show, + sane, + group, + } in flags + { + if !show { continue; } - let val = flag.is_in(termios); - if opts.all || val != flag.sane { - if !val { - print!("-"); + let val = flag.is_in(termios, group); + if group.is_some() { + if val && (!sane || opts.all) { + print!("{name} "); + printed = true; + } + } else { + if opts.all || val != sane { + if !val { + print!("-"); + } + print!("{name} "); + printed = true; } - print!("{} ", flag.name); - printed = true; } } - printed + if printed { + println!(); + } } -fn apply_setting(termios: &mut Termios, s: &str) -> Option<()> { - if let Some(()) = apply_flag(termios, &CONTROL_FLAGS, s) { - return Some(()); - } - if let Some(()) = apply_flag(termios, &INPUT_FLAGS, s) { - return Some(()); - } - if let Some(()) = apply_flag(termios, &OUTPUT_FLAGS, s) { - return Some(()); - } - if let Some(()) = apply_flag(termios, &LOCAL_FLAGS, s) { - return Some(()); - } - None -} - -fn apply_flag(termios: &mut Termios, flags: &[Flag], name: &str) -> Option<()> -where - T: Copy, - Flag: TermiosFlag, -{ - let (remove, name) = strip_hyphen(name); - find(flags, name)?.apply(termios, !remove); - Some(()) -} - -fn strip_hyphen(s: &str) -> (bool, &str) { - match s.strip_prefix('-') { +/// Apply a single setting +/// +/// The value inside the `Break` variant of the `ControlFlow` indicates whether +/// the setting has been applied. +fn apply_setting(termios: &mut Termios, s: &str) -> ControlFlow { + let (remove, name) = match s.strip_prefix('-') { Some(s) => (true, s), None => (false, s), + }; + apply_flag(termios, &CONTROL_FLAGS, name, remove)?; + apply_flag(termios, &INPUT_FLAGS, name, remove)?; + apply_flag(termios, &OUTPUT_FLAGS, name, remove)?; + apply_flag(termios, &LOCAL_FLAGS, name, remove)?; + ControlFlow::Break(false) +} + +/// Apply a flag to a slice of flags +/// +/// The value inside the `Break` variant of the `ControlFlow` indicates whether +/// the setting has been applied. +fn apply_flag( + termios: &mut Termios, + flags: &[Flag], + input: &str, + remove: bool, +) -> ControlFlow { + for Flag { + name, flag, group, .. + } in flags + { + if input == *name { + // Flags with groups cannot be removed + // Since the name matches, we can short circuit and don't have to check the other flags. + if remove || group.is_some() { + return ControlFlow::Break(false); + } + // If there is a group, the bits for that group should be cleared before applying the flag + if let Some(group) = group { + group.apply(termios, false); + } + flag.apply(termios, !remove); + return ControlFlow::Break(true); + } } + ControlFlow::Continue(()) } pub fn uu_app<'a>() -> Command<'a> { @@ -186,55 +249,46 @@ pub fn uu_app<'a>() -> Command<'a> { ) } -impl TermiosFlag for Flag { - fn is_in(&self, termios: &Termios) -> bool { - termios.control_flags.contains(self.flag) +impl TermiosFlag for ControlFlags { + fn is_in(&self, termios: &Termios, group: Option) -> bool { + termios.control_flags.contains(*self) + && group.map_or(true, |g| !termios.control_flags.intersects(g - *self)) } fn apply(&self, termios: &mut Termios, val: bool) { - termios.control_flags.set(self.flag, val) + termios.control_flags.set(*self, val) } } -impl TermiosFlag for Flag { - fn is_in(&self, termios: &Termios) -> bool { - termios.input_flags.contains(self.flag) +impl TermiosFlag for InputFlags { + fn is_in(&self, termios: &Termios, group: Option) -> bool { + termios.input_flags.contains(*self) + && group.map_or(true, |g| !termios.input_flags.intersects(g - *self)) } fn apply(&self, termios: &mut Termios, val: bool) { - termios.input_flags.set(self.flag, val) + termios.input_flags.set(*self, val) } } -impl TermiosFlag for Flag { - fn is_in(&self, termios: &Termios) -> bool { - termios.output_flags.contains(self.flag) +impl TermiosFlag for OutputFlags { + fn is_in(&self, termios: &Termios, group: Option) -> bool { + termios.output_flags.contains(*self) + && group.map_or(true, |g| !termios.output_flags.intersects(g - *self)) } fn apply(&self, termios: &mut Termios, val: bool) { - termios.output_flags.set(self.flag, val) + termios.output_flags.set(*self, val) } } -impl TermiosFlag for Flag { - fn is_in(&self, termios: &Termios) -> bool { - termios.local_flags.contains(self.flag) +impl TermiosFlag for LocalFlags { + fn is_in(&self, termios: &Termios, group: Option) -> bool { + termios.local_flags.contains(*self) + && group.map_or(true, |g| !termios.local_flags.intersects(g - *self)) } fn apply(&self, termios: &mut Termios, val: bool) { - termios.local_flags.set(self.flag, val) + termios.local_flags.set(*self, val) } } - -fn find<'a, T>(flags: &'a [Flag], flag_name: &str) -> Option<&'a Flag> -where - T: Copy, -{ - flags.iter().find_map(|flag| { - if flag.name == flag_name { - Some(flag) - } else { - None - } - }) -} From 6896a85b412a06bd68545585f1bf868243d6c81d Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 27 Jun 2022 13:03:48 +0200 Subject: [PATCH 04/17] test_stty: fix spell checker errors --- tests/by-util/test_stty.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/by-util/test_stty.rs b/tests/by-util/test_stty.rs index 544d20afe..7dc0b20d2 100644 --- a/tests/by-util/test_stty.rs +++ b/tests/by-util/test_stty.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore parenb parmrk ixany iuclc onlcr ofdel icanon noflsh + use crate::common::util::*; #[test] From 0c44fc07d26c013e7bfc38f5f92bc1b446abb064 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 27 Jun 2022 13:04:56 +0200 Subject: [PATCH 05/17] stty: fix ixany and iutf8 not sane --- src/uu/stty/src/flags.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/stty/src/flags.rs b/src/uu/stty/src/flags.rs index 7a985a04b..226c5ce66 100644 --- a/src/uu/stty/src/flags.rs +++ b/src/uu/stty/src/flags.rs @@ -41,9 +41,9 @@ pub const INPUT_FLAGS: [Flag; 15] = [ Flag::new("ixon", I::IXON), // not supported by nix // Flag::new("iuclc", I::IUCLC), - Flag::new("ixany", I::IXANY).sane(), + Flag::new("ixany", I::IXANY), Flag::new("imaxbel", I::IMAXBEL).sane(), - Flag::new("iutf8", I::IUTF8).sane(), + Flag::new("iutf8", I::IUTF8), ]; pub const OUTPUT_FLAGS: [Flag; 24] = [ From 96ea77201631913665a52e590a9b35813fc55565 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 27 Jun 2022 13:07:35 +0200 Subject: [PATCH 06/17] stty: fix clippy warnings --- src/uu/stty/src/stty.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index fa29f761b..fefbf2014 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -40,7 +40,7 @@ where T: Copy, { pub const fn new(name: &'static str, flag: T) -> Self { - Flag { + Self { name, flag, show: true, @@ -164,14 +164,12 @@ fn print_flags(termios: &Termios, opts: &Options, flags: &[Flag< print!("{name} "); printed = true; } - } else { - if opts.all || val != sane { - if !val { - print!("-"); - } - print!("{name} "); - printed = true; + } else if opts.all || val != sane { + if !val { + print!("-"); } + print!("{name} "); + printed = true; } } if printed { @@ -256,7 +254,7 @@ impl TermiosFlag for ControlFlags { } fn apply(&self, termios: &mut Termios, val: bool) { - termios.control_flags.set(*self, val) + termios.control_flags.set(*self, val); } } @@ -267,7 +265,7 @@ impl TermiosFlag for InputFlags { } fn apply(&self, termios: &mut Termios, val: bool) { - termios.input_flags.set(*self, val) + termios.input_flags.set(*self, val); } } @@ -278,7 +276,7 @@ impl TermiosFlag for OutputFlags { } fn apply(&self, termios: &mut Termios, val: bool) { - termios.output_flags.set(*self, val) + termios.output_flags.set(*self, val); } } @@ -289,6 +287,6 @@ impl TermiosFlag for LocalFlags { } fn apply(&self, termios: &mut Termios, val: bool) { - termios.local_flags.set(*self, val) + termios.local_flags.set(*self, val); } } From eac88022b2dc2ffc5624a576598e77d526a29a1f Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 27 Jun 2022 13:45:59 +0200 Subject: [PATCH 07/17] stty: fix setting grouped flags --- src/uu/stty/src/stty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index fefbf2014..1eecf5654 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -210,7 +210,7 @@ fn apply_flag( if input == *name { // Flags with groups cannot be removed // Since the name matches, we can short circuit and don't have to check the other flags. - if remove || group.is_some() { + if remove && group.is_some() { return ControlFlow::Break(false); } // If there is a group, the bits for that group should be cleared before applying the flag From 679fd2371e1105c8da8d2e6012e56fcc521871be Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 27 Jun 2022 13:46:34 +0200 Subject: [PATCH 08/17] stty: disallow combining settings with --all and --save --- src/uu/stty/src/stty.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 1eecf5654..76a281462 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -85,7 +85,7 @@ mod options { struct Options<'a> { all: bool, - _save: bool, + save: bool, file: RawFd, settings: Option>, } @@ -94,7 +94,7 @@ impl<'a> Options<'a> { fn from(matches: &'a ArgMatches) -> io::Result { Ok(Self { all: matches.is_present(options::ALL), - _save: matches.is_present(options::SAVE), + save: matches.is_present(options::SAVE), file: match matches.value_of(options::FILE) { Some(_f) => todo!(), None => stdout().as_raw_fd(), @@ -120,7 +120,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { fn stty(opts: &Options) -> UResult<()> { // TODO: Figure out the right error message 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( From cc147a7c8dc6420150a035d5079af46e94ffdb07 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 27 Jun 2022 19:45:38 +0200 Subject: [PATCH 09/17] stty: expand --help information --- src/uu/stty/src/stty.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 76a281462..68c128a7b 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -21,9 +21,9 @@ use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS}; const NAME: &str = "stty"; const USAGE: &str = "\ -{} [-F DEVICE | --file=DEVICE] [SETTING]... -{} [-F DEVICE | --file=DEVICE] [-a|--all] -{} [-F DEVICE | --file=DEVICE] [-g|--save]"; + {} [-F DEVICE | --file=DEVICE] [SETTING]... + {} [-F DEVICE | --file=DEVICE] [-a|--all] + {} [-F DEVICE | --file=DEVICE] [-g|--save]"; const SUMMARY: &str = "Print or change terminal characteristics."; #[derive(Clone, Copy, Debug)] @@ -239,19 +239,32 @@ pub fn uu_app<'a>() -> Command<'a> { .override_usage(format_usage(USAGE)) .about(SUMMARY) .infer_long_args(true) - .arg(Arg::new(options::ALL).short('a').long(options::ALL)) - .arg(Arg::new(options::SAVE).short('g').long(options::SAVE)) + .arg( + Arg::new(options::ALL) + .short('a') + .long(options::ALL) + .help("print all current settings in human-readable form"), + ) + .arg( + Arg::new(options::SAVE) + .short('g') + .long(options::SAVE) + .help("print all current settings in a stty-readable form"), + ) .arg( Arg::new(options::FILE) .short('F') .long(options::FILE) .takes_value(true) - .value_hint(clap::ValueHint::FilePath), + .value_hint(clap::ValueHint::FilePath) + .value_name("DEVICE") + .help("open and use the specified DEVICE instead of stdin") ) .arg( Arg::new(options::SETTINGS) .takes_value(true) - .multiple_values(true), + .multiple_values(true) + .help("settings to change"), ) } From f861fc0854934a660fb0abac7e08d8cba82eaaf3 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Fri, 1 Jul 2022 15:55:58 +0200 Subject: [PATCH 10/17] stty: print special terminal information --- src/uu/stty/src/flags.rs | 106 ++++++++++++++++++++++++++++++++++++- src/uu/stty/src/stty.rs | 91 +++++++++++++++++++++++++------ tests/by-util/test_stty.rs | 35 ++++++++++++ 3 files changed, 214 insertions(+), 18 deletions(-) diff --git a/src/uu/stty/src/flags.rs b/src/uu/stty/src/flags.rs index 226c5ce66..14042ff70 100644 --- a/src/uu/stty/src/flags.rs +++ b/src/uu/stty/src/flags.rs @@ -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; 12] = [ Flag::new("parenb", C::PARENB), @@ -19,7 +21,7 @@ pub const CONTROL_FLAGS: [Flag; 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; 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), +]; diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 68c128a7b..5cabe6da6 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -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(termios: &Termios, opts: &Options, flags: &[Flag]) { @@ -169,14 +228,14 @@ fn print_flags(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) diff --git a/tests/by-util/test_stty.rs b/tests/by-util/test_stty.rs index 7dc0b20d2..730ccdf37 100644 --- a/tests/by-util/test_stty.rs +++ b/tests/by-util/test_stty.rs @@ -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", + ); +} From 420c69aa9826861191b68d2b6c3c0bbfc90710bf Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Sat, 2 Jul 2022 20:00:46 +0200 Subject: [PATCH 11/17] stty: make compatible with Rust pre 1.61 --- src/uu/stty/src/flags.rs | 40 ++++++++++++++++++++-------------------- src/uu/stty/src/stty.rs | 36 ++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/uu/stty/src/flags.rs b/src/uu/stty/src/flags.rs index 14042ff70..e564011b5 100644 --- a/src/uu/stty/src/flags.rs +++ b/src/uu/stty/src/flags.rs @@ -17,10 +17,10 @@ pub const CONTROL_FLAGS: [Flag; 12] = [ Flag::new("parenb", C::PARENB), Flag::new("parodd", C::PARODD), Flag::new("cmspar", C::CMSPAR), - Flag::new("cs5", C::CS5).group(C::CSIZE), - 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_grouped("cs5", C::CS5, C::CSIZE), + Flag::new_grouped("cs6", C::CS6, C::CSIZE), + Flag::new_grouped("cs7", C::CS7, C::CSIZE), + Flag::new_grouped("cs8", C::CS8, C::CSIZE).sane(), Flag::new("hupcl", C::HUPCL), Flag::new("cstopb", C::CSTOPB), Flag::new("cread", C::CREAD).sane(), @@ -57,22 +57,22 @@ pub const OUTPUT_FLAGS: [Flag; 24] = [ Flag::new("onlret", O::ONLRET), Flag::new("ofill", O::OFILL), Flag::new("ofdel", O::OFDEL), - Flag::new("nl0", O::NL0).group(O::NLDLY).sane(), - Flag::new("nl1", O::NL1).group(O::NLDLY), - Flag::new("cr0", O::CR0).group(O::CRDLY).sane(), - Flag::new("cr1", O::CR1).group(O::CRDLY), - Flag::new("cr2", O::CR2).group(O::CRDLY), - Flag::new("cr3", O::CR3).group(O::CRDLY), - Flag::new("tab0", O::TAB0).group(O::TABDLY).sane(), - Flag::new("tab1", O::TAB1).group(O::TABDLY), - Flag::new("tab2", O::TAB2).group(O::TABDLY), - Flag::new("tab3", O::TAB3).group(O::TABDLY), - Flag::new("bs0", O::BS0).group(O::BSDLY).sane(), - Flag::new("bs1", O::BS1).group(O::BSDLY), - Flag::new("vt0", O::VT0).group(O::VTDLY).sane(), - Flag::new("vt1", O::VT1).group(O::VTDLY), - Flag::new("ff0", O::FF0).group(O::FFDLY).sane(), - Flag::new("ff1", O::FF1).group(O::FFDLY), + Flag::new_grouped("nl0", O::NL0, O::NLDLY).sane(), + Flag::new_grouped("nl1", O::NL1, O::NLDLY), + Flag::new_grouped("cr0", O::CR0, O::CRDLY).sane(), + Flag::new_grouped("cr1", O::CR1, O::CRDLY), + Flag::new_grouped("cr2", O::CR2, O::CRDLY), + Flag::new_grouped("cr3", O::CR3, O::CRDLY), + Flag::new_grouped("tab0", O::TAB0, O::TABDLY).sane(), + Flag::new_grouped("tab1", O::TAB1, O::TABDLY), + Flag::new_grouped("tab2", O::TAB2, O::TABDLY), + Flag::new_grouped("tab3", O::TAB3, O::TABDLY), + Flag::new_grouped("bs0", O::BS0, O::BSDLY).sane(), + Flag::new_grouped("bs1", O::BS1, O::BSDLY), + Flag::new_grouped("vt0", O::VT0, O::VTDLY).sane(), + Flag::new_grouped("vt1", O::VT1, O::VTDLY), + Flag::new_grouped("ff0", O::FF0, O::FFDLY).sane(), + Flag::new_grouped("ff1", O::FF1, O::FFDLY), ]; pub const LOCAL_FLAGS: [Flag; 18] = [ diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 5cabe6da6..c53c01557 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -37,10 +37,7 @@ pub struct Flag { group: Option, } -impl Flag -where - T: Copy, -{ +impl Flag { pub const fn new(name: &'static str, flag: T) -> Self { Self { name, @@ -51,26 +48,25 @@ where } } - pub const fn hidden(&self) -> Self { - Self { - show: false, - ..*self - } - } - - pub const fn sane(&self) -> Self { - Self { - sane: true, - ..*self - } - } - - pub const fn group(&self, group: T) -> Self { + pub const fn new_grouped(name: &'static str, flag: T, group: T) -> Self { Self { + name, + flag, + show: true, + sane: false, group: Some(group), - ..*self } } + + pub const fn hidden(mut self) -> Self { + self.show = false; + self + } + + pub const fn sane(mut self) -> Self { + self.sane = true; + self + } } trait TermiosFlag: Copy { From 85e6f8659ffe81d9bbcc7f3e021e932f63b31a59 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Tue, 16 Aug 2022 11:16:33 +0200 Subject: [PATCH 12/17] stty: fix bsd/mac builds --- Cargo.lock | 2 +- src/uu/stty/src/flags.rs | 88 ++++++++++++++++------------------------ src/uu/stty/src/stty.rs | 63 +++++++++++++++++++++------- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c37e0ce6..e5d317659 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2913,7 +2913,7 @@ dependencies = [ name = "uu_stty" version = "0.0.14" dependencies = [ - "clap 3.1.18", + "clap 3.2.15", "nix", "uucore", ] diff --git a/src/uu/stty/src/flags.rs b/src/uu/stty/src/flags.rs index e564011b5..ad24fef30 100644 --- a/src/uu/stty/src/flags.rs +++ b/src/uu/stty/src/flags.rs @@ -9,13 +9,25 @@ // 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::{ - BaudRate, ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O, -}; -pub const CONTROL_FLAGS: [Flag; 12] = [ +#[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" +)))] +use nix::sys::termios::BaudRate; +use nix::sys::termios::{ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O}; + +pub const CONTROL_FLAGS: &[Flag] = &[ Flag::new("parenb", C::PARENB), Flag::new("parodd", C::PARODD), + #[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_arch = "mips")) + ))] Flag::new("cmspar", C::CMSPAR), Flag::new_grouped("cs5", C::CS5, C::CSIZE), Flag::new_grouped("cs6", C::CS6, C::CSIZE), @@ -28,7 +40,7 @@ pub const CONTROL_FLAGS: [Flag; 12] = [ Flag::new("crtscts", C::CRTSCTS), ]; -pub const INPUT_FLAGS: [Flag; 15] = [ +pub const INPUT_FLAGS: &[Flag] = &[ Flag::new("ignbrk", I::IGNBRK), Flag::new("brkint", I::BRKINT).sane(), Flag::new("ignpar", I::IGNPAR), @@ -48,8 +60,14 @@ pub const INPUT_FLAGS: [Flag; 15] = [ Flag::new("iutf8", I::IUTF8), ]; -pub const OUTPUT_FLAGS: [Flag; 24] = [ +pub const OUTPUT_FLAGS: &[Flag] = &[ Flag::new("opost", O::OPOST).sane(), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "linux", + target_os = "openbsd" + ))] Flag::new("olcuc", O::OLCUC), Flag::new("ocrnl", O::OCRNL), Flag::new("onlcr", O::ONLCR).sane(), @@ -75,7 +93,7 @@ pub const OUTPUT_FLAGS: [Flag; 24] = [ Flag::new_grouped("ff1", O::FF1, O::FFDLY), ]; -pub const LOCAL_FLAGS: [Flag; 18] = [ +pub const LOCAL_FLAGS: &[Flag] = &[ Flag::new("isig", L::ISIG).sane(), Flag::new("icanon", L::ICANON).sane(), Flag::new("iexten", L::IEXTEN).sane(), @@ -98,6 +116,15 @@ pub const LOCAL_FLAGS: [Flag; 18] = [ Flag::new("extproc", L::EXTPROC), ]; +// BSD's use u32 as baud rate, to using the enum is unnecessary. +#[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" +)))] pub const BAUD_RATES: &[(&str, BaudRate)] = &[ ("0", BaudRate::B0), ("50", BaudRate::B50), @@ -111,62 +138,17 @@ pub const BAUD_RATES: &[(&str, BaudRate)] = &[ ("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" - ))] + #[cfg(any(target_os = "android", target_os = "linux",))] ("921600", BaudRate::B921600), #[cfg(any(target_os = "android", target_os = "linux"))] ("1000000", BaudRate::B1000000), diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index c53c01557..0f5476279 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -19,7 +19,16 @@ use std::os::unix::io::{AsRawFd, RawFd}; use uucore::error::{UResult, USimpleError}; use uucore::{format_usage, InvalidEncodingHandling}; -use flags::{BAUD_RATES, CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS}; +#[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" +)))] +use flags::BAUD_RATES; +use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS}; const NAME: &str = "stty"; const USAGE: &str = "\ @@ -177,6 +186,28 @@ fn stty(opts: &Options) -> UResult<()> { fn print_terminal_size(termios: &Termios, opts: &Options) -> nix::Result<()> { let speed = cfgetospeed(termios); + + // BSDs use a u32 for the baud rate, so we can simply print it. + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + print!("speed {} baud; ", speed); + + // Other platforms need to use the baud rate enum, so printing the right value + // becomes slightly more complicated. + #[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + )))] for (text, baud_rate) in BAUD_RATES { if *baud_rate == speed { print!("speed {} baud; ", text); @@ -190,21 +221,25 @@ fn print_terminal_size(termios: &Termios, opts: &Options) -> nix::Result<()> { 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); + #[cfg(any(target_os = "linux", target_os = "redox"))] + { + // 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); + 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(()) } @@ -249,10 +284,10 @@ fn apply_setting(termios: &mut Termios, s: &str) -> ControlFlow { Some(s) => (true, s), None => (false, s), }; - apply_flag(termios, &CONTROL_FLAGS, name, remove)?; - apply_flag(termios, &INPUT_FLAGS, name, remove)?; - apply_flag(termios, &OUTPUT_FLAGS, name, remove)?; - apply_flag(termios, &LOCAL_FLAGS, name, remove)?; + apply_flag(termios, CONTROL_FLAGS, name, remove)?; + apply_flag(termios, INPUT_FLAGS, name, remove)?; + apply_flag(termios, OUTPUT_FLAGS, name, remove)?; + apply_flag(termios, LOCAL_FLAGS, name, remove)?; ControlFlow::Break(false) } From 6f6784f1137743fc0971d978b04ba51154c60a95 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 18 Aug 2022 10:52:25 +0200 Subject: [PATCH 13/17] Adjust the code after https://github.com/uutils/coreutils/pull/3832 --- src/uu/stty/src/stty.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 0f5476279..5303e3959 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -17,7 +17,7 @@ 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 uucore::format_usage; #[cfg(not(any( target_os = "freebsd", @@ -138,8 +138,7 @@ ioctl_write_ptr_bad!( #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args - .collect_str(InvalidEncodingHandling::ConvertLossy) - .accept_any(); + .collect_lossy(); let matches = uu_app().get_matches_from(args); From 0e14e1ded08b3232b7c936ce157f61e059379d42 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Fri, 19 Aug 2022 12:30:31 +0200 Subject: [PATCH 14/17] stty: add cfg guards for flags that don't exist on BSD fix another flag --- src/uu/stty/src/flags.rs | 134 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/src/uu/stty/src/flags.rs b/src/uu/stty/src/flags.rs index ad24fef30..2c3f00eec 100644 --- a/src/uu/stty/src/flags.rs +++ b/src/uu/stty/src/flags.rs @@ -57,6 +57,7 @@ pub const INPUT_FLAGS: &[Flag] = &[ // Flag::new("iuclc", I::IUCLC), Flag::new("ixany", I::IXANY), Flag::new("imaxbel", I::IMAXBEL).sane(), + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] Flag::new("iutf8", I::IUTF8), ]; @@ -73,23 +74,156 @@ pub const OUTPUT_FLAGS: &[Flag] = &[ Flag::new("onlcr", O::ONLCR).sane(), Flag::new("onocr", O::ONOCR), Flag::new("onlret", O::ONLRET), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new("ofill", O::OFILL), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new("ofdel", O::OFDEL), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("nl0", O::NL0, O::NLDLY).sane(), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("nl1", O::NL1, O::NLDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("cr0", O::CR0, O::CRDLY).sane(), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("cr1", O::CR1, O::CRDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("cr2", O::CR2, O::CRDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("cr3", O::CR3, O::CRDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("tab0", O::TAB0, O::TABDLY).sane(), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("tab1", O::TAB1, O::TABDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("tab2", O::TAB2, O::TABDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("tab3", O::TAB3, O::TABDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("bs0", O::BS0, O::BSDLY).sane(), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("bs1", O::BS1, O::BSDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("vt0", O::VT0, O::VTDLY).sane(), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("vt1", O::VT1, O::VTDLY), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("ff0", O::FF0, O::FFDLY).sane(), + #[cfg(any( + target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] Flag::new_grouped("ff1", O::FF1, O::FFDLY), ]; From a1250ec4620190d6d4f231c12d727f2b4f36b331 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Fri, 19 Aug 2022 12:34:51 +0200 Subject: [PATCH 15/17] stty: fix nix version --- src/uu/stty/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/stty/Cargo.toml b/src/uu/stty/Cargo.toml index d39786494..919dd288b 100644 --- a/src/uu/stty/Cargo.toml +++ b/src/uu/stty/Cargo.toml @@ -17,7 +17,7 @@ path = "src/stty.rs" [dependencies] clap = { version = "3.1", features = ["wrap_help", "cargo"] } uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } -nix = { version="0.24.1", features = ["term"] } +nix = { version="0.25", features = ["term"] } [[bin]] name = "stty" From c269a386eb59fe454c2f8982ff1f6d26bf7e60f3 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Fri, 19 Aug 2022 13:12:43 +0200 Subject: [PATCH 16/17] stty: cargo fmt --- src/uu/stty/src/stty.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 5303e3959..23c1c357c 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -137,8 +137,7 @@ ioctl_write_ptr_bad!( #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let args = args - .collect_lossy(); + let args = args.collect_lossy(); let matches = uu_app().get_matches_from(args); From afda6be8c7ac0b970bbe052dbdfe55cab7a54946 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Sat, 20 Aug 2022 01:02:53 +0200 Subject: [PATCH 17/17] Update clap from 3.2.15 to 3.2.17 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e5d317659..5f1d64758 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2913,7 +2913,7 @@ dependencies = [ name = "uu_stty" version = "0.0.14" dependencies = [ - "clap 3.2.15", + "clap 3.2.17", "nix", "uucore", ]