1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2026-01-20 04:01:06 +00:00

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

View file

@ -47,8 +47,8 @@ enum SrcStat
EOF,
}
/// Captures all Conv Flags that apply to the input
pub struct ConvFlagInput
/// Stores all Conv Flags that apply to the input
pub struct IConvFlags
{
ctable: Option<&'static ConversionTable>,
block: bool,
@ -58,9 +58,9 @@ pub struct ConvFlagInput
noerror: bool,
}
/// Captures all Conv Flags that apply to the output
/// Stores all Conv Flags that apply to the output
#[derive(Debug, PartialEq)]
pub struct ConvFlagOutput
pub struct OConvFlags
{
sparse: bool,
excl: bool,
@ -70,6 +70,47 @@ pub struct ConvFlagOutput
fsync: bool,
}
/// Stores all Flags that apply to the input
pub struct IFlags
{
cio: bool,
direct: bool,
directory: bool,
dsync: bool,
sync: bool,
nocache: bool,
nonblock: bool,
noatime: bool,
noctty: bool,
nofollow: bool,
nolinks: bool,
binary: bool,
text: bool,
fullblock: bool,
count_bytes: bool,
skip_bytes: bool,
}
/// Stores all Flags that apply to the output
pub struct OFlags
{
append: bool,
cio: bool,
direct: bool,
directory: bool,
dsync: bool,
sync: bool,
nocache: bool,
nonblock: bool,
noatime: bool,
noctty: bool,
nofollow: bool,
nolinks: bool,
binary: bool,
text: bool,
seek_bytes: bool,
}
/// The value of the status cl-option.
/// Controls printing of transfer stats
#[derive(PartialEq)]
@ -101,7 +142,8 @@ struct Input<R: Read>
src: R,
ibs: usize,
xfer_stats: StatusLevel,
cf: ConvFlagInput,
cflags: IConvFlags,
iflags: IFlags,
}
impl<R: Read> Read for Input<R>
@ -110,7 +152,7 @@ impl<R: Read> Read for Input<R>
{
let len = self.src.read(&mut buf)?;
if let Some(ct) = self.cf.ctable
if let Some(ct) = self.cflags.ctable
{
for idx in 0..len
{
@ -118,7 +160,7 @@ impl<R: Read> Read for Input<R>
}
}
if self.cf.swab
if self.cflags.swab
{
let mut tmp = DEFAULT_FILL_BYTE;
@ -140,17 +182,25 @@ impl Input<io::Stdin>
{
let ibs = parseargs::parse_ibs(matches)?;
let xfer_stats = parseargs::parse_status_level(matches)?;
let cf = parseargs::parse_conv_flag_input(matches)?;
let cflags = parseargs::parse_conv_flag_input(matches)?;
let iflags = parseargs::parse_iflags(matches)?;
let skip = parseargs::parse_skip_amt(matches)?;
Ok(
Input {
src: io::stdin(),
ibs,
xfer_stats,
cf,
}
)
let mut i = Input {
src: io::stdin(),
ibs,
xfer_stats,
cflags,
iflags,
};
if let Some(skip_amt) = skip
{
let mut buf = vec![DEFAULT_FILL_BYTE; skip_amt];
i.read(&mut buf)?;
}
Ok(i)
}
}
@ -160,16 +210,29 @@ impl Input<File>
{
let ibs = parseargs::parse_ibs(matches)?;
let xfer_stats = parseargs::parse_status_level(matches)?;
let cf = parseargs::parse_conv_flag_input(matches)?;
let cflags = parseargs::parse_conv_flag_input(matches)?;
let iflags = parseargs::parse_iflags(matches)?;
let skip = parseargs::parse_skip_amt(matches)?;
if let Some(fname) = matches.opt_str("if")
{
Ok(Input {
src: File::open(fname)?,
let mut src = File::open(fname)?;
if let Some(skip_amt) = skip
{
let skip_amt: u64 = skip_amt.try_into()?;
src.seek(io::SeekFrom::Start(skip_amt))?;
}
let i = Input {
src,
ibs,
xfer_stats,
cf,
})
cflags,
iflags,
};
Ok(i)
}
else
{
@ -209,22 +272,23 @@ struct Output<W: Write>
{
dst: W,
obs: usize,
cf: ConvFlagOutput,
cflags: OConvFlags,
oflags: OFlags,
}
impl Output<io::Stdout> {
fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
{
let obs = parseargs::parse_obs(matches)?;
let cf = parseargs::parse_conv_flag_output(matches)?;
let cflags = parseargs::parse_conv_flag_output(matches)?;
let oflags = parseargs::parse_oflags(matches)?;
Ok(
Output {
Ok(Output {
dst: io::stdout(),
obs,
cf,
}
)
cflags,
oflags,
})
}
}
@ -232,20 +296,28 @@ impl Output<File> {
fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
{
let obs = parseargs::parse_obs(matches)?;
let cf = parseargs::parse_conv_flag_output(matches)?;
let cflags = parseargs::parse_conv_flag_output(matches)?;
let seek = parseargs::parse_seek_amt(matches)?;
let oflags = parseargs::parse_oflags(matches)?;
if let Some(fname) = matches.opt_str("of")
{
let dst = OpenOptions::new()
let mut dst = OpenOptions::new()
.write(true)
.create(!cf.nocreat)
.truncate(!cf.notrunc)
.create(!cflags.nocreat)
.truncate(!cflags.notrunc)
.open(fname)?;
if let Some(seek_amt) = seek
{
dst.seek(io::SeekFrom::Start(seek_amt))?;
}
Ok(Output {
dst,
obs,
cf,
cflags,
oflags,
})
}
else
@ -255,32 +327,14 @@ impl Output<File> {
}
}
impl<W: Write> Seek for Output<W>
impl Seek for Output<File>
{
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
{
unimplemented!()
self.dst.seek(pos)
}
}
// impl Seek for Output<io::Stdout>
// {
// fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
// {
// // Default method. Called when output dst not backed by a traditional file and
// // should not be seeked (eg. stdout)
// Ok(0)
// }
// }
//
// impl Seek for Output<File>
// {
// fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
// {
// self.dst.seek(pos)
// }
// }
impl<W: Write> Write for Output<W>
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
@ -322,7 +376,29 @@ fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
}
}
fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, usize), Box<dyn Error>>
#[inline]
fn dd_read_helper<R: Read, W: Write>(mut buf: &mut [u8], i: &mut Input<R>, o: &Output<W>) -> Result<SrcStat, Box<dyn Error>>
{
match i.fill_n(&mut buf, o.obs)
{
Ok(ss) =>
Ok(ss),
Err(e) =>
if !i.cflags.noerror
{
return Err(e);
}
else
{
Ok(SrcStat::Read(0))
},
}
}
/// Perform the copy/convert opertaions. Non file backed output version
// Note: Some of dd's functionality depends on whether the output is actually a file. This breaks the Output<Write> abstraction,
// and should be fixed in the future.
fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(usize, usize), Box<dyn Error>>
{
let prog_tx = if i.xfer_stats == StatusLevel::Progress
{
@ -345,24 +421,81 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
let mut buf = vec![DEFAULT_FILL_BYTE; o.obs];
// Read
let r_len = match i.fill_n(&mut buf, o.obs) {
Ok(SrcStat::Read(len)) =>
let r_len = match dd_read_helper(&mut buf, &mut i, &o)?
{
SrcStat::Read(0) =>
continue,
SrcStat::Read(len) =>
{
bytes_in += len;
len
},
Ok(SrcStat::EOF) =>
SrcStat::EOF =>
break,
Err(e) =>
if !i.cf.noerror {
return Err(e);
} else {
continue
},
};
// Write
let w_len = if o.cf.sparse && is_sparse(&buf)
let w_len = o.write(&buf[..r_len])?;
// Prog
bytes_out += w_len;
if let Some(prog_tx) = &prog_tx
{
prog_tx.send(bytes_out)?;
}
}
if o.cflags.fsync || o.cflags.fdatasync
{
o.flush()?;
}
Ok((bytes_in, bytes_out))
}
/// Perform the copy/convert opertaions. File backed output version
// Note: Some of dd's functionality depends on whether the output is actually a file. This breaks the Output<Write> abstraction,
// and should be fixed in the future.
fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(usize, usize), Box<dyn Error>>
{
let prog_tx = if i.xfer_stats == StatusLevel::Progress
{
let (prog_tx, prog_rx) = mpsc::channel();
thread::spawn(gen_prog_updater(prog_rx));
Some(prog_tx)
}
else
{
None
};
let mut bytes_in = 0;
let mut bytes_out = 0;
loop
{
let mut buf = vec![DEFAULT_FILL_BYTE; o.obs];
// Read
let r_len = match dd_read_helper(&mut buf, &mut i, &o)?
{
SrcStat::Read(0) =>
continue,
SrcStat::Read(len) =>
{
bytes_in += len;
len
},
SrcStat::EOF =>
break,
};
// Write
let w_len = if o.cflags.sparse && is_sparse(&buf)
{
let seek_amt: i64 = r_len.try_into()?;
o.seek(io::SeekFrom::Current(seek_amt))?;
@ -382,11 +515,15 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
}
}
// TODO: Also ensure file metadata is written when fsync option is specified. When _wouldn't_ this happen?
// See fs::File::sync_all && fs::File::sync_data methods!
if o.cf.fsync || o.cf.fdatasync
if o.cflags.fsync
{
o.flush()?;
o.dst.sync_all()?;
}
else if o.cflags.fdatasync
{
o.flush()?;
o.dst.sync_data()?;
}
Ok((bytes_in, bytes_out))
@ -397,36 +534,68 @@ macro_rules! build_app (
() =>
{
app!(SYNTAX, SUMMARY, LONG_HELP)
.optopt(
"",
"skip",
"Skip N ibs-byte blocks in the input file before copying. If iflag=skip_bytes is specified, N is interpreted as a byte count rather than a block count.",
"N"
)
.optopt(
"",
"seek",
"Skip N obs-byte blocks in the input file before copying. If oflag=skip_bytes is specified, N is interpreted as a byte count rather than a block count.",
"N"
)
.optopt(
"",
"count",
"Copy N ibs-byte blocks from the input file, instead of everything until the end of the file. if iflag=count_bytes is specified, N is interpreted as a byte count rather than a block count. Note if the input may return short reads as could be the case when reading
from a pipe for example, iflag=fullblock will ensure that count= corresponds to complete input blocks rather than the traditional POSIX specified behavior of counting input read operations.",
"BYTES"
)
.optopt(
"",
"bs",
"Set both input and output block sizes to BYTES. This makes dd read and write BYTES per block, overriding any ibs and obs settings. In addition, if no data-transforming conv option is specified, input is copied to the output as soon as its read, even
if it is smaller than the block size.",
"BYTES"
)
.optopt(
"",
"if",
"The input file",
"Read from FILE instead of standard input.",
"FILE"
)
.optopt(
"",
"ibs",
"read up to BYTES bytes at a time (default: 512)",
"Set the input block size to BYTES. This makes dd read BYTES per block. The default is 512 bytes.",
"BYTES"
)
.optopt(
"",
"of",
"The output file",
"Write to FILE instead of standard output. Unless conv=notrunc is given, dd truncates FILE to zero bytes (or the size specified with seek=).",
"FILE"
)
.optopt(
"",
"obs",
"write BYTES bytes at a time (default: 512)",
"Set the output block size to BYTES. This makes dd write BYTES per block. The default is 512 bytes.",
"BYTES"
)
.optopt(
"",
"conv",
"One or more conversion options as a comma-serparated list",
"Convert the file as specified by the CONVERSION argument(s). (No spaces around any comma(s).)",
"OPT[,OPT]..."
)
.optopt(
"",
"cbs",
"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"
)
}
);
@ -457,7 +626,7 @@ pub fn uumain(args: impl uucore::Args) -> i32
let o = Output::<File>::new(&matches)
.expect("TODO: Return correct error code");
dd(i,o)
dd_fileout(i,o)
},
(true, false) =>
{
@ -466,7 +635,7 @@ pub fn uumain(args: impl uucore::Args) -> i32
let o = Output::<io::Stdout>::new(&matches)
.expect("TODO: Return correct error code");
dd(i,o)
dd_stdout(i,o)
},
(false, true) =>
{
@ -475,7 +644,7 @@ pub fn uumain(args: impl uucore::Args) -> i32
let o = Output::<File>::new(&matches)
.expect("TODO: Return correct error code");
dd(i,o)
dd_fileout(i,o)
},
(false, false) =>
{
@ -484,7 +653,7 @@ pub fn uumain(args: impl uucore::Args) -> i32
let o = Output::<io::Stdout>::new(&matches)
.expect("TODO: Return correct error code");
dd(i,o)
dd_stdout(i,o)
},
};
@ -492,7 +661,8 @@ pub fn uumain(args: impl uucore::Args) -> i32
{
Ok((b_in, b_out)) =>
{
// TODO: Print output stats, unless noxfer
// TODO: Print final xfer stats
// print_stats(b_in, b_out);
RTN_SUCCESS
},

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,

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)
}
}

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,