mirror of
https://github.com/uutils/coreutils
synced 2024-11-16 09:48:03 +00:00
dd: move ConversionMode parsing to parseargs mod.
Move the code for parsing the `ConversionMode` to use up to the `parseargs` module. This location makes more sense for it because the conversion mode can be determined entirely from the command-line arguments at the time of parsing just like the other parameters. Using an enum for this purpose also eliminates the amount of code we need later on.
This commit is contained in:
parent
b98bccf9cc
commit
f856bfc479
4 changed files with 85 additions and 73 deletions
|
@ -33,10 +33,8 @@ pub(crate) enum ConversionMode<'a> {
|
|||
|
||||
/// Stores all Conv Flags that apply to the input
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct IConvFlags {
|
||||
pub ctable: Option<&'static ConversionTable>,
|
||||
pub block: Option<Cbs>,
|
||||
pub unblock: Option<Cbs>,
|
||||
pub(crate) struct IConvFlags {
|
||||
pub mode: Option<ConversionMode<'static>>,
|
||||
pub swab: bool,
|
||||
pub sync: Option<u8>,
|
||||
pub noerror: bool,
|
||||
|
|
|
@ -45,7 +45,6 @@ const BUF_INIT_BYTE: u8 = 0xDD;
|
|||
|
||||
struct Input<R: Read> {
|
||||
src: R,
|
||||
non_ascii: bool,
|
||||
ibs: usize,
|
||||
print_level: Option<StatusLevel>,
|
||||
count: Option<CountType>,
|
||||
|
@ -56,7 +55,6 @@ struct Input<R: Read> {
|
|||
impl Input<io::Stdin> {
|
||||
fn new(matches: &Matches) -> UResult<Self> {
|
||||
let ibs = parseargs::parse_ibs(matches)?;
|
||||
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
||||
let print_level = parseargs::parse_status_level(matches)?;
|
||||
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
||||
let iflags = parseargs::parse_iflags(matches)?;
|
||||
|
@ -67,7 +65,6 @@ impl Input<io::Stdin> {
|
|||
|
||||
let mut i = Self {
|
||||
src: io::stdin(),
|
||||
non_ascii,
|
||||
ibs,
|
||||
print_level,
|
||||
count,
|
||||
|
@ -131,7 +128,6 @@ fn make_linux_iflags(iflags: &IFlags) -> Option<libc::c_int> {
|
|||
impl Input<File> {
|
||||
fn new(matches: &Matches) -> UResult<Self> {
|
||||
let ibs = parseargs::parse_ibs(matches)?;
|
||||
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
||||
let print_level = parseargs::parse_status_level(matches)?;
|
||||
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
||||
let iflags = parseargs::parse_iflags(matches)?;
|
||||
|
@ -163,7 +159,6 @@ impl Input<File> {
|
|||
|
||||
let i = Self {
|
||||
src,
|
||||
non_ascii,
|
||||
ibs,
|
||||
print_level,
|
||||
count,
|
||||
|
@ -605,47 +600,6 @@ impl Write for Output<io::Stdout> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given the various command-line parameters, determine the conversion mode.
|
||||
///
|
||||
/// The `conv` command-line option can take many different values,
|
||||
/// each of which may combine with others. For example, `conv=ascii`,
|
||||
/// `conv=lcase`, `conv=sync`, and so on. The arguments to this
|
||||
/// function represent the settings of those various command-line
|
||||
/// parameters. This function translates those settings to a
|
||||
/// [`ConversionMode`].
|
||||
fn conversion_mode(
|
||||
ctable: Option<&ConversionTable>,
|
||||
block: Option<usize>,
|
||||
unblock: Option<usize>,
|
||||
non_ascii: bool,
|
||||
is_sync: bool,
|
||||
) -> Option<ConversionMode> {
|
||||
match (ctable, block, unblock) {
|
||||
(Some(ct), None, None) => Some(ConversionMode::ConvertOnly(ct)),
|
||||
(Some(ct), Some(cbs), None) => {
|
||||
if non_ascii {
|
||||
Some(ConversionMode::ConvertThenBlock(ct, cbs, is_sync))
|
||||
} else {
|
||||
Some(ConversionMode::BlockThenConvert(ct, cbs, is_sync))
|
||||
}
|
||||
}
|
||||
(Some(ct), None, Some(cbs)) => {
|
||||
if non_ascii {
|
||||
Some(ConversionMode::ConvertThenUnblock(ct, cbs))
|
||||
} else {
|
||||
Some(ConversionMode::UnblockThenConvert(ct, cbs))
|
||||
}
|
||||
}
|
||||
(None, Some(cbs), None) => Some(ConversionMode::BlockOnly(cbs, is_sync)),
|
||||
(None, None, Some(cbs)) => Some(ConversionMode::UnblockOnly(cbs)),
|
||||
(None, None, None) => None,
|
||||
// The remaining variants should never happen because the
|
||||
// argument parsing above should result in an error before
|
||||
// getting to this line of code.
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read helper performs read operations common to all dd reads, and dispatches the buffer to relevant helper functions as dictated by the operations requested by the user.
|
||||
fn read_helper<R: Read>(i: &mut Input<R>, bsize: usize) -> std::io::Result<(ReadStat, Vec<u8>)> {
|
||||
// Local Helper Fns -------------------------------------------------
|
||||
|
@ -671,14 +625,7 @@ fn read_helper<R: Read>(i: &mut Input<R>, bsize: usize) -> std::io::Result<(Read
|
|||
perform_swab(&mut buf);
|
||||
}
|
||||
|
||||
let mode = conversion_mode(
|
||||
i.cflags.ctable,
|
||||
i.cflags.block,
|
||||
i.cflags.unblock,
|
||||
i.non_ascii,
|
||||
i.cflags.sync.is_some(),
|
||||
);
|
||||
match mode {
|
||||
match i.cflags.mode {
|
||||
Some(ref mode) => {
|
||||
let buf = conv_block_unblock_helper(buf, mode, &mut rstat);
|
||||
Ok((rstat, buf))
|
||||
|
@ -1129,7 +1076,6 @@ mod tests {
|
|||
src: LazyReader {
|
||||
src: File::open("./test-resources/deadbeef-16.test").unwrap(),
|
||||
},
|
||||
non_ascii: false,
|
||||
ibs: 16,
|
||||
print_level: None,
|
||||
count: None,
|
||||
|
@ -1176,7 +1122,6 @@ mod tests {
|
|||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test")
|
||||
.unwrap(),
|
||||
},
|
||||
non_ascii: false,
|
||||
ibs: 521,
|
||||
print_level: None,
|
||||
count: None,
|
||||
|
|
|
@ -535,9 +535,50 @@ fn parse_flag_list<T: std::str::FromStr<Err = ParseError>>(
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Given the various command-line parameters, determine the conversion mode.
|
||||
///
|
||||
/// The `conv` command-line option can take many different values,
|
||||
/// each of which may combine with others. For example, `conv=ascii`,
|
||||
/// `conv=lcase`, `conv=sync`, and so on. The arguments to this
|
||||
/// function represent the settings of those various command-line
|
||||
/// parameters. This function translates those settings to a
|
||||
/// [`ConversionMode`].
|
||||
fn conversion_mode(
|
||||
ctable: Option<&ConversionTable>,
|
||||
block: Option<usize>,
|
||||
unblock: Option<usize>,
|
||||
non_ascii: bool,
|
||||
is_sync: bool,
|
||||
) -> Option<ConversionMode> {
|
||||
match (ctable, block, unblock) {
|
||||
(Some(ct), None, None) => Some(ConversionMode::ConvertOnly(ct)),
|
||||
(Some(ct), Some(cbs), None) => {
|
||||
if non_ascii {
|
||||
Some(ConversionMode::ConvertThenBlock(ct, cbs, is_sync))
|
||||
} else {
|
||||
Some(ConversionMode::BlockThenConvert(ct, cbs, is_sync))
|
||||
}
|
||||
}
|
||||
(Some(ct), None, Some(cbs)) => {
|
||||
if non_ascii {
|
||||
Some(ConversionMode::ConvertThenUnblock(ct, cbs))
|
||||
} else {
|
||||
Some(ConversionMode::UnblockThenConvert(ct, cbs))
|
||||
}
|
||||
}
|
||||
(None, Some(cbs), None) => Some(ConversionMode::BlockOnly(cbs, is_sync)),
|
||||
(None, None, Some(cbs)) => Some(ConversionMode::UnblockOnly(cbs)),
|
||||
(None, None, None) => None,
|
||||
// The remaining variants should never happen because the
|
||||
// argument parsing above should result in an error before
|
||||
// getting to this line of code.
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse Conversion Options (Input Variety)
|
||||
/// Construct and validate a IConvFlags
|
||||
pub fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError> {
|
||||
pub(crate) fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError> {
|
||||
let mut iconvflags = IConvFlags::default();
|
||||
let mut fmt = None;
|
||||
let mut case = None;
|
||||
|
@ -546,6 +587,9 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError
|
|||
let flags = parse_flag_list(options::CONV, matches)?;
|
||||
let cbs = parse_cbs(matches)?;
|
||||
|
||||
let mut block = None;
|
||||
let mut unblock = None;
|
||||
|
||||
for flag in flags {
|
||||
match flag {
|
||||
ConvFlag::FmtEtoA => {
|
||||
|
@ -565,7 +609,7 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError
|
|||
//
|
||||
// -- https://www.gnu.org/software/coreutils/manual/html_node/dd-invocation.html
|
||||
if cbs.is_some() {
|
||||
iconvflags.unblock = cbs;
|
||||
unblock = cbs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -585,7 +629,7 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError
|
|||
//
|
||||
// -- https://www.gnu.org/software/coreutils/manual/html_node/dd-invocation.html
|
||||
if cbs.is_some() {
|
||||
iconvflags.block = cbs;
|
||||
block = cbs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -603,13 +647,13 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError
|
|||
case = Some(flag);
|
||||
}
|
||||
}
|
||||
ConvFlag::Block => match (cbs, iconvflags.unblock) {
|
||||
(Some(cbs), None) => iconvflags.block = Some(cbs),
|
||||
ConvFlag::Block => match (cbs, unblock) {
|
||||
(Some(cbs), None) => block = Some(cbs),
|
||||
(None, _) => return Err(ParseError::BlockUnblockWithoutCBS),
|
||||
(_, Some(_)) => return Err(ParseError::MultipleBlockUnblock),
|
||||
},
|
||||
ConvFlag::Unblock => match (cbs, iconvflags.block) {
|
||||
(Some(cbs), None) => iconvflags.unblock = Some(cbs),
|
||||
ConvFlag::Unblock => match (cbs, block) {
|
||||
(Some(cbs), None) => unblock = Some(cbs),
|
||||
(None, _) => return Err(ParseError::BlockUnblockWithoutCBS),
|
||||
(_, Some(_)) => return Err(ParseError::MultipleBlockUnblock),
|
||||
},
|
||||
|
@ -630,7 +674,7 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError
|
|||
// block implies sync with ' '
|
||||
// unblock implies sync with 0
|
||||
// So the final value can't be set until all flags are parsed.
|
||||
let sync = if is_sync && (iconvflags.block.is_some() || iconvflags.unblock.is_some()) {
|
||||
let sync = if is_sync && (block.is_some() || unblock.is_some()) {
|
||||
Some(b' ')
|
||||
} else if is_sync {
|
||||
Some(0u8)
|
||||
|
@ -638,8 +682,27 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result<IConvFlags, ParseError
|
|||
None
|
||||
};
|
||||
|
||||
// Some user options, such as the presence of conversion tables,
|
||||
// will determine whether the input is assumed to be ascii. This
|
||||
// parser sets the non_ascii flag accordingly.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - If conv=ebcdic or conv=ibm is specified then block,
|
||||
// unblock or swab must be performed before the conversion
|
||||
// happens since the source will start in ascii.
|
||||
// - If conv=ascii is specified then block, unblock or swab
|
||||
// must be performed after the conversion since the source
|
||||
// starts in ebcdic.
|
||||
// - If no conversion is specified then the source is assumed
|
||||
// to be in ascii.
|
||||
//
|
||||
// For more info see `info dd`.
|
||||
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
||||
let mode = conversion_mode(ctable, block, unblock, non_ascii, is_sync);
|
||||
|
||||
Ok(IConvFlags {
|
||||
ctable,
|
||||
mode,
|
||||
sync,
|
||||
..iconvflags
|
||||
})
|
||||
|
|
|
@ -170,8 +170,11 @@ fn test_all_top_level_args_no_leading_dashes() {
|
|||
);
|
||||
assert_eq!(
|
||||
IConvFlags {
|
||||
ctable: Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE),
|
||||
unblock: Some(1), // because ascii implies unblock
|
||||
// ascii implies unblock
|
||||
mode: Some(ConversionMode::ConvertThenUnblock(
|
||||
&EBCDIC_TO_ASCII_LCASE_TO_UCASE,
|
||||
1
|
||||
)),
|
||||
..IConvFlags::default()
|
||||
},
|
||||
parse_conv_flag_input(&matches).unwrap()
|
||||
|
@ -269,8 +272,11 @@ fn test_all_top_level_args_with_leading_dashes() {
|
|||
);
|
||||
assert_eq!(
|
||||
IConvFlags {
|
||||
ctable: Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE),
|
||||
unblock: Some(1), // because ascii implies unblock
|
||||
// ascii implies unblock
|
||||
mode: Some(ConversionMode::ConvertThenUnblock(
|
||||
&EBCDIC_TO_ASCII_LCASE_TO_UCASE,
|
||||
1
|
||||
)),
|
||||
..IConvFlags::default()
|
||||
},
|
||||
parse_conv_flag_input(&matches).unwrap()
|
||||
|
|
Loading…
Reference in a new issue