From 06dcdc0f1f952e84007531066b55d6f526f94ee1 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 15 Jun 2021 12:19:18 -0700 Subject: [PATCH] Implements status=LEVEL parser. --- src/uu/dd/src/dd.rs | 39 +++++++++- src/uu/dd/src/parseargs.rs | 100 ++++++++++++++++++-------- src/uu/dd/src/parseargs/unit_tests.rs | 63 ++++++++++++++++ 3 files changed, 171 insertions(+), 31 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 8d1c7121a..db1c077a1 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -164,7 +164,7 @@ pub struct OFlags /// The value of the status cl-option. /// Controls printing of transfer stats -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum StatusLevel { Progress, @@ -1316,6 +1316,43 @@ macro_rules! build_app ( "Set the conversion block size to BYTES. When converting variable-length records to fixed-length ones (‘conv=block’) or the reverse (‘conv=unblock’), use BYTES as the fixed record length.", "BYTES" ) + .optopt( + "", + "status", + "Specify the amount of information printed. If this operand is + given multiple times, the last one takes precedence. The LEVEL + value can be one of the following: + + ‘none’ + Do not print any informational or warning messages to stderr. + Error messages are output as normal. + + ‘noxfer’ + Do not print the final transfer rate and volume statistics + that normally make up the last status line. + + ‘progress’ + Print the transfer rate and volume statistics on stderr, when + processing each input block. Statistics are output on a + single line at most once every second, but updates can be + delayed when waiting on I/O. + + Transfer information is normally output to stderr upon receipt of + the ‘INFO’ signal or when ‘dd’ exits, and defaults to the following + form in the C locale: + + 7287+1 records in + 116608+0 records out + 59703296 bytes (60 MB, 57 MiB) copied, 0.0427974 s, 1.4 GB/s + + The notation ‘W+P’ stands for W whole blocks and P partial blocks. + A partial block occurs when a read or write operation succeeds but + transfers less data than the block size. An additional line like + ‘1 truncated record’ or ‘10 truncated records’ is output after the + ‘records out’ line if ‘conv=block’ processing truncated one or more + input records.", + "LEVEL" + ) } ); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 620f18250..b4ee0efb2 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -27,6 +27,7 @@ pub enum ParseError ByteStringContainsNoValue(String), MultiplierStringWouldOverflow(String), BlockUnblockWithoutCBS, + StatusLevelNotRecognized(String), } impl std::fmt::Display for ParseError @@ -189,6 +190,26 @@ impl std::str::FromStr for Flag } } +impl std::str::FromStr for StatusLevel +{ + type Err = ParseError; + + fn from_str(s: &str) -> Result + { + match s + { + "none" => + Ok(StatusLevel::None), + "noxfer" => + Ok(StatusLevel::Noxfer), + "progress" => + Ok(StatusLevel::Progress), + _ => + Err(ParseError::StatusLevelNotRecognized(s.to_string())), + } + } +} + fn parse_multiplier<'a>(s: &'a str) -> Result { match s @@ -251,7 +272,7 @@ fn parse_bytes_with_opt_multiplier(s: String) -> Result { if let Some(idx) = s.find(char::is_alphabetic) { - let base = parse_bytes_only(&s[0..idx])?; + let base = parse_bytes_only(&s[..idx])?; let mult = parse_multiplier(&s[idx..])?; if let Some(bytes) = base.checked_mul(mult) @@ -300,7 +321,16 @@ fn parse_cbs(matches: &getopts::Matches) -> Result, ParseError> pub fn parse_status_level(matches: &getopts::Matches) -> Result, ParseError> { - unimplemented!() + match matches.opt_str("status") + { + Some(s) => + { + let st = s.parse()?; + Ok(Some(st)) + }, + None => + Ok(None), + } } pub fn parse_obs(matches: &getopts::Matches) -> Result @@ -321,45 +351,55 @@ pub fn parse_obs(matches: &getopts::Matches) -> Result fn parse_ctable(fmt: Option, case: Option) -> Option<&'static ConversionTable> { + fn parse_conv_and_case_table(fmt: &ConvFlag, case: &ConvFlag) -> Option<&'static ConversionTable> + { + match (fmt, case) + { + (ConvFlag::FmtAtoE, ConvFlag::UCase) => + Some(&ASCII_TO_EBCDIC_LCASE_TO_UCASE), + (ConvFlag::FmtAtoE, ConvFlag::LCase) => + Some(&ASCII_TO_EBCDIC_UCASE_TO_LCASE), + (ConvFlag::FmtEtoA, ConvFlag::UCase) => + Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE), + (ConvFlag::FmtEtoA, ConvFlag::LCase) => + Some(&EBCDIC_TO_ASCII_UCASE_TO_LCASE), + (ConvFlag::FmtAtoI, ConvFlag::UCase) => + Some(&ASCII_TO_IBM_UCASE_TO_LCASE), + (ConvFlag::FmtAtoI, ConvFlag::LCase) => + Some(&ASCII_TO_IBM_LCASE_TO_UCASE), + (_, _) => + None, + } + } + fn parse_conv_table_only(fmt: &ConvFlag) -> Option<&'static ConversionTable> + { + match fmt + { + ConvFlag::FmtAtoE => + Some(&ASCII_TO_EBCDIC), + ConvFlag::FmtEtoA => + Some(&EBCDIC_TO_ASCII), + ConvFlag::FmtAtoI => + Some(&ASCII_TO_IBM), + _ => + None, + } + } + // ------------------------------------------------------------------------ match (fmt, case) { // Both [ascii | ebcdic | ibm] and [lcase | ucase] specified (Some(fmt), Some(case)) => - match (fmt, case) - { - (ConvFlag::FmtAtoE, ConvFlag::UCase) => - Some(&ASCII_TO_EBCDIC_LCASE_TO_UCASE), - (ConvFlag::FmtAtoE, ConvFlag::LCase) => - Some(&ASCII_TO_EBCDIC_UCASE_TO_LCASE), - (ConvFlag::FmtEtoA, ConvFlag::UCase) => - Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE), - (ConvFlag::FmtEtoA, ConvFlag::LCase) => - Some(&EBCDIC_TO_ASCII_UCASE_TO_LCASE), - (ConvFlag::FmtAtoI, ConvFlag::UCase) => - Some(&ASCII_TO_IBM_UCASE_TO_LCASE), - (ConvFlag::FmtAtoI, ConvFlag::LCase) => - Some(&ASCII_TO_IBM_LCASE_TO_UCASE), - (_, _) => - None, - }, + parse_conv_and_case_table(&fmt, &case), // Only [ascii | ebcdic | ibm] specified (Some(fmt), None) => - match fmt - { - ConvFlag::FmtAtoE => - Some(&ASCII_TO_EBCDIC), - ConvFlag::FmtEtoA => - Some(&EBCDIC_TO_ASCII), - ConvFlag::FmtAtoI => - Some(&ASCII_TO_IBM), - _ => - None, - }, + parse_conv_table_only(&fmt), // Only [lcase | ucase] specified (None, Some(ConvFlag::UCase)) => Some(&ASCII_LCASE_TO_UCASE), (None, Some(ConvFlag::LCase)) => Some(&ASCII_UCASE_TO_LCASE), + // ST else... (_, _) => None, } diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 33e082a1b..19ff317de 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -7,6 +7,69 @@ use crate::{ StatusLevel, }; +#[test] +fn test_status_level_absent() +{ + let args = vec![ + String::from("dd"), + String::from("--if=foo.file"), + String::from("--of=bar.file"), + ]; + + let matches = build_app!().parse(args); + let st = parse_status_level(&matches).unwrap(); + + assert_eq!(st, None); +} + +#[test] +fn test_status_level_none() +{ + let args = vec![ + String::from("dd"), + String::from("--status=none"), + String::from("--if=foo.file"), + String::from("--of=bar.file"), + ]; + + let matches = build_app!().parse(args); + let st = parse_status_level(&matches).unwrap().unwrap(); + + assert_eq!(st, StatusLevel::None); +} + +#[test] +fn test_status_level_progress() +{ + let args = vec![ + String::from("dd"), + String::from("--if=foo.file"), + String::from("--of=bar.file"), + String::from("--status=progress"), + ]; + + let matches = build_app!().parse(args); + let st = parse_status_level(&matches).unwrap().unwrap(); + + assert_eq!(st, StatusLevel::Progress); +} + +#[test] +fn test_status_level_noxfer() +{ + let args = vec![ + String::from("dd"), + String::from("--if=foo.file"), + String::from("--status=noxfer"), + String::from("--of=bar.file"), + ]; + + let matches = build_app!().parse(args); + let st = parse_status_level(&matches).unwrap().unwrap(); + + assert_eq!(st, StatusLevel::Noxfer); +} + // ----- IConvFlags/Output ----- #[test]