Implements functionality for seeking output files

- Adds duplicate dd fn :-( for differentiating between File backed and
  non-File outputs.
- Implements cflag=sparse,fsync,fdatasync which were previously blocked.
- Adds plumbing for IFlags & OFlags incl parsing.
- Partial impl for seek=N and skip=N which were previously blocked.
This commit is contained in:
Tyler
2021-04-26 15:17:48 -07:00
parent 3c3af72d9a
commit 4d7be2f098
4 changed files with 612 additions and 129 deletions
+247 -77
View File
File diff suppressed because it is too large Load Diff
+65 -23
View File
@@ -9,7 +9,7 @@ use hex_literal::hex;
// use tempfile::tempfile;
// TODO: (Maybe) Use tempfiles in the tests.
const DEFAULT_CFO: ConvFlagOutput = ConvFlagOutput {
const DEFAULT_CFO: OConvFlags = OConvFlags {
sparse: false,
excl: false,
nocreat: false,
@@ -18,15 +18,52 @@ const DEFAULT_CFO: ConvFlagOutput = ConvFlagOutput {
fsync: false,
};
const DEFAULT_IFLAGS: IFlags = IFlags {
cio: false,
direct: false,
directory: false,
dsync: false,
sync: false,
nocache: false,
nonblock: false,
noatime: false,
noctty: false,
nofollow: false,
nolinks: false,
binary: false,
text: false,
fullblock: false,
count_bytes: false,
skip_bytes: false,
};
const DEFAULT_OFLAGS: OFlags = OFlags {
append: false,
cio: false,
direct: false,
directory: false,
dsync: false,
sync: false,
nocache: false,
nonblock: false,
noatime: false,
noctty: false,
nofollow: false,
nolinks: false,
binary: false,
text: false,
seek_bytes: false,
};
#[macro_export]
macro_rules! cfi (
macro_rules! icf (
() =>
{
cfi!(None)
icf!(None)
};
( $ctable:expr ) =>
{
ConvFlagInput {
IConvFlags {
ctable: $ctable,
block: false,
unblock: false,
@@ -51,12 +88,13 @@ macro_rules! make_spec_test (
src: $src,
ibs: 512,
xfer_stats: StatusLevel::None,
cf: cfi!(),
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(),
obs: 512,
cf: DEFAULT_CFO,
cflags: DEFAULT_CFO,
},
$spec,
format!("./test-resources/FAILED-{}.test", $test_name)
@@ -67,7 +105,7 @@ macro_rules! make_spec_test (
#[test]
fn $test_id()
{
dd($i,$o).unwrap();
dd_fileout($i,$o).unwrap();
let res = File::open($tmp_fname).unwrap();
let res = BufReader::new(res);
@@ -94,12 +132,13 @@ macro_rules! make_conv_test (
src: $src,
ibs: 512,
xfer_stats: StatusLevel::None,
cf: cfi!($ctable),
cflags: icf!($ctable),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(),
obs: 512,
cf: DEFAULT_CFO,
cflags: DEFAULT_CFO,
},
$spec,
format!("./test-resources/FAILED-{}.test", $test_name)
@@ -107,8 +146,8 @@ macro_rules! make_conv_test (
};
);
macro_rules! make_cfi_test (
( $test_id:ident, $test_name:expr, $src:expr, $cfi:expr, $spec:expr ) =>
macro_rules! make_icf_test (
( $test_id:ident, $test_name:expr, $src:expr, $icf:expr, $spec:expr ) =>
{
make_spec_test!($test_id,
$test_name,
@@ -116,12 +155,13 @@ macro_rules! make_cfi_test (
src: $src,
ibs: 512,
xfer_stats: StatusLevel::None,
cf: $cfi,
cflags: $icf,
iflags: DEFAULT_IFLAGS,
},
Output {
dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(),
obs: 512,
cf: DEFAULT_CFO,
cflags: DEFAULT_CFO,
},
$spec,
format!("./test-resources/FAILED-{}.test", $test_name)
@@ -240,16 +280,17 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test()
src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(),
ibs: 128,
xfer_stats: StatusLevel::None,
cf: cfi!(Some(&ASCII_TO_EBCDIC)),
cflags: icf!(Some(&ASCII_TO_EBCDIC)),
iflags: DEFAULT_IFLAGS,
};
let o = Output {
dst: File::create(&tmp_fname_ae).unwrap(),
obs: 1024,
cf: DEFAULT_CFO,
cflags: DEFAULT_CFO,
};
dd(i,o).unwrap();
dd_fileout(i,o).unwrap();
// EBCDIC->ASCII
let test_name = "all-valid-ebcdic-to-ascii";
@@ -259,16 +300,17 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test()
src: File::open(&tmp_fname_ae).unwrap(),
ibs: 256,
xfer_stats: StatusLevel::None,
cf: cfi!(Some(&EBCDIC_TO_ASCII)),
cflags: icf!(Some(&EBCDIC_TO_ASCII)),
iflags: DEFAULT_IFLAGS,
};
let o = Output {
dst: File::create(&tmp_fname_ea).unwrap(),
obs: 1024,
cf: DEFAULT_CFO,
cflags: DEFAULT_CFO,
};
dd(i,o).unwrap();
dd_fileout(i,o).unwrap();
let res = {
let res = File::open(&tmp_fname_ea).unwrap();
@@ -289,11 +331,11 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test()
fs::remove_file(&tmp_fname_ea).unwrap();
}
make_cfi_test!(
make_icf_test!(
swab_256_test,
"swab-256",
File::open("./test-resources/seq-byte-values.test").unwrap(),
ConvFlagInput {
IConvFlags {
ctable: None,
block: false,
unblock: false,
@@ -304,11 +346,11 @@ make_cfi_test!(
File::open("./test-resources/seq-byte-values-swapped.test").unwrap()
);
make_cfi_test!(
make_icf_test!(
swab_257_test,
"swab-257",
File::open("./test-resources/seq-byte-values-odd.test").unwrap(),
ConvFlagInput {
IConvFlags {
ctable: None,
block: false,
unblock: false,
+283 -12
View File
@@ -3,13 +3,15 @@ mod test;
use crate::conversion_tables::*;
use crate::{
ConvFlagInput, ConvFlagOutput,
IConvFlags, OConvFlags,
StatusLevel,
};
use crate::{
IFlags, OFlags,
};
use std::error::Error;
/// Parser Errors describe errors with input
#[derive(Debug)]
pub enum ParseError
@@ -18,6 +20,7 @@ pub enum ParseError
MultipleUCaseLCase,
MultipleBlockUnblock,
MultipleExclNoCreat,
FlagNoMatch(String),
ConvFlagNoMatch(String),
NoMatchingMultiplier(String),
MultiplierStringContainsNoValue(String),
@@ -106,6 +109,84 @@ impl std::str::FromStr for ConvFlag
}
}
enum Flag
{
// Input only
FullBlock,
CountBytes,
SkipBytes,
// Either
Cio,
Direct,
Directory,
Dsync,
Sync,
NoCache,
NonBlock,
NoATime,
NoCtty,
NoFollow,
NoLinks,
Binary,
Text,
// Output only
Append,
SeekBytes,
}
impl std::str::FromStr for Flag
{
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err>
{
match s
{
// Input only
"fullblock" =>
Ok(Self::FullBlock),
"count_bytes" =>
Ok(Self::CountBytes),
"skip_bytes" =>
Ok(Self::SkipBytes),
// Either
"cio" =>
Ok(Self::Cio),
"direct" =>
Ok(Self::Direct),
"directory" =>
Ok(Self::Directory),
"dsync" =>
Ok(Self::Dsync),
"sync" =>
Ok(Self::Sync),
"nocache" =>
Ok(Self::NoCache),
"nonblock" =>
Ok(Self::NonBlock),
"noatime" =>
Ok(Self::NoATime),
"noctty" =>
Ok(Self::NoCtty),
"nofollow" =>
Ok(Self::NoFollow),
"nolinks" =>
Ok(Self::NoLinks),
"binary" =>
Ok(Self::Binary),
"text" =>
Ok(Self::Text),
// Output only
"append" =>
Ok(Self::Append),
"seek_bytes" =>
Ok(Self::SeekBytes),
_ =>
Err(ParseError::FlagNoMatch(String::from(s))),
}
}
}
fn parse_multiplier<'a>(s: &'a str) -> Result<usize, ParseError>
{
match s
@@ -269,11 +350,11 @@ fn parse_ctable(fmt: Option<ConvFlag>, case: Option<ConvFlag>) -> Option<&'stati
}
}
fn parse_conv_opts(matches: &getopts::Matches) -> Result<Vec<ConvFlag>, ParseError>
fn parse_flag_list<T: std::str::FromStr<Err = ParseError>>(tag: &str, matches: &getopts::Matches) -> Result<Vec<T>, ParseError>
{
let mut flags = Vec::new();
if let Some(comma_str) = matches.opt_str("conv")
if let Some(comma_str) = matches.opt_str(tag)
{
for s in comma_str.split(",")
{
@@ -286,10 +367,10 @@ fn parse_conv_opts(matches: &getopts::Matches) -> Result<Vec<ConvFlag>, ParseErr
}
/// Parse Conversion Options (Input Variety)
/// Construct and validate a ConvFlagInput
pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result<ConvFlagInput, ParseError>
/// Construct and validate a IConvFlags
pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result<IConvFlags, ParseError>
{
let flags = parse_conv_opts(matches)?;
let flags = parse_flag_list("conv", matches)?;
let mut fmt = None;
let mut case = None;
@@ -378,7 +459,7 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result<ConvFlagInput
let ctable = parse_ctable(fmt, case);
Ok(ConvFlagInput {
Ok(IConvFlags {
ctable,
block,
unblock,
@@ -389,10 +470,10 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result<ConvFlagInput
}
/// Parse Conversion Options (Output Variety)
/// Construct and validate a ConvFlagOutput
pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result<ConvFlagOutput, ParseError>
/// Construct and validate a OConvFlags
pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result<OConvFlags, ParseError>
{
let flags = parse_conv_opts(matches)?;
let flags = parse_flag_list("conv", matches)?;
let mut sparse = false;
let mut excl = false;
@@ -435,7 +516,7 @@ pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result<ConvFlagOutp
}
}
Ok(ConvFlagOutput {
Ok(OConvFlags {
sparse,
excl,
nocreat,
@@ -444,3 +525,193 @@ pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result<ConvFlagOutp
fsync,
})
}
/// Parse IFlags struct from CL-input
pub fn parse_iflags(matches: &getopts::Matches) -> Result<IFlags, ParseError>
{
let mut cio = false;
let mut direct = false;
let mut directory = false;
let mut dsync = false;
let mut sync = false;
let mut nocache = false;
let mut nonblock = false;
let mut noatime = false;
let mut noctty = false;
let mut nofollow = false;
let mut nolinks = false;
let mut binary = false;
let mut text = false;
let mut fullblock = false;
let mut count_bytes = false;
let mut skip_bytes = false;
let flags = parse_flag_list("iflag", matches)?;
for flag in flags
{
match flag
{
Flag::Cio =>
cio = true,
Flag::Direct =>
direct = true,
Flag::Directory =>
directory = true,
Flag::Dsync =>
dsync = true,
Flag::Sync =>
sync = true,
Flag::NoCache =>
nocache = true,
Flag::NoCache =>
nocache = true,
Flag::NonBlock =>
nonblock = true,
Flag::NoATime =>
noatime = true,
Flag::NoCtty =>
noctty = true,
Flag::NoFollow =>
nofollow = true,
Flag::NoLinks =>
nolinks = true,
Flag::Binary =>
binary = true,
Flag::Text =>
text = true,
Flag::FullBlock =>
fullblock = true,
Flag::CountBytes =>
count_bytes = true,
Flag::SkipBytes =>
skip_bytes = true,
_ => {},
}
}
Ok(IFlags{
cio,
direct,
directory,
dsync,
sync,
nocache,
nonblock,
noatime,
noctty,
nofollow,
nolinks,
binary,
text,
fullblock,
count_bytes,
skip_bytes,
})
}
/// Parse OFlags struct from CL-input
pub fn parse_oflags(matches: &getopts::Matches) -> Result<OFlags, ParseError>
{
let mut append = false;
let mut cio = false;
let mut direct = false;
let mut directory = false;
let mut dsync = false;
let mut sync = false;
let mut nocache = false;
let mut nonblock = false;
let mut noatime = false;
let mut noctty = false;
let mut nofollow = false;
let mut nolinks = false;
let mut binary = false;
let mut text = false;
let mut seek_bytes = false;
let flags = parse_flag_list("oflag", matches)?;
for flag in flags
{
match flag
{
Flag::Append =>
append = true,
Flag::Cio =>
cio = true,
Flag::Direct =>
direct = true,
Flag::Directory =>
directory = true,
Flag::Dsync =>
dsync = true,
Flag::Sync =>
sync = true,
Flag::NoCache =>
nocache = true,
Flag::NoCache =>
nocache = true,
Flag::NonBlock =>
nonblock = true,
Flag::NoATime =>
noatime = true,
Flag::NoCtty =>
noctty = true,
Flag::NoFollow =>
nofollow = true,
Flag::NoLinks =>
nolinks = true,
Flag::Binary =>
binary = true,
Flag::Text =>
text = true,
Flag::SeekBytes =>
seek_bytes = true,
_ => {},
}
}
Ok(OFlags {
append,
cio,
direct,
directory,
dsync,
sync,
nocache,
nonblock,
noatime,
noctty,
nofollow,
nolinks,
binary,
text,
seek_bytes,
})
}
/// Parse the amount of the input file to skip.
pub fn parse_skip_amt(matches: &getopts::Matches) -> Result<Option<usize>, ParseError>
{
if let Some(skip_amt) = matches.opt_str("skip")
{
unimplemented!()
}
else
{
Ok(None)
}
}
/// Parse the amount of the output file to seek.
pub fn parse_seek_amt(matches: &getopts::Matches) -> Result<Option<u64>, ParseError>
{
if let Some(seek_amt) = matches.opt_str("seek")
{
unimplemented!()
}
else
{
Ok(None)
}
}
+17 -17
View File
@@ -3,16 +3,16 @@ use super::*;
use crate::{
build_app,
SYNTAX, SUMMARY, LONG_HELP,
ConvFlagInput, ConvFlagOutput,
IConvFlags, OConvFlags,
StatusLevel,
};
// ----- ConvFlagInput/Output -----
// ----- IConvFlags/Output -----
#[test]
fn build_cfi()
fn build_icf()
{
let cfi_expd = ConvFlagInput {
let icf_expd = IConvFlags {
ctable: Some(&ASCII_TO_IBM),
block: false,
unblock: false,
@@ -28,15 +28,15 @@ fn build_cfi()
let matches = build_app!().parse(args);
let cfi_parsed = parse_conv_flag_input(&matches).unwrap();
let icf_parsed = parse_conv_flag_input(&matches).unwrap();
unimplemented!()
// assert_eq!(cfi_expd, cfi_parsed);
// assert_eq!(icf_expd, icf_parsed);
}
#[test]
#[should_panic]
fn cfi_ctable_error()
fn icf_ctable_error()
{
let args = vec![
String::from("dd"),
@@ -45,12 +45,12 @@ fn cfi_ctable_error()
let matches = build_app!().parse(args);
let cfi_parsed = parse_conv_flag_input(&matches).unwrap();
let icf_parsed = parse_conv_flag_input(&matches).unwrap();
}
#[test]
#[should_panic]
fn cfi_case_error()
fn icf_case_error()
{
let args = vec![
String::from("dd"),
@@ -59,12 +59,12 @@ fn cfi_case_error()
let matches = build_app!().parse(args);
let cfi_parsed = parse_conv_flag_input(&matches).unwrap();
let icf_parsed = parse_conv_flag_input(&matches).unwrap();
}
#[test]
#[should_panic]
fn cfi_block_error()
fn icf_block_error()
{
let args = vec![
String::from("dd"),
@@ -73,12 +73,12 @@ fn cfi_block_error()
let matches = build_app!().parse(args);
let cfi_parsed = parse_conv_flag_input(&matches).unwrap();
let icf_parsed = parse_conv_flag_input(&matches).unwrap();
}
#[test]
#[should_panic]
fn cfi_creat_error()
fn icf_creat_error()
{
let args = vec![
String::from("dd"),
@@ -87,11 +87,11 @@ fn cfi_creat_error()
let matches = build_app!().parse(args);
let cfi_parsed = parse_conv_flag_output(&matches).unwrap();
let icf_parsed = parse_conv_flag_output(&matches).unwrap();
}
#[test]
fn parse_cfi_token_ibm()
fn parse_icf_token_ibm()
{
let exp = vec![
ConvFlag::FmtAtoI,
@@ -113,7 +113,7 @@ fn parse_cfi_token_ibm()
}
#[test]
fn parse_cfi_tokens_elu()
fn parse_icf_tokens_elu()
{
let exp = vec![
ConvFlag::FmtEtoA,
@@ -136,7 +136,7 @@ fn parse_cfi_tokens_elu()
}
#[test]
fn parse_cfi_tokens_remaining()
fn parse_icf_tokens_remaining()
{
let exp = vec![
ConvFlag::FmtAtoE,