1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Implements status=LEVEL parser.

This commit is contained in:
Tyler 2021-06-15 12:19:18 -07:00
parent 8141919064
commit 06dcdc0f1f
3 changed files with 171 additions and 31 deletions

View file

@ -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"
)
}
);

View file

@ -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<Self, Self::Err>
{
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<usize, ParseError>
{
match s
@ -251,7 +272,7 @@ fn parse_bytes_with_opt_multiplier(s: String) -> Result<usize, ParseError>
{
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<Option<usize>, ParseError>
pub fn parse_status_level(matches: &getopts::Matches) -> Result<Option<StatusLevel>, 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<usize, ParseError>
@ -321,45 +351,55 @@ pub fn parse_obs(matches: &getopts::Matches) -> Result<usize, ParseError>
fn parse_ctable(fmt: Option<ConvFlag>, case: Option<ConvFlag>) -> 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,
}

View file

@ -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]