From 4d7be2f098a24c9e39ea524798199a8738348fe8 Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 26 Apr 2021 15:17:48 -0700 Subject: [PATCH] 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. --- src/uu/dd/src/dd.rs | 324 ++++++++++++++++++++++++-------- src/uu/dd/src/dd_test.rs | 88 ++++++--- src/uu/dd/src/parseargs.rs | 295 +++++++++++++++++++++++++++-- src/uu/dd/src/parseargs/test.rs | 34 ++-- 4 files changed, 612 insertions(+), 129 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 50c66a639..4efa156ab 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -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 src: R, ibs: usize, xfer_stats: StatusLevel, - cf: ConvFlagInput, + cflags: IConvFlags, + iflags: IFlags, } impl Read for Input @@ -110,7 +152,7 @@ impl Read for Input { 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 Read for Input } } - if self.cf.swab + if self.cflags.swab { let mut tmp = DEFAULT_FILL_BYTE; @@ -140,17 +182,25 @@ impl Input { 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 { 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 { dst: W, obs: usize, - cf: ConvFlagOutput, + cflags: OConvFlags, + oflags: OFlags, } impl Output { fn new(matches: &getopts::Matches) -> Result> { 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 { fn new(matches: &getopts::Matches) -> Result> { 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 { } } -impl Seek for Output +impl Seek for Output { fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - unimplemented!() + self.dst.seek(pos) } } -// impl Seek for Output -// { -// fn seek(&mut self, pos: io::SeekFrom) -> io::Result -// { -// // 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 -// { -// fn seek(&mut self, pos: io::SeekFrom) -> io::Result -// { -// self.dst.seek(pos) -// } -// } - impl Write for Output { fn write(&mut self, buf: &[u8]) -> io::Result @@ -322,7 +376,29 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -fn dd(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> +#[inline] +fn dd_read_helper(mut buf: &mut [u8], i: &mut Input, o: &Output) -> Result> +{ + 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 abstraction, +// and should be fixed in the future. +fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> { let prog_tx = if i.xfer_stats == StatusLevel::Progress { @@ -345,24 +421,81 @@ fn dd(mut i: Input, mut o: Output) -> 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 abstraction, +// and should be fixed in the future. +fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> +{ + 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(mut i: Input, mut o: Output) -> 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 it’s 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::::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::::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::::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::::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 }, diff --git a/src/uu/dd/src/dd_test.rs b/src/uu/dd/src/dd_test.rs index d73a9ede2..9dd1d1333 100644 --- a/src/uu/dd/src/dd_test.rs +++ b/src/uu/dd/src/dd_test.rs @@ -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, diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 29f643e90..4605bf6cf 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -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 + { + 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 { match s @@ -269,11 +350,11 @@ fn parse_ctable(fmt: Option, case: Option) -> Option<&'stati } } -fn parse_conv_opts(matches: &getopts::Matches) -> Result, ParseError> +fn parse_flag_list>(tag: &str, matches: &getopts::Matches) -> Result, 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, ParseErr } /// Parse Conversion Options (Input Variety) -/// Construct and validate a ConvFlagInput -pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result +/// Construct and validate a IConvFlags +pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result { - 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 Result Result +/// Construct and validate a OConvFlags +pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result { - 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 Result Result +{ + 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 +{ + 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, 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, ParseError> +{ + if let Some(seek_amt) = matches.opt_str("seek") + { + unimplemented!() + } + else + { + Ok(None) + } +} diff --git a/src/uu/dd/src/parseargs/test.rs b/src/uu/dd/src/parseargs/test.rs index 491d3f335..390622a12 100644 --- a/src/uu/dd/src/parseargs/test.rs +++ b/src/uu/dd/src/parseargs/test.rs @@ -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,