From af6bf3a22454084c6813cdb3f2f7f1b39819eac0 Mon Sep 17 00:00:00 2001 From: ty Date: Tue, 16 Mar 2021 14:32:55 -0700 Subject: [PATCH 01/66] Begins work on dd - Prototype code. - Adds project boilerplate for integration with coreutils framework. --- Cargo.lock | 11 ++++ Cargo.toml | 2 + src/uu/dd/Cargo.toml | 23 +++++++++ src/uu/dd/Makefile | 15 ++++++ src/uu/dd/src/dd.rs | 109 +++++++++++++++++++++++++++++++++++++++ src/uu/dd/src/main.rs | 1 + tests/by-util/test_dd.rs | 7 +++ 7 files changed, 168 insertions(+) create mode 100644 src/uu/dd/Cargo.toml create mode 100644 src/uu/dd/Makefile create mode 100644 src/uu/dd/src/dd.rs create mode 100644 src/uu/dd/src/main.rs create mode 100644 tests/by-util/test_dd.rs diff --git a/Cargo.lock b/Cargo.lock index ebb288d3d..fe0440d73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "advapi32-sys" version = "0.2.0" @@ -219,6 +221,7 @@ dependencies = [ "uu_csplit 0.0.4", "uu_cut 0.0.4", "uu_date 0.0.4", + "uu_dd 0.0.4", "uu_df 0.0.4", "uu_dircolors 0.0.4", "uu_dirname 0.0.4", @@ -1495,6 +1498,14 @@ dependencies = [ "uucore_procs 0.0.5", ] +[[package]] +name = "uu_dd" +version = "0.0.4" +dependencies = [ + "uucore 0.0.7", + "uucore_procs 0.0.5", +] + [[package]] name = "uu_df" version = "0.0.4" diff --git a/Cargo.toml b/Cargo.toml index 9b55abe5c..786c66fe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -150,6 +150,7 @@ feat_require_unix = [ "chown", "chroot", "du", + "dd", "groups", "hostid", "id", @@ -249,6 +250,7 @@ df = { optional=true, version="0.0.4", package="uu_df", path="src/uu/df" } dircolors= { optional=true, version="0.0.4", package="uu_dircolors", path="src/uu/dircolors" } dirname = { optional=true, version="0.0.4", package="uu_dirname", path="src/uu/dirname" } du = { optional=true, version="0.0.4", package="uu_du", path="src/uu/du" } +dd = { optional=true, version="0.0.4", package="uu_dd", path="src/uu/dd" } echo = { optional=true, version="0.0.4", package="uu_echo", path="src/uu/echo" } env = { optional=true, version="0.0.4", package="uu_env", path="src/uu/env" } expand = { optional=true, version="0.0.4", package="uu_expand", path="src/uu/expand" } diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml new file mode 100644 index 000000000..0a7a945bf --- /dev/null +++ b/src/uu/dd/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "uu_dd" +version = "0.0.4" +authors = ["uutils developers"] +license = "MIT" +description = "dd ~ (uutils) copy and convert files" + +homepage = "https://github.com/uutils/coreutils" +repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dd" +keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] +categories = ["command-line-utilities"] +edition = "2018" + +[lib] +path = "src/dd.rs" + +[dependencies] +uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } + +[[bin]] +name = "dd" +path = "src/main.rs" diff --git a/src/uu/dd/Makefile b/src/uu/dd/Makefile new file mode 100644 index 000000000..bd6462c41 --- /dev/null +++ b/src/uu/dd/Makefile @@ -0,0 +1,15 @@ +## +# Test runner +# +# @file +# @version 0.1 + +all: int unit + +int: + cargo test --features "dd" --no-default-features --manifest-path "../../../Cargo.toml" + +unit: + cargo test --manifest-path "./Cargo.toml" + +# end diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs new file mode 100644 index 000000000..d2a0a0fa4 --- /dev/null +++ b/src/uu/dd/src/dd.rs @@ -0,0 +1,109 @@ +// This file is part of the uutils coreutils package. +// +// (c) Tyler Steele +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// spell-checker:ignore (T0DO) + +#[macro_use] +extern crate uucore; + +use std::error::Error; +use std::io; +use std::sync::mpsc; +use std::thread; + +const NAME: &str = "dd"; +const SUMMARY: &str = "Copies, and optionally converts, file system resources."; +const LONG_HELP: &str = " +TODO: This is where the long help string for dd goes! +"; + +struct Input +{ + src: Box, +} + +impl Input +{ + fn run(&self, tx: mpsc::SyncSender>) -> Result<(), Box> + { + let buff = vec![0; 128]; + let msg = self.src.read(&buff)?; + + println!("Sender sending:\t\t{:?}", &msg); + tx.send(msg)?; + + Ok(()) + } +} + +struct Output +{ + dst: Box, +} + +impl Output +{ + fn run(&self, rx: mpsc::Receiver>) -> Result<(), Box> + { + let data = rx.recv()?; + + println!("Receiver received:\t{:?}", data); + + Ok(()) + } +} + +fn dd(i: Input, o: Output) -> () +{ + let (tx, rx) = mpsc::sync_channel(0); // each send will block until the recv'r is ready for it + + thread::spawn(move || { + i.run(tx); + }); + + thread::spawn(move || { + o.run(rx); + }); + + loop{}; +} + +pub fn uumain(args: impl uucore::Args) -> i32 +{ + // TODO: parse args + + -1 +} + +#[cfg(test)] +mod test_dd_internal { + #[allow(unused_imports)] + use super::*; + + #[test] + fn hello_world_test() + { + let (src, dst) = mpsc::channel(); + let data = vec![0xFF; 128]; + + for c in data { + src.send(c); + } + + let i = Input { + src: Box::new(src), + }; + + let o = Output { + dst: Box::new(dst), + }; + + dd(i,o); + + assert_eq!(data, dst); + } +} diff --git a/src/uu/dd/src/main.rs b/src/uu/dd/src/main.rs new file mode 100644 index 000000000..fb6f5a6f5 --- /dev/null +++ b/src/uu/dd/src/main.rs @@ -0,0 +1 @@ +uucore_procs::main!(uu_dd); // spell-checker:ignore procs uucore diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs new file mode 100644 index 000000000..2a010b4e3 --- /dev/null +++ b/tests/by-util/test_dd.rs @@ -0,0 +1,7 @@ +use crate::common::util::*; + +#[test] +fn fail_from_test_dd() +{ + panic!() +} From 28e6d0d3854e2f79d4fdc204153d39e815bdd423 Mon Sep 17 00:00:00 2001 From: ty Date: Tue, 16 Mar 2021 19:39:22 -0700 Subject: [PATCH 02/66] Develop prototype code - Move code closer to real impl - Remove Makefile from repo --- src/uu/dd/Makefile | 15 ---- src/uu/dd/src/dd.rs | 162 +++++++++++++++++++++++++++++++++----------- 2 files changed, 122 insertions(+), 55 deletions(-) delete mode 100644 src/uu/dd/Makefile diff --git a/src/uu/dd/Makefile b/src/uu/dd/Makefile deleted file mode 100644 index bd6462c41..000000000 --- a/src/uu/dd/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -## -# Test runner -# -# @file -# @version 0.1 - -all: int unit - -int: - cargo test --features "dd" --no-default-features --manifest-path "../../../Cargo.toml" - -unit: - cargo test --manifest-path "./Cargo.toml" - -# end diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index d2a0a0fa4..ff5d465d1 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -11,7 +11,11 @@ extern crate uucore; use std::error::Error; -use std::io; +use std::fs::File; +use std::io::{ + self, Read, Write, + BufWriter, +}; use std::sync::mpsc; use std::thread; @@ -21,89 +25,167 @@ const LONG_HELP: &str = " TODO: This is where the long help string for dd goes! "; -struct Input +const RTN_SUCCESS: i32 = 0; +const RTN_FAILURE: i32 = 1; + +// Conversion tables are just lookup tables. +// eg. +// The ASCII->EBDIC table stores the EBDIC code at the index +// obtained by treating the ASCII representation as a number. +// This idea is from the original GNU implementation. +type ConversionTable = [u8; u8::MAX as usize]; + +struct Input { - src: Box, + src: R, + read_size: usize, } -impl Input +impl Read for Input { - fn run(&self, tx: mpsc::SyncSender>) -> Result<(), Box> + fn read(&mut self, buf: &mut [u8]) -> io::Result { - let buff = vec![0; 128]; - let msg = self.src.read(&buff)?; - - println!("Sender sending:\t\t{:?}", &msg); - tx.send(msg)?; - - Ok(()) + self.src.read(buf) } } -struct Output +struct Output { - dst: Box, + dst: W, + write_size: usize, + conv_table: Option, } -impl Output +impl Write for Output { - fn run(&self, rx: mpsc::Receiver>) -> Result<(), Box> + fn write(&mut self, buf: &[u8]) -> io::Result { - let data = rx.recv()?; + if let Some(ct) = self.conv_table + { + let mut cbuf = vec![0; buf.len()]; + + for (idx, byte) in buf.iter().enumerate() + { + cbuf[idx] = ct[*byte as usize] + } - println!("Receiver received:\t{:?}", data); + self.dst.write(&cbuf) + } + else + { + self.dst.write(buf) + } + } - Ok(()) + fn flush(&mut self) -> io::Result<()> + { + self.dst.flush() } } -fn dd(i: Input, o: Output) -> () + + +fn dd(mut i: Input, mut o: Output) -> Result<(), Box> { - let (tx, rx) = mpsc::sync_channel(0); // each send will block until the recv'r is ready for it + let (prog_tx, prog_rx) = mpsc::channel(); thread::spawn(move || { - i.run(tx); + // TODO: Replace ?? with accurate info + print!("Progress ({}/??)", 0); + + loop { + let prog = prog_rx.recv() + .expect("TODO: Handle this error in the project-specific way"); + print!("\rProgress ({}/??)", prog); + } }); - thread::spawn(move || { - o.run(rx); - }); + let mut buf = vec![0; i.read_size]; - loop{}; + loop + { + let r_len = i.read(&mut buf)?; + if r_len == 0 { break; } + + let w_len = o.write(&buf[..r_len])?; + + // if *full write buffer* { o.flush(); } + + prog_tx.send(w_len)?; + + buf.clear(); + } + + Ok(()) } pub fn uumain(args: impl uucore::Args) -> i32 { // TODO: parse args - -1 + let if_name = "foo.txt"; + let of_name = "bar.txt"; + let read_size = 512; + let write_size = 4096; + + let in_f = File::open(if_name) + .expect("TODO: Handle this error in the project-specific way"); + + let out_f = File::open(of_name) + .expect("TODO: Handle this error in the project-specific way"); + let out_f = BufWriter::with_capacity(write_size, out_f); + + let i = Input { + src: in_f, + read_size, + }; + let o = Output { + dst: out_f, + write_size, + conv_table: None, + }; + + match dd(i, o) { + Ok(_) => + RTN_SUCCESS, + Err(_) => + RTN_FAILURE, + } } #[cfg(test)] -mod test_dd_internal { +mod test_dd_internal +{ #[allow(unused_imports)] use super::*; - #[test] - fn hello_world_test() - { - let (src, dst) = mpsc::channel(); - let data = vec![0xFF; 128]; + use std::io::prelude::*; - for c in data { - src.send(c); - } + #[test] + fn empty_reader_test() + { + let src = io::empty(); + + let dst = vec![0xFF as u8, 128]; + let dst_ptr = dst.as_ptr(); + let exp = vec![0xFF as u8, 128]; let i = Input { - src: Box::new(src), + src, + read_size: 1, }; let o = Output { - dst: Box::new(dst), + dst, + write_size: 1, + conv_table: None, }; - dd(i,o); + dd(i,o).unwrap(); - assert_eq!(data, dst); + for (i, byte) in exp.iter().enumerate() + { + panic!(); + } } } From d6086fc6495ea6b23bb660c06863f502cdb7d377 Mon Sep 17 00:00:00 2001 From: ty Date: Thu, 18 Mar 2021 14:38:08 -0700 Subject: [PATCH 03/66] Adds impl & tests - implements basic copy & progress functionality - rough work for conversion tables - adds sanity tests (mt, zeros, ones, deadbeef & rand) --- src/uu/dd/Cargo.toml | 4 + src/uu/dd/src/dd.rs | 220 +++++++++++++----- ...9661a1de1fc9af21b0ec2cd67ba3-deadbeef.test | 1 + ...28891cb1230748e146f34223bbd3b5-random.test | Bin 0 -> 74400 bytes ...20f0b67a91f7f74151bc5be745b7110-zeros.test | Bin 0 -> 4096 bytes ...6ae59e64850377ee5470c854761551ea-ones.test | 1 + 6 files changed, 171 insertions(+), 55 deletions(-) create mode 100644 src/uu/dd/test-resources/18d99661a1de1fc9af21b0ec2cd67ba3-deadbeef.test create mode 100644 src/uu/dd/test-resources/5828891cb1230748e146f34223bbd3b5-random.test create mode 100644 src/uu/dd/test-resources/620f0b67a91f7f74151bc5be745b7110-zeros.test create mode 100644 src/uu/dd/test-resources/6ae59e64850377ee5470c854761551ea-ones.test diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 0a7a945bf..dac27d833 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -18,6 +18,10 @@ path = "src/dd.rs" uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } +[dev-dependencies] +md-5 = "0.9" +hex-literal = "0.3" + [[bin]] name = "dd" path = "src/main.rs" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index ff5d465d1..ed9e0508f 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -35,10 +35,16 @@ const RTN_FAILURE: i32 = 1; // This idea is from the original GNU implementation. type ConversionTable = [u8; u8::MAX as usize]; +enum SrcStat +{ + Read(usize), + EOF, +} + struct Input { src: R, - read_size: usize, + ibs: usize, } impl Read for Input @@ -49,10 +55,36 @@ impl Read for Input } } +impl Input +{ + fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result> + { + let ibs = self.ibs; + let mut bytes_read = 0; + + for n in 0..(obs/ibs) { + // fill an ibs-len slice from src + let this_read = self.read(&mut buf[n*ibs..(n+1)*ibs])?; + + if this_read != 0 { + bytes_read += this_read; + } else { + break; + } + } + + if bytes_read != 0 { + Ok(SrcStat::Read(bytes_read)) + } else { + Ok(SrcStat::EOF) + } + } +} + struct Output { dst: W, - write_size: usize, + obs: usize, conv_table: Option, } @@ -83,40 +115,60 @@ impl Write for Output } } +fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () +{ + move || { // LAAAAMBDA! + // TODO: Replace ?? with accurate info + print!("\rProgress ({}/??)", 0); -fn dd(mut i: Input, mut o: Output) -> Result<(), Box> + loop + { + match rx.recv() + { + Ok(wr_total) => { + print!("\rProgress ({}/??)", wr_total); + }, + Err(_) => { + println!(""); + break + }, + } + } + } +} + +fn dd(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> { let (prog_tx, prog_rx) = mpsc::channel(); + thread::spawn(gen_prog_updater(prog_rx)); - thread::spawn(move || { - // TODO: Replace ?? with accurate info - print!("Progress ({}/??)", 0); - - loop { - let prog = prog_rx.recv() - .expect("TODO: Handle this error in the project-specific way"); - print!("\rProgress ({}/??)", prog); - } - }); - - let mut buf = vec![0; i.read_size]; + let mut bytes_in = 0; + let mut bytes_out = 0; loop { - let r_len = i.read(&mut buf)?; - if r_len == 0 { break; } + let mut buf = vec![0xDD; o.obs]; + let r_len = + match i.fill_n(&mut buf, o.obs)? { + SrcStat::Read(len) => + { + bytes_in += len; + len + }, + SrcStat::EOF => + break, + }; let w_len = o.write(&buf[..r_len])?; + o.flush()?; - // if *full write buffer* { o.flush(); } + bytes_out += w_len; - prog_tx.send(w_len)?; - - buf.clear(); + prog_tx.send(bytes_out)?; } - Ok(()) + Ok((bytes_in, bytes_out)) } pub fn uumain(args: impl uucore::Args) -> i32 @@ -125,29 +177,33 @@ pub fn uumain(args: impl uucore::Args) -> i32 let if_name = "foo.txt"; let of_name = "bar.txt"; - let read_size = 512; - let write_size = 4096; + let ibs = 512; + let obs = 4096; let in_f = File::open(if_name) .expect("TODO: Handle this error in the project-specific way"); let out_f = File::open(of_name) .expect("TODO: Handle this error in the project-specific way"); - let out_f = BufWriter::with_capacity(write_size, out_f); + let out_f = BufWriter::with_capacity(obs, out_f); let i = Input { src: in_f, - read_size, + ibs, }; let o = Output { dst: out_f, - write_size, + obs, conv_table: None, }; match dd(i, o) { - Ok(_) => - RTN_SUCCESS, + Ok((b_in, b_out)) => + { + println!("Completed: Bytes in: {}, Bytes out: {}", b_in, b_out); + + RTN_SUCCESS + }, Err(_) => RTN_FAILURE, } @@ -160,32 +216,86 @@ mod test_dd_internal use super::*; use std::io::prelude::*; + use std::io::BufReader; + use std::fs; + use md5::{ Md5, Digest, }; + use hex_literal::hex; - #[test] - fn empty_reader_test() - { - let src = io::empty(); - - let dst = vec![0xFF as u8, 128]; - let dst_ptr = dst.as_ptr(); - let exp = vec![0xFF as u8, 128]; - - let i = Input { - src, - read_size: 1, - }; - - let o = Output { - dst, - write_size: 1, - conv_table: None, - }; - - dd(i,o).unwrap(); - - for (i, byte) in exp.iter().enumerate() + macro_rules! make_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $exp:expr ) => { - panic!(); - } - } + #[test] + fn $test_id() + { + // let test_name = "6ae59e64850377ee5470c854761551ea-ones"; + let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); + + let i = Input { + src: $src, + ibs: 256, + }; + + let o = Output { + dst: File::create(&tmp_fname).unwrap(), + obs: 1024, + conv_table: None, + }; + + dd(i,o).unwrap(); + + let res = { + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let mut h = Md5::new(); + for b in res.bytes() + { + h.update([b.unwrap()]); + } + + h.finalize() + }; + + assert_eq!(hex!($exp), res[..]); + + fs::remove_file(&tmp_fname).unwrap(); + } + }; + ); + + make_test!( + empty_file_test, + "stdio-empty-file", + io::empty(), + "d41d8cd98f00b204e9800998ecf8427e" + ); + + make_test!( + zeros_4k_test, + "zeros-4k", + File::open("./test-resources/620f0b67a91f7f74151bc5be745b7110-zeros.test").unwrap(), + "620f0b67a91f7f74151bc5be745b7110" + ); + + make_test!( + ones_4k_test, + "ones-4k", + File::open("./test-resources/6ae59e64850377ee5470c854761551ea-ones.test").unwrap(), + "6ae59e64850377ee5470c854761551ea" + ); + + make_test!( + deadbeef_32k_test, + "deadbeef_32k", + File::open("./test-resources/18d99661a1de1fc9af21b0ec2cd67ba3-deadbeef.test").unwrap(), + "18d99661a1de1fc9af21b0ec2cd67ba3" + ); + + make_test!( + random_73k_test, + "random_73k", + File::open("./test-resources/5828891cb1230748e146f34223bbd3b5-random.test").unwrap(), + "5828891cb1230748e146f34223bbd3b5" + ); + } diff --git a/src/uu/dd/test-resources/18d99661a1de1fc9af21b0ec2cd67ba3-deadbeef.test b/src/uu/dd/test-resources/18d99661a1de1fc9af21b0ec2cd67ba3-deadbeef.test new file mode 100644 index 000000000..d2a5d0683 --- /dev/null +++ b/src/uu/dd/test-resources/18d99661a1de1fc9af21b0ec2cd67ba3-deadbeef.test @@ -0,0 +1 @@ +­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾ \ No newline at end of file diff --git a/src/uu/dd/test-resources/5828891cb1230748e146f34223bbd3b5-random.test b/src/uu/dd/test-resources/5828891cb1230748e146f34223bbd3b5-random.test new file mode 100644 index 0000000000000000000000000000000000000000..6e997411c30e758e1d8654b02d365940cda4426d GIT binary patch literal 74400 zcmeCV9I;Cyab4@1EmJk`CTs6mJyUJoy~YXqOwA{muNF0usEsSkY1J3!c~Q7Lru%>S zKaaqsC9HFJ*9!BWaozVVx8uA=LSdoFl)EAZ^L}4uQ@!J3{_^@ItC;g=*PQ&+cW2|g znv8ij_Z^Wb@MYd@X}bU5w9Ht8rW*GbZ(Uz+m>05q#?Cc=R?jJ&uffBTT*TGl$Bf82;kvh)&c z*pn02zAKu2N#}LJxz5jZ;^ta474ym8*>Tl7&}zGLXS#gfU;alKn>-xNTNV|3_!8hd z+4_m^o8MW#l8TGEwr;m@el2U*r~cyUF2@5ex;}0_6#nA^`=NN&cNyjS!f%Ur^QlS* z+_>^^iQD3zw#=(brmnYZz4GmyVz=(LlXFckK7DCd+Tp%~;@i8o>U)?eIT!5{zq@;jxA+CEtvMT~`*j-W#JO>>v0uK& z@+s-bGkK#g*M6S2eA9I&BlG6Jjqz)CJveamji33nNjHsF@TYh@duZm_o={o)v~`)2 zZ1$1#mv-^CIlq^E`taNK=`)A-6<@qH9-LiOT~QvtW6ccdt=SykA6dQGY<)j_eUj63 z&3C1@?_X4l_BWgJM||Uq=eot=N8LN;`?zRqK!RluYG;p0_j&% z*V=sIn#Ws4VpF6io*DX)ar5`O@H2cd0|zRlx~7=OB$=4M8TbmhP{(c_E;{Qc%RSyN2Sdr zhF?wA?f*&=yez{nLX)zsEXmv(5+m`FI4M`qb_h~=eyq8HSNY*wmfHx zx_Pb%KE?T6@$LB%vijfV7FKL#-gQP@>U+(b`e`q-J$28t8r&;YjSx@1x7lC&v~_ud z*`xElxwF_;U7a9#a`xxADKR%=-(OvH_#o5T+8ukoXPB%$7!VgFRbREj-Kw(fIpd7X zZ#LO8g)O#U@0!Qg*|kr$BHU5-_T))jQ?<{fs*Blazsvk1acgT!<4XSr9u-TQklTw>3_91 z{8$x7@65c6(*l#ZQpMGtc5lfqf3)aF;=&oWUD>>w9p=i0^-0S1Zi=t*^jB}$nm1SC zAb-24W{=vkIlu1D*k6JY^Jx=?b`z3-JY?hV>9xRP% zxUu^4)(rbS(+cOo9o0;#%DCtkvAMK^VRJ?1JGs+70sB;@H|)?BicnzSWjp!Krq24Zci@fx zQ6hJ`J#+u>G2@sLCa+qQmMV9NjWuUim}fvmk)qF!_}$8{Q=ew0->+SlahhZ5r{+jI zKR;c5^SS;r*l)fM{=vuB`=%oK?>CPrO7#h>zl6@}->uF3ZKv0NAvETs+;hPr?c4_z zaJZi?_Mh{%r?9_gZvSyVfjdo%H)p3>7>BGq@GHXptKBPxaN#q%EG`G1(`e+}*4MI; zPtEkx+PybsO}nZr?;0;X6M|4{ms>%tz=Fy8wd-TJlG?$bMkc6*Z(9$ znHY?|cJ1e>6;KZG-hO}6)IXipxv6jdeJyBs_{mkVXF@QWw&%2#hF#Z6Csau!-e2yK zSwA(+mO1CQltk5;ul#j=QMYo8X7V)4ZxJ+paY2ibl{bImMP(W71od-w!~gw@V~Lea znYUFzO+>&b=eQEHeZ8*Fx0l6@H}eFZ?{|DCqTM60`FML!UvXB&tHitWPai(ade${V zG+^bd^Bi(@&cBod9jDtEO#5Yg$hd*$jaf_a`r83@2OD;Ve{}t_d;Lvyd1;?HeJ9pS z*|{Hlz+-+i%(`}C+tLR0blJZ~Ez1{Yl*L?`uzdr2;cFenpyk1{G`>Vd$IQ&H`*1`1 z_Rf7L+ln|Pzuq-clfRyEeEM<8X96pZ)O=X)`XHjtr~ChLh8rcd-sLwBf4U%Sd+(YV zhnB`_n=>+fOP)>izvVhtLt{;h$m=fKkkV_f8kz!6WO~n>nzW?vV2~zH5~JsSBjLAJ zQ}(Ye|GS3ct+7e5$m6%Owy$Ork+j`h?DX|}6T|g~Rf!JE=HJf?$@<(MXQA313D$g!WMm>eK@C7E@r-)L}=2!IbCTAs>asO zc`V#>lf%8gaB&(xd%(5r+4DIw_b*%<+U-^tdY)@a$GJD@o9bC1^4qkf;Y5(*6q1|vF%OE|EW9v9M=A^ zY4hb7@2^}v%P>b~QB7M&`KtOj=SjUC?2rHb)5uGB&-7xK@IppYr&os!R%~ZjKkccf z-kCLDd%lN1O&4ApFrWWHfUA%4>tl7D3pK(|%apVIyzu)e^Wt@rPUfyCnto#Kg&3d4 zjHm1OJnRUJy7j6zS=%yq%UMsEpGHb&Hf3|>`Q>Y-UYz`ZDVX2l>EDI_lUMfay~8Ef z*DvFD%Iy@dn)+{^%mSq^?1JBZCh)Lb^tDn^HTRujBFQ|j_;PA`cr5G2f0dP^ZVLUIG41Si2d+BKOSxwXd%ot!7e&?2tiD+LQvXO-M@)d) z(hYB>O3r?w;g+Q}|Bi&l!Ncv(FB_MnF5XZTw~S3`ip}hg9u`WW34JRMG8Tp(iE5AE zYUiNBx?q2|(9=2Rw5Jz6U+uAVwz}5g!v*}SV;ze8-1HwyCTQNQT*k43so>?N&i7s2 zj$GHR94hv%_%N{Gw>73diY{wVX=QzVFU1&Dy9G-R84r z>Ot1*uDFoyEmNcC=J5wQ8p|^+eSIU`;Pkd>zm)Wv(mU@jnXRNW z*;89(xTN-^;F;J<>ufK7?&An>UG0&4D15rspMa)wPyO6^KeRC?w4T~ERVq=D<=F@S3R~M`^Wb?VQXJm%`3ZZ8!uCy8~E&m z#0KY2(frLvME&N(w_Ck$IkiKz$hl%_gV&Qi%1b_6U;Rmc>b#quKkb=6E3!S0@olh8 zQ}dmjn&J~Knf(8-)A6O)-3Je!JuWJJ{7mQe$40Yf^XpFA-A#_I*wVF3>=^sYW%2HJ zcg>Bh<9)Jt*VLWf!4KwyUkZudcv!z?7SsOK9CoJNU;f(Jep#q2J3~oEFn`L_?-#bE zTbL+r&X@mLs&rw^PhRWnufG{DUVqSK*Kj=OblC*SdG3w7G>c_=7b;7xa zd|q1LSz<#qgNT)(^Pi(pV$)fj?mRvj;uXic!bE-Y`?Npv!x&E_*)cZoo?%LgSZl(d zw8;EPpVn`+doS0|(E4`i`@|itr{ij8Ei-gHRl4$zN^s4ZcPpb(9Ut)=-_y23BI;H9 zqQ9J5-v%F-D{}UXxZ^JPa!t)ygCMat(ZAU(TqZKO*_>LvHL2Xmt-m=Vw_&&YOQA)H zn<^F>{@HbGe$f)16U*zjKU_YUJ=?tHV?>Ypp{nJ_^Vr_|CG9_WaG^(I@SZi>4O=D! zu@!FH{pp0;OY7|p94B|YnA8$+sj2hzmb%h))$$HAZf(1^V^a20(@4WF8+YWMXvz!8f z8>Q+w!MF4K%4`j8{JlN9EupXB)Q8`m!3xJ`?&v<1)S7!TC@ks0r-|i^i$6@|?=yP% zT{dExv|`%T&Nz4VfSdbD|M9B#?~gvmG2d{Z-PSW@$D4HDTrB&y{)_TCpT@vR+MlDZ z&)nI)_vhMu5%&W4GEb>&e_P~UnyZny(24Xa(SN|;=UGZ zwP5d;*m?3-C-1y{LZvt6=nVHrrG2~$9^4DBSL!r)bMWP-DVrpBKDy>Vxo%yqTo3yc zsY8sfSIml(=Tn%o`q%qcp^TY1JdcB!X1FzO+RQAa+2ZWBv93b<=kl+g|5=dy`nG^)uvw)l9L~;V)i` zDlg3D{>C5nS5Yf>XCOy@3jg(_MZ1`7=e-YneRH3jq)^W8TDy%!wO`(E3%+VKYr;u& z)5gcuo7yXvGQE@Q-uO-brfuk_--oW9kJxxopE2?GzRrd5)#}NX8>Mr#|G)byoLlAo zZjqmGROITUKaC43N`AapAz6F$b!(GC`Vu>l^y0sXhF*VWeF;mtbJI^&e-RHO*N#$+ zH&?HlKCtG)TVXHF|7AT#BHtyX7iVQHYQEtwBcUs{`qgSaMUBPD`@0^`Z;{%Q zDZTIY9q;AV3~vj{q}#j%*xuT-JygG{y;CXw!K%6og)sqd^QA6p9Dkj7BC2Zcf}OhD z&20e|-&ak4|7*^--m`Oh-xq1T?$fJhSd|t3UJy5zt8Pw;ipkBmgwQ^fE1t(Jy{v0gHhFie z9nbmNe_+?tGp=vTU3}tf?X>HU zWx~%rW&bET_x}6nsAezM+AHA;7aCRXi<60%{p+GafPC4)NIzljYXS3rPd^l-R;d>K z_EDeS{;Y*H?|sC$_I`HFdv)rXpJ$LS*WMr{qVkD$sq-|pWLwh{D|f1!a9GR^H-mUhE9F` znB$KNYv;qU;B!j*`d(Ez zv!^*5uU=7$J+mt9-{A+xngj}V&Y2+_@#6iTjhi=H)@42y=E$0Jt4^?2Sn{LOFGsIN z?LX6XZYrI0>DEzN_duEFT+`J49V=O64oX{Wk=?l1amz--x0`1Ky>lq2(~b?T-Lk^fhi4+gLtRMyYvsR&RP`+-82IF>>XHT=m?#4c>YO zbmLA)vxPp?T6NpxL7ar^x#e?ucy>?FUVi3+YI@7o_-mQ-(yyFeyRn*c?$u?*Z5rYw zmyYnfTBLnTygFTNw| z`X9X(QF^YjyeTM8@WQ02R`o(pIKJAsU(!7P_TUo^kxy;cf>wyL>0G@P(fpu3cJ+)m z&YM!NzuUj3?&^6yryhUR!dAr-ol{EcV>=`i^FB^pXc^7eSrnQ3X6cMC1y4o3vP-^y zZeih9`srk?foJ&2Z_`F{QJ5zZw3b#s^2 za?Ad^u;=2GWj|PV#hV}dRVz4kC$CQK-lFe;(T%lRsCA#O+L+`VwV_jUAS<~6{U0A_Hr5N=YBgdWs5@35^=Ky ztTRQf)U5p7yZ66Xvb@Xs8^4CmTX9<1ZYBR}-6cZiEykO_>jzR2FJ)PA+%Rk%TJDbKmW z#J3k$&RUSTW&L%AIlF(^x=olfZR&dqrhweTEFQ8ukGvB+`d{uQQ`5!A!kvXO8z%f- z%VhJ_bh2RISqZ&$7ax273SPGKyOHv&zUfn$;#m*()-PMe&lK_RQ()}go^?yjX3NFj zX*zsP?CR^V#ghui1&obPEec@qpDntzm6l>KtT`bJ9{ zYn}YU-v=`iQ`L)^C;npbQc<%#Z@)*BXRp}8)sEVhk21v9r>j<#o$TGu^P+R=iuz5< z)t66M@7X&!The~ogHsMAzw6r8Ddc>wKXR{q`Ya8#O5Z&uJgh4phR^zMC{Q!`>Rt0E z!S`!6K1d9>t?Vcr`)X6w{Yw{5edsAEPDuWw6>_9R%4~knq1p+uoU^_!|NY?OFSX~> z430)NN z*z18?gW(O2RYC8pp2+bPn%*t&Pq|i*CbjrRrH}Zc_9rP1c5g6r=gU02oXJ9abH6gf zjB@@aW`+drwjR%ia!U*9LK0W-I67~7yqD>qVc-2#yW_Sqo_*Y;t#H~jW8Fn%nca&8 zni?L+hbrtd;EtO$uV9f~d2HWQ-4|*{UQLdFZ~M!SUoGY5g8Z;t;d`gt%ZeTLtq?5I zxXjVTzrotgcI`6TAWa*;#w#Z279SZSKIyzNKiM|T12vMnnX7B)`V*7e@4*J)wtUJlnDmrojb7cQGkidw&i>->qx-nfF|& zSa|*drY&M#GmQ;3eu+NN{1=ngB2V14g7g|#zfH>e$$?4j7{ zy!eokkoI9q(+>+iRhWHUvhwNv{c@2e`P(~onlh=owu$S%lb+CfZC{$Qf%MzY9%mvi zC(6xj*LOR^Sa4qG!s4o^!t0WAx7^^L_mlb64(T~UGiP{iGG^d6`XwvvvPjKyMbKJ# zL&nc+3${GoDfRF(V@2T2Q%0|&ejNR==IqQwk9*71te1Aj+*s*g_|@Lv_2hEVc$S%g zdrl>(Ej%9@6+7?I5ewZb(OVYhZx-^4cp+VOjO~CU+Y-^Atk!qmTv9z2x2D|mU)6zU zU7fFI_3xPJBwVmG#N<+K*oJ0ayLbP8SMRZqW3K72k+{BV&GS2fSs@FjFJ{r;>v4JC zvvunI0E?}smdyXJ`OL{;-}FaGzCJIClag->6sH9k+-~f-I_+p%3lpbZ^Pk3pD)q|C zKhIn}Tk`dK#`)7c*PT2&x$oG*c}5$zH&rt6sHx98ol~IBwLyB}T6diYpM0nFVe7u_ zT==4(ey@vQZYk%n{uBFlxkN1Zz0=Ox_H$=s>-W+-^U?zvEgf#^?)ADYoc`x1x6!0c z?v|H#+&NvFsBx^F;haNUWY66+kC~U0CghbeEetSRy5s-aw7YiiV-D`QU%6<`+>2Jh zDFV-bIp6uKdpc(Mf6ks$$(t*jbk{T)&%Z2sUOVe2v(CO1#rb=54`g3Dey{4e&664q z%YU;pR|vX^Z_n_y&U*R$%p*0I_RhqY9HmvimTp+uAo&wXeuQ#+8iv99QOFDid z{isv$Dl?bex$nm77e!^BQ}VNnuUa`3@-A`@qNRev{rZi>Mym8FXsp!{Nu2D^>nwI zoc!sFOay$c-rH@shLQK*yNz#eZ*iYFZGw3G2I(G$JI$-s&e`?0>5RfwHt##N)5DL+ zo1QRvBVN2F(S65pPq``6pQ|1It7^#YUC-F-9>esT;nSmYm!ApElHK2Gd5Z7-+bfRm zV?WuZ7+p85pD}yc@>6#2c9y@d-?#1B4Rbr;#OZwA924|rg?`pCT&b9U(09R<6Vo*k z{3^Z}Oggy1Rd8v3`R9u2a{}r*B_UaIW;c)ZF5IlkRkl(%Xv#Nf@qPPqFLPTKHGOT? z{&gqB`}~`v`wzw3BsM?v{ZXqu3MPv?Je`hwub1jO=<(#n|4YT4v%BYJ zY(6D-Ymt1xo6=?L=87G6{G{cyiTl2XUC8C$EKyja>5 zU$AK_(`c**TGf6DJwmdDks)}606`}XuB=Sj9Jas8{q4?5nk z{;$DZU&U@Jlbs&^ckN>{$C4t(4F@}~$^NT5-IDG##V}w8{~`|6eyisDM}>u`GK{U#ZJ8d@{HfIY~vN+Si_YEcaQBU-7_)+AU(!??wJz`l@GsVr}@QM|W6^ zb{(CSbn?Bp!>mJdp55B{LXf{Hd=K-B9+7}UjAzfTl(}Kr#;A3py7Hx!^487g{F;AH ze4SXc%_*VJ@qjR!z{}_p3CorkCp;2Rd3JrH6o<-A`H$f;28DU%>4meObxyb(ZY}>% zFuml?idkOGn>#EW=elbx}JCqHh`S~McZr{GVFa9VOXT+krjJvLS@?~-L zO=jL2duFClLruH(6JGXpVXq5s_C zRcy(pjAyAW6+M~9X?r1>GyT`R{=&|VXX`^1-`p?qubZ(V=bA5r7Pt5Qou0ZDCQrK- zu9?rbV#i6vW8MLADZ4st=6v4%=-!LPz5f5RMFW^5)GwUb6}M1KD4^QTnQ`q^3#B6q zCUtLK>8CciVv&JvT1z}<-b{-%tWO(RO=I^w-0;<|U3rPRMEBWA_Fp)%4$V2XyqtG! z!gHUp))LQUD^gQ;>|!vS=T{e$eAwpZXX}f_9ABratYVZis{G9{$=T5Bj$?E4mr3V> z=dCEseYg5Wp~7{a<=y3*^rFrG|Mf_V5fBe@+P_HS&F2meUgg!#TqPH+<&)QR@%GTz zT6=E?{!G>b$G^Y?px5Gp_uTvYK>$wV|~0_bmNInDranZ|Nha(%k^cZ z1Z?@0el|koX^RZ^{ac5x=>7C`(0(#g-gN?dXPH*ytj8(*rVbbUt(AjVq8u}8Rx>mz zxc|D?lq@Z=O0#lLaY^Mqr*_4k$8CfzbzA99I@9J~d+u+FL)gEm|IH7+XS`q>Vx9M@ zc1`xSRUa)6Ts6)+$ys)6{>D$@bL1PX{3hHp&L}tgTEk@9dNA$QwZR9-1|z&6v3rUB-0jTmZr`mJ4|AWkOgpOP7b+pF-sA9b_nzjw;&CEB zkJzKyNd&| zUAq-LwCy|@uPi$0KhHB_f6k})xT}6YHkvBbJx_A7iW9l9kMr_QZ@1^?HqAH~EFKcv zTl>a$0UzJ@NVm?7UsjX1(KglT-NBs5S9F4o59I`ev5uzmF?i-fg>Cb?!iSV)!sNeQ(v4lxf$rXZ3b3{Z2DeHJt(t1`Nt~a*-9(tJf)L%Y;CW1?qINM zH(~zxoa5hW2i`nMgY6Cet-meeWnaC>{Z_T(UF(Ti%Rg-C-dw%@-Lq7s%GPy@{9g<0 zx>4<@dR2B3`+9A)AD?zrex2j?!*F><@>|sza!&mxPjO~NseV=Anz^=m-JAUu8+)d8 zC+;o_T)s|mi>}O4rw>BWUEjNyEzGyqX-9aaPdb*c_R<&87RD3Sxi7iA9;i8V)LeP` zPwIHjG?BWjo%#1J-pxL9N>*pr=ZjW*Dqm;wIhH?Bb5}ZO8nmf>%ewfEatGIgM`GVN zx_yoLeEILK8(I!(Uq1cI4?DK?zj9RcSqmFClSiQ{`cWYkdCkTfk1RR9;!$N(=AL_t zH5dJLpWtxgVa^ZtyWGnc$=q^Zxb@K6=a1Fxb(bggwO4KnveSQ~XR$a-&yxTDg7c#H zH}74^e5S*q^UnLM1)F&LFLli<*t{;r;-&`IQt1shu5Il;(suog?xarrl>7%KlZ0!+ zb6xYCEkAM_xt%-yqUGo57?Y(pLr+Sxc^Gcu*&8GGH|)$Ko^S1C97Cb8yFbAI@0gFk^ezx}3Z<=AX~a zuYDT#H$k!7{2#kQHT&lnP1hTtiI#Otk2^_vQPZE-3A6MZ!T17N&a#9 zq*Tu&V7R=NtEWGEPf4Tzm&|mn7JsR)=ld_-{aNOnbW?;=bO zlAPK+hFt<>+ZRe(mLyAed@GEzoaolYb!fYdA9WQ$)7A2zwt6Wchq~{Qta6{@k9FWPuDoN`8+(>o2jWE{DDz9aOFuE z!C6N=o|XK)H0Md$j;7ecoiS!DFBW`Sz~#7F@xrs$M|U>|J`H@c>GnUCnU@!c@*G+9 z>Aey|^zB5qgLw+<;adZ?)(4-PwQZ7Tan;K*$5k_ZljiIQSJwIax>0z}Jkwv@4oBq9 z>22^A-@a?<&pSF(*_5VFYDswhWUtcFXN#^TJ9w-)|NLC}k!59f4^Fo7$-e#TSy#~3 ztCr3jmA6GLJRD~kO#66at*D6JyY;i@Xsi6Zd3F-hU$ZplC5n=o-JuiK-^x36)@_Nm z)|1^@IdS3F{)#tli25d6+MVF$mNj|LO#OwulKW&f)!jFGm9u_k*q145`_wDuoeKWZ zWz7|?Gx5h$?O8K-6{wgAzDez8c%nY%(~GW?WwmprH{O`kkn?k`?f;aSQ;Uq4uKS)i z)VNMc;ka<_ua9vJa+7wfFmgI%JE^u%L`3>l-^>6Vsq;b8ifz*~`j$*{k@IVLQy9KE z{YJ%ONgYm|Y0o4cKKScXHPu7 zL3PFC$_X#*D~}$CXj1hS^LFIEBkIpPN>f=O7CX*k0Le2h_y>h{8oOue9VA7Nu8Z{ zyeTAncc_?wOz8fn4NKf&>LP=rc{hBVlPkXWjH*~)$4SK&N3HN;#>X2z9e=R?@#<~6 zmd^G(Dl_xG*b@FL;TKcV&-_zrxJSwykq1*Tk}AET5#ePjOA0 znVR&{`>Nw-`8kfJ^K-3FE|1!bRe%qPdUEDq^<=as*BRn>f-{T@C{`}6j8T1E2i z35#j1S(SF=Rg0_-(=XrZ) zy`}sCD@8ZUB`JjbxOB>YgRt?z>7^b5$LBn$`Q2fAL?f3sLFjD4o)3DK7yd70mgsxR z^SN$9{$8V1PRn1$nDBp{z`1t*Iz^?mQ>5k_7BAemf2Z-Sb$aLKSiLAI3=j_ey;8Iy zFRg_0%MErtksCkm{d%JocD&D0l&8otd*^}}qibb~9&R$}GCNaG?HAs)v4H#fN$F%8 zqeTwic+NL?r!KCpx>D$5q<8hliiydd8%5{eKd@Kv-z0Gjg()`I&U);Z=PT^6R@6QH ze??TVd_uXzXD8kbF4r&3`}Y3oqCDHk-4;5U$GG11tj;pfh*f-bHQV_6so2S@YyM2Q zx?5O3y5;W6Nvkd#ytwVau77*d1J<0(sNAi5R))%b-VJt3vV=tGETKPoSv{VWY=b|v|XJ- zfy<&ApM?L&yMMHNwq3K(3%OH{>$jXMc3pKasNq1eS?r9PdHOr%@d=(5{4&Eh!o}@~ z(W8m=A|k>Gb7T+4y~zHM$Y6ZkewU4QL~W2#`ud3*%`WV|zW+p@arMMG`Vaj6$Q%kg z!Zd%4h*O zllJ~l-;yu!s$#dtKtJXQJ0ajS!0 zRytL>N!&_XbgAC$>3qH$&OZxpuw`{V)1GT~|LQB|lWk9)ePp@q=Xq#Zgq1J%iCLXj z=U$D>^4<8lb;K^Szf+M`qex+KE@fx{xoh7Fg#*Vcea*)$(nMGjre+;%xE`u}{v#TMF5)+I;Si}g+mvpmsb+s8La-KtzX$5b_E zO=MUX|C;3By{?a}&GV&B^V~|jo$z<=Q$_~) zy59l%SKGH-Fy7CrqwoGDT54^@_xDy8HhKn1c3H-!BzVqGDgUqh{FUOqy~$^UA6|6P zJZjvQ;I{nUYL=U-dA8wCuU?wCbosmAbH8s`%vQe9%F8dV>i$>dt}A>!EU%w$-O{FZ z(LnV_Vc72}Wpj(>1+#=ku4dk#Fwtd6z}dq|v(8MLuNt&OT*y54ugaI#;?I<3EpmJJ z+aRX)mcolkh5yr&Z&b%E`b+im1_-0E4|cJ}8a@v5>2#4K05ckkm}u8un`)%Dv|i(joy zIm^fQTHO^|^JVvW8vqN89lTKN7wyVb3a%)gPr-;y2eqZU=i|jAy zcyIEY7JVwn1CWuwr}Dfw^Zwp?x#Gd=GeE_U*D??b*#Dbmja z-X7j{ldVEBz2*`(^j2}WqPa1xqsbD_X`U)K2YD08-91Y*`7bLY0Fw> z>W5GJc`nXS!=l!Pzo;i&N@lx(TjkLmLis1(UK3O-l0R+w{>1v6tE}grT}xfG^#I?; z1*x`Dd6WK{7M{|+omM=z`%%I4l6m(8?yQ}x5jio~X~~{5`xOU z)Vz>+bjmk7S<{lWUcxr^2fly2H}}is`rR#2!P3hsV^6Y3DKAzzVZ84U|JQ)u4@~N{ zo=PVC^Il}R)HUb4Y(GQJ{=cIC8oAEv2ILlJABnirgep!hj#v;#GnjYJuv5?uVNMg~kaI^9^TT5e8GloJ0QeQe{x{ii>wbJtytsTd_0@l2=d?_;}jcZE}Uq-sMP0hVqN2Uu*T~qd` zRxw;K(P86`361vep81GXOU!X~-Bn$luuI7OA>-B%bK~a~ia8f;KjW2Pw?sd~~exUjN=^ zar0M4b(Yoywi{@FvYvHD&TaY(XYV8HJF6W&Urn5Hy~FjIO$|dpLbXbXo_(F)0p{hg zMi$i)ks`cXthQIpPzX?5<#?Fe`8w~}?e%<0=Ou1$Z4O)Z>}t?exgh2F+A)3EV*gn* z9dd7cQIf1`sO)nK(Bf3urjoe+ZyXIY|-Zk;i?GGu8 z7t*55|Cav9_TxTBaW~XR->n>l7d;eX{iJX&8y^1K@s-567 zeJ2OIRMNVvkVbx+^zmoZzlV%dxDt;>Wgy!0m@6I^g7{?P7Q zw_|44ep!^NypO?0>+tqnS7tpq_8~`8=(_lpc^97@mvCSDeCj?UOW9+aszhYwuT5Wa z_4M_%6)WS*y+!s&ZT)|jYkqK+`B{Cgl{+_2Slpr8ptxO)f!6>8@vN~pHIDepXEdYVB@c(#e|w%*k{`*&UN>9q*F%XehvHiakl$5r1d zZ7uL=5%A%Q(Kju-@7nWt-XmRWulyG|O@7Dw?y1Tem|Z&e<+yx z=BkLkFE-(*@AK2gigsSUooU5pYq&n`^-b<&Z)g3@Q@#6IBgIPP%DKoj$(P*o3Z?FQ zKC@nzW0n!y*K8wXvg3?oxW91iv1+@m%=fOZH{bo-d|#G9>QCy6xwky?%llU%jl?7d^}I-v3_Ro|&~a_6q50 zOPN2tab$Y#v&UG#D&DWy@S@>ERlO;vb5>rk6AQABTwu!k)xde;lX)jf1Zstj+_!XS zIA0KXW8qqU4vTNRcJ=os%yo89*iia+`=#$4A_x6{Y(JPC?fCCY`MN2KBiZ+{3)s3a zxxKwqv(xMCE}OK4Y(_kP#5}sBOV|&TdZzKKhR3gu+-b;kut;}#+Ii+LA+O$By}!zF z@J!z8cU9F39`|h)-SMDsTDpwnevu0Q6_wj3KD*?3V3WAc;g0(S7p!<|)jy?2EX}yu zm3rVeOCP7d?UZlbQ5RXB-m{dtQ1^UM%)#&5Gz``^Eq%&)@>lfl){{G%c0FZ0%jR4; zcWOcN#~T^HqPLv=RJV22{lEM2+V+I7zMuT+efg6|ac0$u^I}x?s%9teTrQY@VM=G- zQjgpT-4~Y33%;nrD^ZZjDHgmmxOATLnWWkUvkiBu)-U-{_37!n_Uhy$H|4%{t17MU zzn$LUTffrgf7ly2C;w#y{W`zfAHR^~lNMt+v{`!JU**;%e)CQxh8*_RY)mMAaQ2nh zrac+0PfYf?&h?%D*JwrSzT|TkIJLX#Pr3OjJiA~2IOq5YM!n<@(@x!-J!x+AugzaF zZfgD7!Z7(JZJ_-(t@)hk{G0FOw@m)JZ3{ZqT|8L3bGCKGR)0=Eg`nwsrLv!G zkG^{L>}mDWyR@IYnETiJknO>HQ~S&o0wwyxec^Vo=i4*&T4J zyR@qM(T8)_b*=jPNL2;zMJW3+Ri#TLQCo5=TJBQqTgF|XO>Ye(=6RkgDY!2ma&Fa;Rje@+X1#DQb(fy@ zH(GaN%vxg>mmQBaFLklHw0fM1KBIE++_IJXrX&h%Rmim4Gv`gebc&>Hkq8G{+=KuX z^^_ML4v%EBezUgniJuI5v1Ubw*NU_6OHNo7uRFN7W(T6z%SrdVPfmbtO%~Rk+ME=&-DBPkwJ^8&7JSuWwU~9IbI0?2n}Pyw z?p1F{HCtY^HfA}syq11&;R@Fl7pF~2W=YSQR^91ep1b_4q`QVglJV8z87%(< zEq1PYm2Cf>O~doN#`U{C`?|{E@D>s*TUp0_^&AnONrV%+>&!Es?uiVrQ1#ymws1y`}Ks4$dk#%C)@fa zESj8N>Hi~KI_{~U-Ggb?KC{aoZT;$#)b;LKbL{m!P9AajH>GCfJZabeKY8`e5|8Lj zYxPz6d33mLA8>op6#47ER7Yp5yXN<6udE-gQtUgZ`Qgd7Wi#h3Q*w>D^(gycOLeud zw2nQ`i%pzI{ktR0t(u<-n)kSD4|s0BX`kJSx|S2WO`XpvuHL-vI!AT^R!)TM3|h-Cd#3w68D z>NQ8;?@z97_bWe}QdTK5M_04^JhiCa&k+4%Lg9hZ&{`e;OYzNH>Y{%>_1zvkJ4Jh6 zBX2BU+s!2}r}GIdlIs1ZGTD3@r|0eMOQt05yK0!Iwp`&avjS(V?@Q$!TTggRnqj3b z&K>lgG5GKW|D%hqGfladskirkg4Gu~;o$m}_8ZFAEC2eteIZ&)4-hPa754~k*6{hPK=Jm56oEEIrdaG6dm?#d%AJM&nw4f zdGPdGHQjxiJZt5}kbwDuXG|0dvO*eclljN&RJ3DUSyYbga|&%s(3vjN-Vj=AwqX`eB3sLy35mzUicGFEef$6W(iwhp z%hfzuif4mT-XC25@7S}gp)a1?Ol9$WU?Ra_>U3TB_J75*Hv%JUe%_lL_B`VMk=%@l zO6~KeeLeb$Q@8fR2BVF?{-26^YP z?1rDZpPyK7)iR`?a36Kvzr@vZojsfeL8f;<@NhQ z&T7_6*XG5nRNv*$ev)%O?~K)drF7!<*t!OoH%!VBi}!n|C(jj)-4rEo_}s*+8DE?^`I31bX}RCvlMVbn zF(_`zLGi^-_lqSy$L-(rDkIN;wX-NO$Mwpd`Q6vOYPvn0KhAx{l-YmuepNSem~ zjasI6i`1IFXaBh2`!Ubzp~f8R^x)dq%*-8b)3-KC7jHY4IJau%thteUZ_HJym~Oaa zMUvj*j*9NgZnm>}Y?rsYhA>2HY>57sqP|^2NaTXf;)^jAT(O3twH-$bEw|lNj{2dU z?PhpB<5k8vBYvmOw7!?CR+$KJE?Kl`;jPwLO)~}fZ~s^z#=^H!EO_Rr?^90CaQwi` z-TN!F%iVqY+!a@5J+O_Lef8d&PwdI^e`2}~o7MLg%N(7YXZGNL=P%wcJ9Wl{7LzHy zD_2Z3IWs}zec;n-mJCag1!+@koM*2+=~(gGUdU#~-|e4%UCr2?SuetuzvEx@*JRUu ztS=^N{@}J3KKAGACNas~7EkU4-tpmoN+B^NdB8wX8vJECKh`?|1w3%kc- z@uz$jCuhI6Ps-QKbl-F$xkBhhdVuoMUm`1izvcZ=>-D`?Sa|idGd2Kd2ER^9cKgX&t_wMYliR>+M*)i$Uq7;tHyxr#bE@wvEC!;&FowR)U zSCu^KclNE(aN9qpR?=yWrni~&@^iJQuIe#$k)^Wk^ zAg4%SSNq=|ABgQgyt3|W?W!e5QV#Avm7QuhyU=K|vBMS%!PX59-YJ*z=L)$MF1RAv z$mi#{#y{dN+c)`1&oh0yR?OUZ>AcyNBk2L%Ej%WRum0#n1f zEPE0dvs&{%wA}t_%-?{ccxVI8L55G{QhUV%B}_Pm_O~XyyOy(r$Qw4S3uoMDdHn3+zT;0PmS{fou<3r@|114V#@<)?<|mf!vXGH?FZ|?JzSv~% zujn^PMxSQ|E&dig&!s(@=a;f%&R4ydmnYcHytd9g!%-5EDwG^7$<4Lm{Ix6M5?j8o zt_xT3On$6pvGQS1^l`-*o-GsIAKLx4JGM;v`A+`)PVhPL~JA-4>Qz$?pFmez`aB z;2q5s|2QRB9rM(;uhMZ5ckJ?N^xeq4{`JV6m{9h z_p*+N*6q}s^qu4WTjrUpeV~K<6QW3){U*=SW_d{mA&j z(hrQ=Lbl(RyHaQwSo`DDRJX-SYBhQSnU%L>k3D|fynn*NUH;k2etKLedc(};K2`eX z3|*`B&Fh|LJ)Qk2x6bp8;=>73{?1+L^f2D^!>1qm|0mnI{*Mn1Px$KLRQc%cw26z4 z9D2iD=Dy^;&L#UjU40G*&mHH8JZo=J6&Keivv0}Gi$+DKFQ*?eR6NNppkEl#5Vri+ zoA~Kf8~V=bvIW@|=F2XrJSZ~Z-P8jMd(E5P{rR2kbNXKNcB`pM*|#U1Zw#K6+ZC`> zv6S<=V4C7!R z?`gkNadRuukMvf0t+ZjH(izX!2QPWAYAl@P+L83mrZ70^Cy{fX%>DsNeYo|Jkc>d4kw4JeI_8ylO0XOZ7 z-yR=r{=SgICiAeO*Bf2tjM+g_A0_>Et=}qtwC%rDl<&zMB@r#3)}&Z|e5~KUW=89_ z<-&JUd_!hCCP_Zgk}$Bbh~(z+PH(ad_%7)6dAahxvbtGk_p_eX@7eOVCr~Y}_15{z z0zvx{@~Z#&ev1ly2kyGO`yDi&L zH5<#55uBEPn!0Y}UUx~$BMASx!pdf2r8tJfvq=^20-BuU0W- zug@%W>xlVi<-17g+-945DjV;=C zYFGYG^r?Q&(9C+wx_E!J8q*}#?YLGO1iU)WIvB zl+EiYwr}1IM)r*54%^~e?nO;if6I_xbzLCN_1Q%4BclGxxlHD%^6JFO)n8}0YI(=G zd*a`j&O%$Z%%4_#I(3zc%EN$-4;@U-I&WjYQ~jUqLb2k(_hAcXycfyVP%gNX{C{I( z-}iV0bHkEJkM;)SpKy6u&l{<#81eFYB%5%b&9P7Fy)%zjyOpG^Qt+OTv1!Rv|Giq* zB{EDOCEsORHS1UT%l3}uRJOIHj5}I)CEN;9G<n5g-X(dCBqqUp8kCNA&~FFa9mi@{O$KkJ@ah9|10^Cb685!Bu~lVisF z39-9s?b$>xS@7s(%v`~iHN|t^id8nhBX*p7z$9lk@x+2XbC0&0>K}VD=f&?NOyJ zGLx1HO3nSNdLZXsgeZez8_S-FuQWEgo-)ncIJ>v~!G3=8h2d6?7grl3+!PVJto2Ro z5tmKXgU%+|NwQyNIGr!L_&zJI=ECe>F0U3XZj=AGAn9D8iO8Rfy`j@{h0kXnS1`LG zYPvLGox*N|({<+J+B0g;z3ypwwv+#KkIGD`@85C)x>v# zI8T0y5J}L9y7(|^Isenw+xVOOS&I($JX5@~^49m0_P4iqvaPduzcSkCzS_pip<%Q3 z?pfM;CNsJDRBhALj#-D-EzzIzrHQleke0R4N|#2rDvwudbLQ*c$EHukT4VYXRc_Sf1qtEN7AV!-Kk zazgN)o7`U(e%ctVw0X{oU+eh)X#|~~@J7s3KYG>)u7>ZNo$nN<2VOOc)z2&5uvw5} zWu(WAGu3iOq@9o>y!+o0!dK0E?O&$p%BnX+@HeRYxe!$rI;`P@eFCc)Nr zi_E!0pD^z(&7QE6Q)$&45o`Cdyv^GWO;X(xv}ecXze&3p>ibv7%eq=JXJ^Qlf4kT$W4(i1;;y>tj}MI*>^^7=4<{bcY8znMFf4+-fKsFth;;X zY0ufJ?{j}g6`lQ(U@^NZH)(Q~U-r-E_nRj5JvaWxB5beb6F;Lh%>RnuTRT@4yE*50 zr`-*kzSx%Ke`!&Xi|9Thop;>dCS=|#@4M~fzfm}H(UuR>j?O-BIr-=E72hvg-*)`r zX;eRPcG>dZuF)xX*=wVt-k1gQNEckVXnx-8bNYnbm$MjmpM7^mO!&pOsL1e>w{H3I zH(ul3r6fNy!9T}SDWs?O!Cy_DgB4#7Z@$aXcS_`YQt&0Grk#sd_-_lkCve&5SxuSE z>h8@Bn}5D(Jol%H`AyoiQ=g6g-+EQ|`0)u=_4zw^T#jsJZ{N`JKtoiirBVF!#;&O! z=7rtKDn8v{XD6`kns%Z?`H}W*+ar^X8SMWsH~T!#0(UFBi}D9*FD||Dmtk*V@82&D z^DeFb{3`dSfM@XERcGhEY~8;0(BtDLBzt%+_Qu>_8k(?Zs%$|@PFGocm9MhJy&m|DFt`tMO~=(%ZzkbxUL~o(d2+} z?UxefZuaYZl?(STKgCh9=#s&fbgSxx(oW}CKFqH9eg0rFZ^*C6*oF@*uS?FBNnF@k zDm3%kzA62$qAz5X_-gqnZb|!GGvTu_V`~g^RO~_ZilY%c+|PNB_$E!Bd&joEJI~g( z^yCiP8O-y)M@&7s!!+<_vHq{H+a}Fvvkgx6%*nYUvSj0?1Ka<poNel~npjJk4I0f3!S9e8s=Wt?Ppq*7p3!ZEx9|%bbzO{>3T3 zs^{X#ec!88CPs=J$iV28pV7*!NDPKO|yl9nw?UM%{%k3MC^G==Ps4}YNd$~c`<6`MKx#@RTKD#M&la3qBp(#fB{;g8G0s-0Yn;z(~6l5vQFi>mN`}VuT@A3Va@oxhx z+Ae36i+wMsG86g7vU^dGd4Y9L`=Vd}Eq(}2*So%~Dp@#Cv;BCd%@#KPJt55hfA16C z_x?=c{B4J$s?I3iI1^HRBA(qf?MYQVW8VJNhLhxGS1{hFev>ib|5V>;`K5`L2MFJ7bi_<>FnXNFS&TEnbGowr^qh9 zw#33cM#7Qr*UVGt@U_!5U9+4kar?%H$Ndxc`t<+b6;m$vT64LF>VH|kY-Z-t3z5S zZRb11i6x7+>+C)nD7fTQ%LZ+sANRiV9(2BX$D}OEJb;0dvwp)=$?c0@OB+lxpZ#8d zQ{Y}6(*<6)=`)?gWhbm&AM#JW@bo;XDe|l9U)W4eeP_<=yOGt*<<8QypU-A&zUy>W zO|{SMi_0O6($ftXtH?C7A4-NyY0WYB;T4Cxhk~qn|kBX-S2NHPZ##H z7f@Y&V5Uv{ZR^edUw0)`tE=pH7VuGfu+*+HRe9>XRd40;&pxwf`jN7z^23x@b}afl zg_D*)-#N|X*gh4$rew{D*RFU=a8~6^)%s~{EywWJDecIcF5Q3AcU$_KG|1&^J((Y+ zbmY$0%EAbiKNhkXOU|38%}kZemHd2myYub4wg0wDs8(Bg*hb!e(*Lt{8{^rl>wDWC z3CO7>ChmH$d4l~!o?oBag~Z<X6 z2PYJ1C@Tw>dE2%rL>6&++NSJyW6Q(`X`m#@@#MX54tsQ(fQ>xRH#d`wo^PGKr@w62qJO7vW>;D9MK4tFw8ow*% z%ZiKTE3XSCO;BR|T9o_Huj8y?Z<@*`fn`%!{;N$1el5O>N9w8n!`+)3{f_7F%t@JN zaJPE3{n~4VA6d_@TYK(;wf(MnWo6+}mv)}rusG)3Hy$};CcYxa@tQG-c1=2EwK(an%Ypk(KP{G&V-=H1V)b}>Pq^_$Y>-d4y3mWY(-n_C z3}gEg($AsvxcX?jKBG_5(ddcU2Olh$-n4Bx&((H&>*N^oH4!x{B0dQ*{0X`AH04;u zkF^J8WW_94t>3=Vaz#^#is2*%`LHmirQ-XVl5ctxoI9KQXobk!ZyfuxDjyh#Ghfmc zycxFX>zqAOXINiuRI{t%O=mk@_s%_5(0A(gd=ZB9CEQ27-O_ir@p5_WQ7QT-eSO1n zu_wLz+h-^5&RQP%S$es1ePCQdOMLC4yy=gpo<4Pc*^{!14o!opPcb zc^7|fJhkmhdTnyWJ>SApuU?yZS}V$5c5Y^z@37Z(Nx*$;ZIOjJs&~ShTIV#~aI%$V zD$}}nzxK{|mWC!x2K7k2kLfS0V=cc;Jym>Td8tD7l8m@gjkbyFbzc`=V)d8ORqEQu zGePKa?b!`hS;u$UEWBI!iSNoq=K}|ioIR@57P*rlM84a-k*VX>s#i0&PY~R3dFhe4 z&3oo=eI|5~$sx!{a;~Y#irvv)f6n8YUij^o3fGgo9hcYKW}NWVfBNFJauExsJzCEH zTYO!w_;;&)76)t&YUF%e&GPo1s4+AFVUySvgvL*RiZFW`qoiMNUYrLZ`JbZ1;v!!O*Hk*bISE|q3Wa~c`VUY4% zdP&Y9>OZ6J9>x%6QRx=1!~4=Q6t~HE+MjznD^;>H)Qje8c(Q)lFDp8nzK zle+6avswD|^MkKeHqJ1aB`YNB%A5Mf?Dz_wDje! zpJLf0wtk9~>zv5}ETz{)ew2t+*Rj|d=W%GSKC|FL$(^+;mz_$Rena%nl|KfqHxg%g zrq5*xb84Pxr?>0!fhQiPPAxyZO6u=xr5xEKlQIInUs?SzFVEm##@lS+Pji%xuNRK_ z`*VGd|AyETTM7!MyUuUh<>x+O;UycVc?^GldI)eWtlJ*BC@p%e!-1c7^H`5B+glo_ z#JXbT+JX%#8#DyubbFcJ9#HnIR6gk{%;@%f@_|)mtL_EWRw-*9Pd&p}a`E1`D625> z;+cmp6-8O0(u!{AKsgON;i;6qtYVwe6EU^SN(s**(6&R$b=eb(?uYUB%?7p7d~#j_VuVzSoW%aKN&v>ymeaNtT``v&xPMbZzQG59@(ttIu&jg z$}p+_*QDxfITNMy_iRn39Sb%#*iYDfXmXR@xmA6~#1z-d>KDjpFE#6&Yq;mp?_cJ2 zQK33(U(SvDns~-=!M~~VXRqfeW%}&&Q~BeD8&MNZ9(*V%sVROqzg6rKW}lPu>l@;J?Y#X0F}D8}FL*PF`H;y^C#6@h*`&h0Bi4 zUe&EwfBL;l{>G;odYgK5zA{wX915HtF?H+wAMH2aX@#C}l$fPBZ~N7*|CLEiGw-Il zt`KHj=(a-c?w*w68B6EJ@A43uFG}R_aP5;?rPMgY`*#YR~3)ho=*$4dLBRjUsxMTD z5xhF`N%tJzU6(c`wsXDyRvFW&^n06NW-gmloIh*M{G90vxeI4)e!IcP|9zXRU984j zKLw?TeQ9pD-gtJG&287)@vYx^b#1`o%f8_kY^NUhl;ytHwWqtw z7aQJ)5`Ne(m9sjachfKa|9x4e%lxAla}(wl%JRMKNe`TLP~#TEqUHshpCWbZgsyJc zqq64FUb!QUYm^f@or|6bK9>t-<_!3~UU=fs&kyyY&)hERTUc|@@C@U9mg+0P&o&&( zuef5!S#d~FRn6^Xhq1T1s_(3m6|Al6>ej^_n(*Se<$+70cCv}TKj-L)b*Pt^zWIGW zAT6%I{kDJgRr$#OFP_PN-O9hd;&Ode6yKHi4<3oCWzVp$h;d7bKYuM*_uTgbd|_OB z7u#K3I{DAtl8rqxHXpHDUimNBh56{?PbV&^*J#^)bgh3EeyrDas#OTnHQi;ti!AFu zF4MkKt9MHzjESv@>Fu8ozlk@0T(iq}&}W*H@=X5WJCAzN_YqUXRBAI!_d2{ZX?!g` zF|Rb{p19IQL9s<)f|8~^+sm%SPnesut?1Rc*+)}v-(6$AzQ1if$Mb(npZ+P_aoIF8 z=+*Q!AA4f6o^Hxnz3h&j5!a8R4#or4b-8$*cv#oB)_Jus2h26gn zoZBO_ct@2+wAVw!ADPZ-&ObkVn{Qs%CZC>cy(Y9F@qCj*h1K3AWzU!Q1$KN{$i{#A z*W^hn!lqvOym7_8{#hF{n{eGX^zGZo! z-kFLwz1^!^wm$2~`KsOaf7Jxm!Y#pV?@Qc{)=#!%W1Et$agHlS+!Oix{1pB3590sqZuq`*&dW)u%CQTK zE*y9y^5^ESHmZ%p8@41|z@VIdD50R?5ZM%PjxIAC6{qzlM)z8s4 zR$MGDcDq*fFd*Ug4Za6wi%oAFvA%lB)H$L@WQl1PdwR6X;S`0-jxD_NpB+=0G_!_B z<8|Rf!TV2-tgQSJuq}9*1>59X89Pqioa`-})jju+SXbm#f2OVlY*I&;YuEmp+%5j` z#DPm%7JuZmkKh0Nu;OuGh`f1PaIJzGv^1S zu9Cj-@P>%hJ%hIuU;Uhw*Sm0i6TNrj`AxYVZS^J(pVzLAs~MaQgdeM2W}zW8CHifN z?y0~f^?9BVJ~5L%zI`_->P4>FS@p$>V`pb9oz(y9)v3(~HcV2#@#$#bQqHTV zKHa$dq;Ny7qfYn$@k>9_v~r6Ymb{uf6XrOA?@L!&`o^&Cl!yMj`sYq zul>Wv+x8be>QCouXP%YAa$Zrtk!#<{et9+S&xew{Yf{Ta?kC>nM}VkZ#ZnB=K1NV;hSyhXWggVFH~y&H8=d!f*?Vkdljaf zONt~u9pc?PuRh_=zD+uuTV9IIy27x(GWFHdk~prf$?xKJa%OYO82)=SQ#nNYcre?$ z`$aZ)?!F0H#u~#NzRp?i?AhW^Nh`lby3W`U9=3i{efZg%D)v9$I=Z;@ZR@?Z^~x!} z&n(GjzKL}2RBNp`m6|*~zoRwxbmio&9TU8wuAkE~`7&=++BB^dJ#H!ew^@&}9xjlp zt$M5EATQpMl$38(wkWym3xCfJ&2(W`?{ur2$!WhP+GPhUTYp{f!TImEx4rrN<<|VL zZ+7wQD(a_Iz1CPid8@pbPrCMg@<#Ho`Q+$}5sq}*e&21}}MYOoj@X&nh zGqZHkkFG1zPG|R88yRP%Rdt56PZHndr<@`9bK2jf79W(RiOdO|*!ti9@CV_<4NQ5T zCA)QQhffW86INUS>yXUQH_~EYY0Q_ABu9$M9=E+t&8~a(Q}7b&=aubEXHU z7SCc~-}OcK;-Vd;hr$h6ZbvyMAJj}p?kEq+s`+`&fMMFpyoK}MW!>8I|9rrKSzMdt z&zxPc>*zTv-wrDomC8G6oBP6ZOHFRbhVp*rt~`C$SKe{>L2=fn;@8qf`{!H5oX+RE7Qb4nP{nI$rekDo!;zv| zfdl^a`HsT#T7RXP+D&@qboxv6itI1Zd_~ML0^9HIR&PI;82ydooj|d8gZH_t(`r`^ zU0R!cUA@U)KQfx-Roa@>{6W1ZUrks2^qeJFsl`z5K>&+UPpVB`*UT@vhk|s3lUGKx z2A&WQmslnjrF15NWzWO?M>!9v-S71dY}vea{r7|?r9wSEPy3Iob$a&I;Pk%zp-MKU zmnFTPWCXR{c~{9V9r2(iLg{^#$&K*M3pH={uZrUFUYhrH=Cs^N56%SLKX%I|RnE$D zvu8@1tiVZ?Rli?P<#FcLQ|p}+*yv}>Z)IiJ^R8f5l>fSj|35CuRe$4?-hBM9gb~Lv z8Oz9SrW)baTfTC9GOxDv`(N_DVsO3Gyx-be*EzIDzI^9Vd*AehW=wy(Hm^>TOY8Q~ z2@laub5{6t)VAd3O83_V|5K$;|JN%!amyt9@msGmMd>Q#yFU8`o}B3va!~Pw+%C2K zpSEtEx+rx)TJml_zV z*TUzw@bj(cd)7hL6Y6fCigbPK{eY*ryl%ox>9oE-wl3M`lZ$#*9GO_|Ve`;1Sc-kh zjE%=b7gn}@F8sawp!cs$+%BILSD7nE%U$>_u&IKDWx7<|uJz3ae2(q7t=WC`-Hx#H zv!_4oYtz3ezOixjDNReueim7`=(9iWa;qnm>eyXAe8y+TG^yJ^|MsGj4869WLB__R)xGkag$k03W$cuP`djCSWC$<665;L_PG%Heo=@hJ-)nJGM#o7#{6Pt&@$-*|_6HlT=>wGSlZJtWr4vepV)GnoAS|>=jVuj zXWL=4>V?ujw>PZOYFAWpz23Ys$$1-eMlm$x@zbh%wx&7;n~ttoIZt}a6xTUozlD{q zEB{LQ$?NBlyXJUD)BVThFE#u=9c(b+?cnyFV%W)f*=FAt_bqGECoDAXeyF=-@s_9S z7dA?N(oXYyf9eYNnKF+PYga7iVB}sexI^ifxZ~6PPfsXd)?QhV=p=hcno=6w5qsIBk#UcQ+rAM&DpNIcLoH2Ze*gMt^+Cpo5jCJeqE zd;jiK^1AZsn7UFZhg6z9?+51;tp)O*bY6do?LW!!Z1wcgZF^Kg^P+9?Sf<|W5j`W8 zC+lP+6cO-!LgIFXug~Hv|LmIMafD&pZZ~t)vrlxC=RC3W&?#kmZxMfGal)&|+wYgN zPGbKa;t|?zaK&a;@Vof$x82R|JUH^YeHLJCzsmm(7;3D~;sM9!@Y54Svdenryl<=Mlhrgy1a-ud@-Uk>-$^r}bG zpKr73_+gR|bN;<#fpxqXuk2~HtLF26ak6!ny!n{%BxcY1ibYRiRwPXQwK1QwAoZP3 zoIq}l>bGG)A5*{l1o#8yX*_78Ms=4IHsv9lgX1b@Uw($45 zNS9m2_DHOId?+|%%D4WE&ZW;@h@NL_RuAs~(juE2b?{^0Nl$j&p9yk?$K$VEpH-cE zan-XoTK{iVa|y24ZM8^Sa$=xd@}ioHffYFxyB-ELp84{cYxDIN+_vX8=2%rm9EfRf zyuD@eXZyVi;^!Z^SrYnULX*UL2eWUpl21Oo-`wzV$-PrqKU}(_FE3xGGbtt|`m_&E z*5tC5YSlS5-VZXW5(Fl_mze4}Z2`mHDfdl33QH$2-mw%iiu|}*YNl6-O=QfCos1dR z^=G;^$2RT$qR<>1)P0mmc^~K016LE33g%B@c~fl^`gQG@!kG7wF2|oA**x)op|jky zo&&Q#A>?%{*tye|i1aurIizK3zgtzd59R;l>GPHv8RL%ycL7!>_aV z-t}_HYLu}*UYYa3Z|WMi)z#a62Z@|J%WzEdhqCjZ=exc?`r5iTK6jR;#q(!z4Ej;^ zMatj%rY@Sg%|+v!qWIB`enI~&Q#(#33bq-n;QN>tyU=*=lJlY`&8IJ%xIKG;g5!%* zb1UZ0TG6yux#83`sqZ~*{+aI>idLW6bwb-uV*lw+(`0V0&P$VjP~*1WcS?T8giFVB zckZ*XSX$i{J6ojqmk9|ljcraYpnbG{`xs9 z9)6DSZRNh+@|3eJse(pc&Wq)#(jG(~PDXDo!Do**G-SIEkZ1IcN3k_yeF7mdT zbnmiF#p#0i+_%pja+&5JUnk*w+PZVvB8Mw06tDcW3X>CblU=`bx@yj`C!77&$6C#pQuig>Ya6bU-9$g^_IrcDGZ-Ix>ZIo!E_XZnlEY>l>r6*Fq} z%8CU)DA&(;Y3r1>tGLR)d~S&^FWZ+bb4zb9JSnvd+--K@nX~jn|D5op&9?g!i-gY4 zpUk1Gz5D+KFV|1cj&u0=EU7=qx6oJr*n~;ElV;85wGhz?W_8`Y&#;mG%js_8KkpP~ z>X!tV2fg0AIixu=RByu+1&f>ZY9&>9dsDMSqD}WbJFE64&HtSBahFWiHuuAFraOVj2)bk=)O&7pd zu<^!?31<8SBD;z*{a>G)vwOx1??3a8=PmW!qG$Z1ZsxkQ{>dC`7B;obT0Boa-_cH+ zQRw^bNHGoG<7WcC9=3R~=;pp;Ju{>8H&yo^+)}-9A8$KvY|PBq)8}T~(oGIK=qcO% zPJVaM6NSF2sq*^-ge;F#ys4F}>HKX|_AKdom9g&$OX+KGdanIEvnVei^r>YCF`#`KztvfygSs6SbMMA1d#+4huWkdSTZz zd*imE^BjuTo-JLP%d>SnH2@gu;r8%JW6~A}anZdvMgD+fq?3#{DeowX4Fj zAK#g#Y2A*M8@Rre>2o-W5FOXwllH$i#BjL#`}#17p}J`DyWvC(;yVdn#r4 z7wzx8QC`M=Y{_ah#_RFF{8wI{?z6z^>!Ep{xM#51PW)+ES?C--&e&*_H$V!5;TgQY|FH=T1^*V*#I_^867tA8)>$@+h( zSyz%^AbooGqD_p|#pZk)uF2m2ewI_2p| zP&tzQp5bNZ=S5Fs-A_2L+*-}IRdjn(m8-G%X@(~<@3YNWZpkf-Zba9rS|QlhzDzOKhHK>Rr%uF*Bf(ueV!g_D|EWo zC9}h6dhsW-a|R)=Laq9CUX1cKOJgx&4PGlRRGGb2Y39ODlXh)fbkqOk?fvUgOTUM8 zzj#o7^w*j-bL}rxv@@!nT$n$*hokB5RD<{GPB+BEZnK1K`FCkiOjO?6Mf(n{30eMg z>*Uk19Q%yz)D_pB{mp&r?VPZqDT1nYp(fRBd!HRNIHPxFo12uIpF5+<|9PJ#d40_; zRC-%}E2Y9@a>dd8QPQ{OKH_c(U%ugN^Kb5y_f6fCMBP*`t#`{>#eU}C>Q{=K@~+Vg zC%C?B_gN|~V`s;0)YgCV!^!gpDksmGSN(14bl$&K^WQ`nhn>yM`d_D9+iT!Z)3xr; zf#PE&U*+A;I9KhzHm7EOdWPMBp(^o}*{OG#_?y zyX5?1wp_3H{iXr8#)R$@r|c7Mm@k^Gmpbw33hpU4EWX+#a&vr`&gvv>zf%2moXF+> zR<~;o$M<(G+IzHok=l~{9$yPh|KG9(5N|5GzI-IRCZ^p*eIaV`J2_(8Mo6HG5J=r+DGey_#!fA;mcL8kZ4{b~&R zR4_BarssflIwoc_% zDY7VN(Tx$%Xx_eUg6Q5OhO%=mZS!iGbS=nH?nWQ`!C(G2P74Y*Ot(CGHrqp6BwS?S zohj2kwX{v~6_uFP#b@ClA<9_)GQQ=)f^BSzC$GQcHEmTxcZ-;*$I?k>j=kGp^UNjo z!)uwXO`8P#6e5;dF~#UJPt5rh+!i0qV>tK!1{+ZeQByO;Ny}z+GJ0H$Q7uecZVZV!q=QXY5N^{K=6$x&GbZb_lDZNGHe2?>ozV3 zUOZQC&(-YR3YknFs%5@C=GBUjV)~;oE%J?y-@g+5YOZU}S2hKk#Xd7w_f_BX?z1aT zHj5otnDpH-*Wc~9-}?2^PCL18U+`)yJy-i+Yin?7yS~|Mv7654Zg#D!|EU~3lc76Z zJ=I8pPw&S@1`gfZ=?+h>X&QXl`9a{m+nmJBJzu4Y_AQN?-^eHLk??A_t&yE{{p7T> zmwh%pYp;54>3Aw6EByJ3J-$aT_VAf5q!FI`KgYTQ#`z#((Nvpu)Ubw^mh49*X@@!aeQYs?Dd_nu7BJ0>YcR-dM)gAFSfnA zeefgOd?$&c`hwd+HpCgHX`^N3rCn-7?R-vdh?G z?H2p>leuT;IO-cex|6FDGEcZi`CWeat(`9B;V(=cf1SSX<(A^G#YL~VuU?#e%H!L- z_14BkN3I=Pep_tK)8GSjx~tp@*3UMR+O< zYtQSb%+9!T>MfUGj`6wbV_SOWW-n}N-OA_qhp*;=-5Zqzi}zoYE*;QsUWDbgy5#a^v6@ z9+@`*DT|n#MQ+}zh&=r6TZ@I@#Tn}C%9a)Xr!Bia{}x9@!^KL~qxGDhF8%+?tN5=d zdacM~j;?LNOY2>-?Q87h_|A%K=v%z{pn>t*$pyI%H?~#0xo}j}ul7w!|Mr)~o3ivI zPy3i9XcXKs+udJR@zSJ^=W$1!AVUSubE6v5HEPnkeX>uT4pex-@Yh_q%!{{Y8FSCi z>od)3-TSv*5b9O9$1Asv{l?ZucNOPKH)=lYwC`W{CiNYQ%)!}T_qmx@Xxw;}veSdX zF5!U7jI}8T%+CLt)lrag@uCga`Th&37Vmm5wykfMdZ@y6=T@i2zU6WoW=y`4^)oVl z)1ka;Ygrrx<%7o+dpFuUxZv>N3s7t*@$h4f~GH z`o?f|Elc#APF>D)?I@NWclG(QC2s>Z`a~@6Q+1z^eQ&kt@-;O(ZqLY-IM}>_FMnx( z+rQjh5s&*9^nDjEJALj*mG?c@?ur@i3y#?a#C}`hbUWFo{p`QoC5^9TorPj-_m-Xh zD)7eSm~mdR%)yzZTa`oAWZG?$xi@x}OnbEdqx$K?7um#_bh@nyM4qqE{dryO&u+eJ zR&yWh-F`vGQN;cFvBOzCb^kc+Uj$obODe36Te2spQOIiLk4C1MYL9&u%s3jZF?;6O ztuH>6#h+iiIQKfkebLQT_pdgjeYrL3*UaQ2?>W44wH>Dae`k<-n2GuQ-ak`6RH|IL z&gSZn+R)Xq?EPHdw*SeDes|bJ4sViTVr5fxJ0qpmBE9iPcHZ>G($4H3Y`)n!?pkGJ z>vBWtQt$M=@qgw9FJ57_YU7sPUwoENH=xoJzLl zZe`Kjb=Kk06XE}TVog#0?9($J#m|;fei^ZJ$5&^LQ=b>KT>7;*%f$GSBST_T(4$4K z_Fb-->)^j%b~($XPes0tGJ>ZQSw7pkNv$prLl;!-l zm7BNWQt0_ry!W2Ad2VE8yVq=XqVHA4ZUv6(pZ-Q2e!ubLuVq({+zCkZXJtuyDZVLU z-xM3Wl$%?B7caD7_@QIz<~_;PK=Mmp>f+byHrXbH+Skf3mZXKqSKhnxx=u4+op)Nf zZ$(yp+bV^ z=-H3Y<*T><&G5J!@4amguaVJhXQpRjD`M*z1XZHfZGAc|^16`a#vY@c&T&Bx`F<{M za9B6tbpFrFR|`FASpIamPm)sD{BlFH+RvCuz3u1b-(1lZ)FxHM<1@g;NMzT z{y2MG3ztvn?SSq5&jL0{teGtDIAc;Y_c|-{DRC;Z1iTkKuZo=bkd4*L^_LaDe%RZS zDoODN>P%{rYc6eH5c4w5LHMLx_PM>Rr|GMu zmvc5K^IzTJ%zA9E`j>#ogDyS?CPE$7v}7g<79Wqk^B zn-*akJ^$3{u!CD#^0rJ1d>glKXIZqBwbHGg`%ZTL(zj>j_=l;!d&p3xB{4ld<=L%* zvzuzV7S7&mKkrsS?#?&G(sz2ch@CHpR($)&>A{Rcha2W}^WN+EpusY`Oj^CYE!$mo z?Tqj8zmD29ANm`1;?GKjwk>vU9mn#t5@VQBs?utjl`edk=xb*_)$e(C;7gZk<&|p+ z4y``3xp3P>nJKM1IGPh?Enn+ao_1yht1^#{Ve=Hdk~&xGV;9a%D%N5b7!PvIL@qL%aO9^V!UzPx|c z|8FnP&k}B6DV%%kUC#S}o34)os+1Sck65E7#?^5Cd;6a|Q9ozvMDc!}^0Ybiq|7h5 zNj@4nXAf);PRKb}rW&|^-bLHTk!nVVJ$^DfICF--eR3n$Axu@}a7L~|^z9!-T*e7U zKd+Rpu~nb$wf0g}+_bvW;#bb_b=G~gNp?T-X|EH@x0S_lPd6}@CV$a#u(`^*=U1Hte_VzsnP_$v-pi+}TW&;In@bCJDQ`_jwxcf-!?FlY$R zP~6mE>uc~yW%{IpObhZ@_XV6TPZM(Scjbc`Sxdcz{gjgp84j$#pCjNl>ro=nEnD5G9S92V{*pl6K|1GP=h2YGuqUwx;zpRyTjl z{Z%FLQb2U7n&)G(P>rreNXyTmr|bJHPMW?*?&(TTsKv61&eym ze1`QocNNo1TD89V_WpbESK|N0n_A9y(iU|cah{aCQEm0>5bynK3+|t^o8A8}nrr@onRB{-Of(69 zdm_5Qr`?nKa^6dCXXLY$ zLK}qrXQbJ^?zcVuLaXZAgekXpU%ZRb70{bn_hgUm{*%x4PUjM^u5V+V#jTY+jvRmvRTsFT3)pd!kL05?4S)AJ2yahF5;idMJBL%D+MUyM?_Q)6amnp-c;w?lFCK zH8^#X%w;86kMr*rW}VBLWxD3!lQ{>jeX-rh;@LItaztH^h6dX%sdIxp2wN*I6niZQuGhxF#7Ke{etM)|}mD z?dHLi+)}FV?mInx^(;9A8r`E`LQ!W zbZgwXSvM}#?B9PNr&qVUNTt2UtJXgGQq0Q~XWs*s6Sl7J@mlb8+JbG5Gv+Lfn0xnV zpl89BgzR&tH$}y*)QHVUW)BSReBY2iH@MLL*E14_Pf&bYEz-YudW|B3~9v&bz;@@0Bj6!a5Ftpd7O<^|R%Prw)DAPn(*` zeX{@Vv(2VIw#a;$xM)e02YKe8a7jp&^|MVAs5}!P@)H>n9q7>k5etmNM zthIU@mrDG}t3PJa_`XZl;(~m~qJ&jm72Bp4HpF|ZZTuj6rk!=tzSJ$#?)}~CCc>>* zJhSHdk>#;Y24Y0f^{T_O(oV~>If?Uq`ZTDj~uHHNG zs7q$2Vv0nPkwqY9G3O#5p6$I{-^|!o{=RXdH^gPe{f_ND-uoBtFf_lUw>?s5S8UX( z_V7~uw!$ml-ge(U!~3@LdkVXedBfAmiXTeut}(g5DOuwD_|xOt`>PB0%|E&CH$$F` zr20DF`=@JeP3BN~_Gb5=SDgzC`@8E^Q#BOJ`D>#zU+j%_QHgwdY@^&N0p1a>B$>1V=a(_?h0j-c}Ew7(kxVOsjn_y< zJgV0Ui{MdkYIPCYoG>N-vK066mJ1qX!JD5pDm8~NwI#ocKawO;J<&Gf`p!o0tIn2L zTBfhBC^m~7TkGU&ENR?$g;j3L0{zz?Ba<3`ZMs?b@AceI4vOAPmP|1dp1e%I@H$p` z!I3Dr)aLW7i$j052+ub95nvs?UdiB?#+h9Ct{Q`=P4_!Y^_klxuDqPj>^|e)spqm^*OiK!Z#*+^+s`RtcRV@%YVCe!8Pkl z$FdlvYfk=Gc3Uc6u-TpFCX%OOyorx#$u~YtMMI%#nay*~OfP((TBW;Yqw_aWyLacS zD|(k~Zu}*+`&i(wQrpf8yF5Cb^g7mWpEu=sZ+geZsKzxL`@YEEOr2uf6eToe)frs}$#(UHDWgcRGYEcq(iBt_vZM~muF{~^}LbZ>=a}qu>aMQuk2hoOJ|w|cT_mc%)KmIBw|<; z_p>2Th;iYeZ5_^&UFPafIPG)YV}C;7#JG~YCr84a_#f71G%GOp-~T*YhNU1`(X-#~ zs_L%BdnZ?|Prh)s`Q}}$)nMiN@H_MN^O-bsXSJNeQz~zW})Ke^V-B~&JS;)?bDhy`bk$czuZ@X}HL%{Rzn@=oW-@x`wUHsQ0 zmskH5?X$a+-W;>Ew0+^{wk;~RW$o)kV?)l{G|&AmrtjgDD{9&F#{a~#r-sHVS6$b! zK6!Qc(}~w7Cft~NB_qAf=kV&a5!pO3w^ek7I(lAmD~gtD-70;v_WN9wqd_iT*Exr5 zx_4OK=ZN(QrI_rKyS_PyO`V$i_o1ysk;9YxJ+A(jCt77yzdH5f-@y}9Hu5`dUm&Jf$OOslA5jlm;Uybz2@ALCwU)UF8dI5@nbO8sYRltq^dgSCQDTP7PpiAV_X>GMgIp*Sog?o%wG+S~Ch>zI;yfvtQ1fzQk(jCsu4L420iq4Rd1CSbAkA%Z@e4cdb5V z>+zrQ30l8ZWZ%M9Q!cG&`mJ|P+3@PRQwdMr%sW-^Lr46`am7^o2}}#;U$8tqyRkAS zPsQ2d@s*;E#RoY$_^U3y*!d%6$AY<1+j@Sk4OsffDXl8cLoDo3)0N}jeylcI7L~iO zV);{LgE#-TH+J>p{O{wel$dw@`ko0(G_Oc|7widWWbkFbsWz#{#Lmx`rzh0y%!`_v zbMp4o^*d+C3hfjY=XpQz(Bm6@o9xy;-}?1nLhfJr+C&G5g@>Lk*`{H`vTb_D!BmOX z&jEaq%IlhyUp&a%b-$>%WNNgOgwx{)gZY2-r1kC3DOcv($V_}+ZGJXuUC*n7FQ14_ z{bFF^cJ0>WBWj0M+VD!u^{QI+Da3V@w8>F19rn|da!ERXg}zV@;;b4TiI=~+G;R=?C3N(3Aa%oS8^l-g5qx%_q<(}B6t z;jSOPnk4%1@j5nb|9Gj$C4cUPxZhVqCTiZ2T9T`J{pH6e>lKpA_bR^2jn00(fU%;g zka=_F8V3=R8+pGScEn!4?IPr~*Z)CJ>dm>SO@Xoo3PP8cPWgQ9%GH#j%*&pZZ;EFp zH}?AJYWmxDIDPYBI#W4G_-XVOSJs=cMSf8$-kOKhNp8DR?_hWIM#+`Hvms(F54Rb< zb>GdoMqYc5I%8UinXe4Pxtq!N=2h}Fe|qKpufEYuVP$P>DOdUXTSv__rhPnn%;ITy z!mgdUSZGGO^VYBly@&UEKsNV+cRQ@Y3s#; zWaI6-=de%wcJ0~1gYy#@PuA|0?b%@X>(}pF`{yj*w>S8D>D-W@m#;)pH+rT^$zA-} z!4O`Ov0m5Y1mnIxHgD^#rxr->I?@xjPs4GRr{I6d|2wWEh?E{bJtZ#7vfT6NR>e23 z+cO;uI-i{6o;zihQR8W`k5^A|1~GP(NVI|qZ- z7jobIH?8I~vC-I}wQb!IyOoM=*`g<2+IuO@J80W>^5(Rv$p?kJmuv0_mA|nev*E{- zr#XKvvfq}_UUK!}78$OW9H*~6JXKb6r{HAhPPbQ2?$5Yc|Ep|UWM^7_?MZJ5r{=ii z_kFGPLLJpQ#`2Hk4a*YS%vg4ts6{sHoigw4^nB@CCuMeUn5ySRJk_+0h`21D^KMPe zEP+W*lKX`(%==e-eJ@ARiCL}Jx4io2D<@pmnPm29mQbPIu1&@DBG+5KT3N?+3kgnq zZ8*Yh=NPQJ42xvt2b6n;MQNZ_WnZ7s*De%dc=4OnyTSoF~sW=oSMWUTD9 zxjps53+<=1TJc?ba@_YYUE2HR-pp8rl+SYeek**|>wmQJF4r>t+l-6f>gpJ^wVq!2 z^Fzuq^+ylby!02euYc{Kpu%Fhq{b}j-GYDy^XvcTT~%^)`+rh{@6Y*$CkG!!UHs0O z*YbivYJE}v{c9H{9V*a2p0zvi@{!+n6=LIgZ#4bh#mN6yB`Brtt=&00>t}g#(knK_ zhrQV2nz2JS*UDj{Ud&YIJ)aYH99L`o_*wC#w4|rz!#D4z%Sz{`d7WflyT6sKAS(Xs z37_!ba zabMWawbx_*+?gMvF#qWNm($sI z{z-*YXgFNxiD;Qrb#s?^!P$RKe62mQPYUP7Z_Dv16z|eM`u|6WR*yw82kQ*aO)M@k zQ!adt$u`$1e{=Ms!;@XyHj>BwMP*5pHfrqA`Sn9ePwu}M|Kl&u_-abMU$)JURO?^6e5@%m3U6~is;11 zGk%|WvMiQc`PJP1dHC4**>7LI2rF5XvH5QK+K2Z1VPCgN2zOY{cMV;5D^@Tw@9oM> z_us7H&3>&O`fu*@%7;q#C%o)B-lOn+$u=DkljGB8uVUCF|2C4fp~|##24||kjbmX^ zN~g=$X^y;$nDOze@~H#V8dJCoJqZPxU3I-Q}gm>NT~CE*YLR zVd?wygk}FHogWEu-Jdu8IFW0ol%K%t=DTLK?XOpx_Rh=+eJgc;;vHLV``hK)UM)H0 zDb|!1y7k7%Ec<1z*VM0HbEl-dt=r|~!M&0jZ}R=9HvRL&_GIa|n+uy%PCCl1oOt8l z54NhGsda*1a|}%GxlJk9o~4`;t#zjO2b*7Nzin}+^G_jGb(L84M5l(xhcBnFRGF-* zZe;8>Tl_QUXlvG|pjfLPw;#Q{uIBq+mtDYh&U;l${WH_WQYO81Jn&vq-BKYk-P5Fg zp+v`4Att9O3%lcDEdL&9@t!2O)muthaI=}kwUvKnM{xgn?Lv8n$e(~lEUtecGg&WtzPRNSh! z)Mj(pkJJo~$+_(NU-_6%pRn)6?3<={w;6xnce$jys8)(+@l}fmyI;2#mpFZ1sBLt2 z@uVlG!~gXtlo_%G>`m4uugJ!>qMO{%{CCof7-iZgwIA7f3}toXvm^>?*+Ua;&rb2reZ zDE+>QbnpF*ycfcB^j7(CbFKN%qt55V5Xm-o?$$>a{zaKc@aK9In0*LXRh=ViQDIlO z;h9=)OXA&bmD6_7&#nG_mujg{aX)@vBvtX`8oP`i`g4B1*gsWdr9)S!XXQ>$BMvqd zwtPN|C>1A}7rSq+t(+3~oM&29{!x9M^u=zmd89 zf38#yc{85kXa`XvL+Ol(W)ml9PG27T<(TPXlcT;z za`SIr;o*6^e}aIg#tMhznDXFBFVnwrx1Uj5>z@5`n}_X}Px3cp%{Hm$+!82q+!l8+ zxBt&JyOJL{dwm6Yw$4BQI<(=RkwKH)X$|9t@$Z+0p9}caXKreva_pXk z*0MRDqV0XBxCzdZmHN41OL&0qYn%3~Df_>#t()Kc>hI2o?X_BuOD|2=j|$kM&~<5s z!ULrrx42oJ$2FDD|1)2Iiw)D82WKR-Vn0PU$sh5LbM}i{-&J$ul<36;a~SVS1wZ=n z^m^ys)2~^s3RHxIAMJRaH}}*TM-Rz}zQg;sKQde7zQgRE;->tR+-V0c|4uN>{$Uv0 z)2zLid9%4dosxg=!|S(NLTat-K6`!h(Rr2pHlV;veV5ddM*AJcfh+g&?FrPklCu-> zPRT!%`P-@Jg`J8*Y@R27R_`H$*y@T&?;Av)+TP(_{cEbHUEkc`0~hN{UQKeqQa9#bSwub&s{AY_#7Rhd2Am710*>~Bq6t3ndAAX?|2bzNJ72G>uxNNn|{f{#$ zPxD6}`rGJZc1@CX$6B$i<^R}tG#)xP-8t|@INjy{y@Y_TW^Fxx{`Gh-cy4x0u7I=Q z$&6K@4CXVwO{fS~+Sy+q`7R(mzailGyfv#lEpsnz`pK7HduZpe(|hvGecM*A)$ZK7 z=DQ~A$u;*o9p$FzZa!74nQ47SQ0!3m%!lhXX2|q}mgY$6ZF?6xJ*USz{=~uGe>P_b zPQJbr!>_0CPbXEU4DiCe2)=>J&pI%C^s{Xmw) z`{h?7j@){{v(MV$Uw_Irl4AG(&~w*VR5 z*WGUvoDmj$?iEw0M|SnnAFd5kT~eD?W-O`R?fqX#OzQo+k3Y?_o`3L_;Em&eTGnnJui}O6X=a=fd2`l=E!m%?=G;*Qh@qd?zH>`b@6>zIJ=j^o`v!nqrI_xXS15Kfj~Q zi>*!Xt)ll62cCQ$dxhL-SC+a@xRmzOwd_}IqHIFB!J;Q$43_#&Kljw8+P`COoG!0= zui5QKPHC)+rp0yat#@`FjCrRgIbnTr_)Jwv#{>V8mhjgJ)c;ZQV(g6E6W-O$&^I?< zPu4F?;l}wtljrp9uW$K1DOqs#mx^P0`Io=USgar)X79scP|qx+!cF%R^9kw>*{@bRW7ca_09djWwsnXm4tt1{(asPb+l*OiR)JXKfZZ4_gC94 zm*^vv^D;8M9`3)VGRb1n47O>ad*6$zcs_Vo#z$n+}}O1EDeTfKG1uC;WM5G4imsik3;wZt7jiE5eLVA7;VGGO zS`o+Nrhospqk+T0bkW~K796b2(I1blP@eeKKSWDj`mEZC8*4J=2pJx_@o)c)(zUOQ zORrZc$jhb~?ljfdGUL|A6tC@{8cc*H?qu`R{-#jQbncAu#eF{08ulx$@bUS@QvJiu zKQ~UR_g=xZqmyG}4k_gR$ah(~&-tI>U3b^R=W18U2^c(ypSk7e=PfVWOuUp%t*~(l zGt-N3z1L*<_1l39Dt}asJ91ec_=F}XPt}NMl2F?Ip|Q3nf76!@hI(_CJ+;YRy!xjj zZ~Mhl=TDrQpFA&HC2tQ?$P<$fosw}YJu0^`ZV@r#djD@d+l}mY!LwbC!UkdDIU%ja zk9vBl&a9Zq7k@Ey-KnnrzujBY8Luq*RQXG-I6^r8AbJ73dj9XJUi>uP1qFhoS0~C{ILNF~Debgq|2pw0 z+*?1$Ye=awa;%@0we0!%i@RP&@8q49@-pVf&#am)AyPN(B^NVF`8iB+;agv}Ylhqj zBc}o;r5;w{+x#n}OV1rhJ1=N9@6pAu$I_1uMU=e?4ZW-W=ax&Bvs%kFrKFy}j{E;@ zI6ZYS|GulXzs?GlG#zMJsh`cMbWFO&Ew9VMp|@g#MAyaa^fMPcVw4^_9O{$SEYr|a zxNu7+(W{s!5C~G>*~xb5nW633@W2UOQF=rN&UOS{$!^u zk8+n;vqvmEdi?eyznD+1`_F8@u|$=#q^VX=C3!Me%A8fN0%kqc^5=RWvtVNqUuvnt zf&|TPwwpsa9k#YaJ#J0Z>H6Q#w7th=`@WY2tG2e8)h)61R|@cZ_TH!ey4Hl>>1u_7 zR_{6f-)x)e9li5U%(`wV->3_7)s$V98n4MZYPn+XYPZY!UGqf6=6*AcKiZzNV?mS7 ziD|0B)g9~RH7M?WCE;|TyxmAWjmN*)Z0}2kj#=up6F;=xUwS|6=pzSFmrjw_5*4%7 ztErct+qKFktNoS4xq=qsvqyfX%Ueyhd>$8_BK$^Bp_PZ_7hft<-dXj+n5_!}K8O6A zyzcxGLvNjfZM!PuqyK!0{PaNm>t)NiyG_}CZoF}%FX8&P?;rm?F|GUdXTm(iC9=EL zR9@xJxUiz6)HSG`&AVb-c$xIg8J~<_S=hgep2&14=+PCIuw|PAuS|S;>(=YU*m-L@ z&UQ-(6wDH5v)FxC`e)*Xo4V`U6D2my+wwF<%8-9X{@g(KK8+rB{{zxuCD%Wk{IV|h z?f2NvQw(~$udGRYd+KkGv{;4U!X)K$9}nohH+$XZF|E+u+?C~hP94{=1KP(JZ}lE~ zXC|3)*TlB?lG(jQrxhDDHY;;4-*u{tyYtSD{=^s2yRY}|*b(OD6|#B9IeVkdlj4Qy zFOKOcu$w=wNKMoCeOC5n*6l5Q*W_8&}8LOy8>$f%Am>%9agXwnt9LttM z)mlx?7jj!K$a63(3JrPWTz_TBGafJXwcJWGD&%qk?w{2WwReAgG|WEm@K-JdUPeZ{ zmHeJ_FBnd8W0HGauPLU!`q@{O;+qWj>w91Jf7Cy1aBuat_M3Kftykl%R~}4TmubAF z^Fm!O+l3!4J^%k4@GQz$TcP>p^(nqq+duAm+`=|(*&Y$@?$TFOZpL?NWxf7{H_LP` zShZVmF!@xxi|Ux+_(j0<^RmE)sTNMAx9(>g(CCes+Q!hrf9Y1Kvzv{y@4x@yu1)=M z_XRG7sej2lXK(S-yGFJ&l+n~6bnD*!nf#Rp!ft403td%y%XOyx@KzC%%8kwSyP{-y zOLvQ}$~|_Y(D!D!l($ zzAw%;Xl#4I^JH?`nL~3fREOlQ-+$QUrr6nUQ?m91t%^GMWp&AVrZwAld2D#)!1z+w zaD|%Iv8$cYEgc1ygN{C$%RW*4Zc}?f%-jb-3zq5M-t?LA&^s5sU0Q0EZvS|1Tvkzi z@_t%PP5%#7jZ-}NzyH+UDE}R_Sn|fNkopg%hyE=nj_|v-HchbL?%sxQ4Fg^t8|gZ) zPxSOegD#Wf9l-sc8|)~S4lek8Jn&#-P_vn@@rJ$S%dDq%OWI2lm9%*cx`s^ zYo%($CXb%)rW51Y5juiX{QKB2MI zvBWvQ?R@mfwaX2vIvz{?D$wM!GGN}j$T8{5{_jbAPkmH!!&}y`W`3A?WcgeRw^J(( zgBLvd)wLmC=U?s4XL&EgMBMR~ zzU2GUd5^c$mwwV)b6`V3uZNcBCcfE?izltlW8ISS;GOBrSk2o@w^~Hsebvp)aNQ%( zMx7%+?bq>#97pxOit%k|_gQW$ea7}No4dw`$ItBJmT|g#oja*0NYqF67f-%%?4ifi zpB6Lz+QVGfy-oJ(P1dF1a~2){{Y1t`sAm1k?G-Po7Jol`tMtBlfY_9+##Nr_N1QjD z=&G1G@BJo6wi6}h;_cn{tll$A78_d}<2&^5Wccq7K{Gz>dH-5}+4ZYuA`_p*A3KBI`#o61`0(m>O)G`mig|`f-U}Z3 zMHnY6GkU_9Xz_$=4omPd%jYu>M9+IQ@8p%eM>{!$i{DsCKc6uBz5Amc<+GC`8UC8Q zJ0Y6ol2@LRxo6JpRS}i_-~AJ_k9I${Y5JgZK0x@~1r|&FX9ue*o@>OfpSkZ@^~1B4 z`sp9qZT1GfnsxDvTg#bxpNt1W*)o5-=f2%@?IG8>X>70We=)j$RzTA6SMU#A@2JC% z(^nprZNJHuB_6|=8Z!N3kB&s*Opb(Ow>;-Gx*zFYo++t?mrubtAaS^R6F-iJQ`YS5SuU0%-yy55C9p7KapI1L* z%hDJ-e=T=w^rfpjk5$dQaM4RSxeBHWp z-mkZY$F99Q#pQKN^w;j=8^Si3-G6)j*8It)>8ox!e`MdYiKA#qEmwN~pI17zuRbr^ zK3&iv>3_fL`b}N4uGm}`EXtCXJE^gEjSqjrx6G$M=9alRCYzjH9M$vW;fdrQ5^Ik# z8NWRoc8lf^*#ds>?Nv1uzVGr#@e$|Y%|rNO^YYUNeE-+ML7{=7)5E<5Qdt29xB zfycLWG5;2QjY0-?=CFf1cmCyB*S+2M`}b*TuWjaKH~Qao5cm4*oim41gMqU>@>7v> zh02cqg_YChhOiwK%j)%NpQL~3l(JFvg%`~~=Gnd!Vq3Pttz_~wivyN{kFB=z;!WDJ>+nJmmbmDHb;ud=(2b-!G99BzLsN z_YUVL*@XCZ=>^HM8$KAkwPsh9ygO-k$tP!B`%{)`lO8Jj{BY5_)>mSDrs(bIx@Vh@ z@5(g^-RS!vSvaF{m$cM-9;3V}^=rnO?fVK!PczL=@$2GUrK%+UyEJVV#z_zw+b7#Gdl;I;SA3Eo@;r2o=`a7@gvLU3bxp@ zvpwuM&A5)~!-@Y(M z)jrGk=DDq^7qa)(EZ>rtlV|nr{pW16kf06uU#~5`J*(r5!JY|99{-ZLikPqRb!1Dh zeR}x&Z0ro_HR30q*|wBUd2>bS2-~aJzUz#~(|YfoS*E};y*%Ga)haHgD$Q<*bK`|; zbNWBIDLz+s{Gx5YB#vXop{6Zyvc>*4qUA4&Z*2MUrnmO~?Uq~YZm&O!6F0;OolwtN8a;c#`*n-;U+V=-bzQjGzxG`6Tn~Ngh$~8W zJ!;OWcSKuv9*TK4^{)8Ff84CsmH$p~oV3MV;9*LqhhcJV&f7&dlGoncI6Gs{%Vb7@ zx==xdEX8Nahji-Hv{(HMEuT{qaw$AbW=V+{&mUzoX^Cs+ny!Yt%adzUINGE+(Pg$w z*6L%oq_i1tES0=qY$mR1VB^22ZUWal#Wy0??TUM}E!7;4>V2&*6ZTCwcfi@|IOj^e zn0_xezM7V@>nEKq_*QUc&HnmL`^kM34TWv4D>{#T7A{cPua01J$G_W+iM%g`%;kS zK~=@cK*4^az^;tLR;y+$ZjR%tDE9j3=4<}q^TEc=mR7TZ?A1M=Jln`*-2S|VuT;{j z=)-5{(~c**m~LFG@YM49w(0Ywm;;x)t=33BTT{WTdOkNJ`IW4Bytje+d99qlRaPzc zo^I|udv3;qXtRnNXA>L$%PgwujJxtsJMEYw$L6n7LL$AFP3K%<66Nf|cGN5|!(6r` zw|rN^qGxxW`p-swtV zEIIH^S7AbC{&|H-7Hx@n(!DEl$`#ekDhpFLs|GYqoqkeK!(+wDTgpqjoA%Vb=MwGvDG_?)#a(_Gffvd}F!OyiVcs&j!Av_X3aRirE&jtP))ka8dNp^VeBNBV&#~ z`*3U}yTzTycfxlFi!5cRzo%3aj*PT{P|TciyCm8M!O2dKGg^ z_IG5RP+xsZd$m=D?Y?C`zL%3E7{hwnJQ&DOtEFTZSC)vmDly(L%n{U zc+iV?D|0(jclq9ldn~Ayy^A?@t#;Y3^UtrS2Bp~k3l-xlm|**^Md!BN#RIlVFRJ&8 z#QmJ7d+&KN@2yj;x~4Ku{J6}UWH)`-#j0QYDPYOH)D8Dmblv04=q)zan(vTlt^4I8 zU&NY?%{BYyo%mp?n0Ke;@z3o>sTTVhpRo%^|2&_P@ll_>f395QsTZa?+I*MSGffPQ zXx0q4_T=`g86RC|hiP?P*jy-k_d|oii@vATHJXM?JmRxsUr;D?FUiG_q+Twdx1P*@xHE*L}b7Q zK-P(;#YuOrFMZ#!mh0(OhNi&S%bSzZZ5U5HS|uyaVSiD}+OB(P@Rx~}g2e_p51*{= zV?G)_@x%9~6SWF!#pY;Uy}3;4F|V9U_@hHg`MrXAvGWd0IPa2{(`>~&?R$1^_in+x z52v}Ej*FilRamy-w#Vg&rEf)9V&3i3ll-wNh$m}8Fc(A3!`s_M_1g>D_b^UaDeoox zsk;7*^H1(4xq9zD2fw{j%e3j%+m^`wPZKWg-Ye}=EBo@mHsQ5>rat-tKI?6do`3p_ zUudaOa@G#vQ;C{;+PCb#wMu}sFGTZc`1eT8ZWe(%iQ6=yD;70mbbh@sQ(z^7RDycU z|5+{H=3nC!J^O0znp5S6TOXG)cNB(Qk5;&QqA)G^?adXERX1d7DmNr=Vq*=FLr{Kp0{c`0$FP@8XH#)g3LU5U4Ra^HW zAH$jQUI}OYci7y#E6q8%+voSAkBbA^6LmfJeS>ZYqz9&Ze27ne~ZIx-twRSxXtZeD+xHS?(X8ZQog!;YP|Wb?>T{Gy=T&H z?#bWub)qxxz0LVGYm_5CxV*|>J~U~IvBPQ3;tdYH2Tj91Y*}61*XgtB?~~utpG|T# zc_q2+L#|QswxrYrb2X~$uZed)JAUMm^2|?>AseVSINP)_p0)IRNC#X`+rkfUK5dazq8w|ypu`SlhLS-CH9T*tViFRKJU9Ux29>$ z(?H(MTsf*5j!%y0@98b7|LCRvmGAP21M7aYJ6BuY6;5e+bJZ|Xdta9;rrQ`<(47S3jH2O-K^=Wh3)lLKmYM(0z<%> zC)pn6AATDuewr+@^U2OclmF(%A8P*T#ahnxPPw7OcU3kg&0g)8zMzHF{5ukIR%CF*XYS1eei=<6bTMvCQ`jDsiEnH~TQ=@5j`hNM%_To_eAZ7J0r(lT$ zpVl^`p9da#|LVi{2aYWy+;prGs>=6cJH11N$Jf`^sZ*bNByXo@*z_{w%*6| z_+RFL#3K&;_Nq6(nP#pr{eIMBlWy_eI&F=vXOahvd*6ASHg#hzT%xjT2UWoyz>4HD$ZwogcfVC@ZzxIL5#KwZ6pT zso_OouM(fHvdCjgItEJ|P$ZL;hnLW7ssJ(eob>YJQ6HZF| zefj7K_F z`%bVLddJb8%C^-+Ju{4<4vyl&$DB{>8ce)r|11T`{ePFYRt){CiSrzQH~= zW${as-kiNA&#b5TXi)=`ysm+oevw;ZPyLD~sg0*1Ud=V%XQ?hU{q9a?`@8z5ml{Rg z-MYU!|81tx-+LJn>-xUm5In3F8SgW(N~!+ocfM67*Y8Y<@j5)?)a>SZ!FfL|HLJh$ zuKlVtr~h^GoRbdg)cUfd#oOXNA0MhUNt(kG8$E4833H^%xrt#e8`kOOzCO|C|6#I_ zRpz0HhoP6hGrhVQRfyNJ2$qq@8UWBQ>Fdbon2}kuV-$2F0tMDmbsgLHcx!J z#jZV}_g6&9@2@$Qx4r!6R&%AQS#3>6~j+ zy6oiV;Z7XK4+1{|CJ;J;5lvL__DO>5Z)2EGow}&up zZ*4fpJ7@NNk>}z~m(Hg#hIH{hD0^VrP{6D*ilg zBjb%sPkZ#tql~ZT7rkKV*Q^bQjo!eo)2>&z)i$WZI(vPrE}I8?_<0?Z2CeQ@J6C%p z2Y47vJ=!oYtCU?q=1H5t_7mc5tDj%_==S{lJ)Q$42fM^;I3~6iZ;aC0ud>7Vob07| z7x|dCHJ4gG+~QZ#mQDMbj{DvJ(4OEP?|A>@%RSC@ z*=fE%4|QiQUcIv6_jX^8oSmk*Z$mQzAIPR1_;+^EtK4mq?!H;i%6|JM_e|Z5PCiFN zwjK4}>$s_M{j1>fvs~9tUTzbks%W`nve1`XiU%JtWb$-aGS6YY)b{q9+q#Q4-+ww{ z<7(eDKi64C+6Bta)$8v(rjdJDKk)O_JXs%u;&O(G%O%Vbwyb$F zZ(HieU;S}c)Nh=6v278n`=f|cE0hbvqWA4OEnn? zfXL&`rpBF-%dQ;R{rb7kHa_jv{GYB$e|I!-I!}9b=z3eq#nRX`MlROiCG#?0#ijp! zv(x?CW`=c(PwW=jALPFIYmoM|!;!^@Ogyg3Cw-UOdS>3bsoCote?HMw4g4+^$3B(M zD?yNTK7an2_y;c!+1y-r%pqv&fqWK`@`D?eTUEK;4M>taa#Swx$*Q;Zp2vQ(+3Me( z$$RLNdhm{)Hu+z_B{5ArS>RSuB-^yS`p;tV9Hl^UQ=>Lp_1m?9Ju{#7Z#=km4O3v$ z`W~UyTeIwCr`jyB?9%z)dOGUomA&0HUs*?)Rl=?@3>Me+Bubs*T6o_(J^p-H z_jGR8D;M1A*BqKKd&`QcGnnr<{NH4@v1h5V|9{r+e8Nj?W?U$fyx_TQ#!=Q>VKdFR z-v-|7GMl85V-jUfwjM}$kg#yO_lIRom$&Nrt3>r2WM26Bdd|U%M>M@06$AM9h{#-7 zddNe{N~PM$?9bb{){N>Hd+DEBFN-7^d_N|br($&1qiS*Rik}gkpOphw-OkqJi1s?k zu;yC7;{JQ;YVNHPffx5oa^gPd+PTZ-wbZZ4m76b~Ue9`T;l@nm#B#P@b9#=a6sj9I z>8xIHVt%xD{IciYTT*xa>Jyw~&f1b;E&OvObAoyCi`kw_=KOw;RJy$Sx7gIGJ=(Ji z0-^)2HficHb{ctV*u8wed_(EA>FyQwjb}^#GBxix6yVYJY+Y~Eu}sZhnH84bgnrB1 zICp&agG$X+&L6qsPwlbRmhO(dRr%bQZ?aKmuGzJ!(u%j!Wp{k8-1=|la(kc4e~!3k&5C`pWR2R~iy3p}dea5Zv-&+NTqN~0 zrN3xhkS~v3j>CeaIxkbn@Xz{;Gp0)OU6wldH+rTy@5js)2 z-zGRWj_HV{SD%Q;JBfTJ!RWId+cxr-MDLM0V)Xl|I+w8QC+nG8DosCq&zZn}#4G5i z&TQWw-#qhVFUf3UamwwxaH^(e-TbXBkFypZoPO~8*Xa{7en+p83yUzb+HR9FYx=#p zD|~M=s!x}kJmImp@2dUlv}?En3YT=9Fr9Zi=StkKlXEtRnWk^pVJvDpW0Bc`s5!G6 z@@9MxGP|Ah=HX@j*r|o=%j2e9nY(-L$&~4qtru3m+N|vM_1lM(hgM2mrFTM?oZl%p zrCR>UT?zHRrFNTbO`R1YLbtR$d!Uee(sSqMFU;=(Ba-fHJ{f%Kx8q0sjw0DV-!$!G ztJd~&%dPxb{r{%SdR9GIqjC=JmttWb7`lJ**IrJk+nBw$=4jtzw&l0ePuYG5J^xf* zjbr)j^Gy_ZezR|8J=vk-aaHY-!q=s{ReP?SShqH9cAc)URDo8o zzzc@@o}(34A4#djsZQXlX=HnQ=po}-o`|*15$i5!UhK=RWB1hjc>Z8#`pU9LlCA5O zFV6UL;mqql8O*;{+wioighpO+FgtT%{?Vv2_CLN*~y)|Up;+(TVJ>t3JN(dpm7WVC%_k4B9Pqst0~=>e0zFSg|JLC)cdlGkw2Hvo5XLxpD>P zPOh_J$IE8f{@izP*#)O{U4OaQOpk54Fx6RY)unIO7qf3&AZK}~d7{e;llTKm7^j9r z>t5&LeOjaSr+9jm?(6*l>70Ki&b{?qocq~Ne%Yt?FDI=%z{~BlXy@FYp3$?vZIs%y z^(Ie!El1-@(M`)2oZRl^d9+drF?)f)@zBw;p$ml;|9w8E z>7?^IpL24N#&>n|Vr6y(UU>Uzecu}gQ*~>GaEZfHJa22Qe7oY~(l7tE)*HXx5-0T5 z;bM0Fhp(#_ruLiWTK;o>f70~BQnwW!A3QC+pU3f2V_~wQ2s6W8VVTUUS5sSzPS1E2 zy?c?ub?3P(tS^G6|9|l2m28D>wDnto%UR+gv$r+euLus@xzwIbPr6Po-BL_5tMB6W zSw=!rel#^U%S3l%lsN6m5;I)FrF-tmGd{m5|NiazwM_HTg}h>p`74#bOT?XG^k=;J z(Y)(p!@qvJ`Ahj2VpBgl2kuF5+@uon+k5q-IY)R!4}E)djj^EluIO~W^5=;rhuY(F zUVb*7UKQ}Gkz-1%{D#8mv9lA;ep9g$is5m3y!;BQjbnnU!QrRZmi;L#4&7O%d1raO z`jha+6|9j{PN5fqA-iOcZ7!UHuGZxZRXzF<$T}&Z8a1BI4%9|Dc8?u7lmE*61rPC zFJ6i1sN4N=wbD;Di#h&ETuMFVd0>a$+)MnKG70Z(H$>VjkFN|!O;wA)xO@nYqv z`%6w$F?y@anPK|8@6E}%ACsPZm}#-fc=`Q{GJY$bzW%UpwNdQ(aDiq?^G7R#H(z;U z)V}qM)Ydm)*EJ-r1v76rp1bbsWV`8N`r%&_N^VxHnVS7pY3@gc-B0qIl^))i>1XCO z$zaL7w~s6qUcIo3D@uQ_vcRIA#r@9TZb|Q1^VHSK?oCefEp}0h56zxdOa9lqeJQp& zqHo{RDvK(P*Upn3S91Q({=AakuXC0|f$5#K3|YtDwXVqNpS?UyJ0ROGdl$3a^}cnB z*GzfBlQqxp*x8tQ!AWgGrE88eX_>v7V*U9p#D!A{D$hrnN_jZB41UmnXeb}z$WBwP-k~Z zonM^(bITsd4FZ*1B<)&)O;&kqfYgn zWxOckVkgfgH|3*ly7>B}G>;uiHN!2YW%4MyNoJfn$C)E_nmLToyz}Jy^-E@55t(m! z%lGLjsT0yC*Zq)uU|&(#tzG}@=qs)0%g*HiCl@C7=NhP8Ultj3?%DRTL+(!x9r51x zy>a?2jT6fapDgc_&MiDVdC^D1R{8I{C09^?!NQ zN*AX_%d<}-PuxA_9-h4Wba=QA>t*-A7qMGDf0J;!?Ri-8KBIA0Q@TKh|4I|_b%(BXOmI%RPmPgM!)y1iL(}^#wW8(+2d_Bxsub! zp?ckn9c}gT7WE%iE`0alx#Wj#hD~hE%G-2VPyTXF4_@b|xkXFzXYcQ?Y;9kb|6(YZ zntV%eZPFs2`6a#f1s_;8GE_gk>$pr(is#&deIa{3S}@+I)oI((vGQDjOnA#?7XSFW z-nW{2?gkZ9@UF}AUiKWKZCAKSOJLK!E;49x-Vw?VwuiET-r<>%YiKnHbPMdy-mHxP}eAeFt-b506ZcCbDeD?n^4;@!-rG(e-=r4WvrNWZvp!BeOw?^}U$&vt zTt4BTx~0FbUpV2{p?0NNI-e&@M#bNI^Zd!W6P2DV{S}y`e{}vw2I1^i+y1jAo<7gM z>rMQZ`gO0AKd>a^F8-1e&nf7_za!}4&FdlBUd)+W#h={Yl5n{6XWI2Qx_K3^HEiCy zEmZb+Rw$tSZAsD5=?6|6ZhkvSmcc3Ss&?Fp#O=rD8-5nMFyX|(P{Wx_+roZTY!aG( z#JxE@K%JZat<3-QJ4MfyxW?WtT=i=6zR1H40k;G-b^3Ia|MM+=$@a?Y@}Djx-e>P` zUexT{kl^!um+(=hj`w^orPEG0#c=(tw3a&hQqJwBpN&Gv*WZmTi+)VVZf-d8P%KOO zX2O@Fo747QpOP})TV~&#SlhVyYrH4Db66$!Fnqo9hS#pQF5TQ0-6HpV<<4_UbWZ)Y za&o@*o9&Ib+iQ=ofW=$aUNJoEY|h!#f~ zo@n32z3EQsO}RT>A6nQsrg9wIG0VI(d3E56y?2vu7j=7gS_GMJa@d&Uu2bZd4}aO+ z`uxum`^_S!+-qC^o44^7YKO$#ni^cHljJKsMXiBxL9L?|1KYW^^}oKkRWI|uk~zMhi~=uKq_(f!WYp5x@QxaHt``4rvk2Om2s=if9D64APCXzl*Ocf;I^ z`{ULAKfZL3Z;NMNS(^V8`Ahq==c*dL-6|)2EKyCLJ6Ah;{rmU3m{#f5+nUeNoFTKa z$vy7X4(+|YPEJR6akp)`@$Fns-q}so+>#kVKIu|w%MVMQi=AAM-+TJtjZ?ve*Y|%} zEy!?!eYWHa@02Xdi9)w!&Tl>KAh@Vr@6E*jGbe3j4pyJ>q&F&$zqfRjyyma9M_hLN zSFy8~3{CuG_g0H-&NkK=w|^c|c&oGMitR;CFPBFX7T2 z6RW$eo<{jwZAd=$#YQC{?=*)@v0iwCy+fc3XHRnajfg#08DmU~mxOPhUa>@gMfiN3 z=$sgfpZYr_LSL6onk{+_9-Cr*Y9hkD^ z+Zq3V;i5aL`yU1ce}A_(Kvs5v%YpbW$L8)^p(Ff8jd{UGbJ649ruCg#zEQIH$Lfvo zd`#*-?Z40T?2}Fp^4Xu;y+^@E_n_8`@;j-?2U6;0 zNn5;6Z9MYX^=sE6vAG;dbAGIGHMq~Ex7aIV>B+kd{N0lFMjJw6FHQZiOfte&SIyG2 z>F#1li7PC3jvcY}J)QMHSXSxB*Po{HXVUjduq-)W&V4WSjX-+Rb1m+~`FBrDct5h*XF#f`D{{mV`}VP+s=&~ zg28ojTEcl`UrsqH;!(3q^0KNsvzpwmX%Ua}_D?_Mbf@<5`-TLQr$%qW=FelAxjXD% z_J>ZJy^qUW?tfsHI5%rk_`|%5dPx#HZk3v7yX3g$vE}Y9J{+)XTJDdoJy%3a($!y0 z;aS#r{?a~~2Fq8WcfYcfr2PC*oP6=n?U;#s6PC`tyV%c5_Ck;G+4NWIOi%Xi`0<=y z{JL(zhqY_&vGHpyId-G+pu^)cT@QBM>ZmuGt~_0P+yJ7W{g^P{1M0jxd z@MgxG%>JB|xFhYJX!_ebS5rN{UDrPH0`777_Tc3F1x~ANE;Bu7nN7!NJc_t@TXfrOD?WN1LWs62G zm+`^*b?gS0^Vd672yzB9J*(~7J5|oB#ozRxfzE<9ZnlPtw?AE)IjvOwOwpvx4JQ-s z{S?VNcIWv*sVnWdx0L%=Xm(6pl)P{8m#7#!rk74#7k;Qz3OtDPWj!6Yg1vd+wc;Dj z$)-AYdoJl8R9gAAQ&w8=jo?eXbdwmtjVw(5dingza>PPFyuDa=savT#Ov zs&2^zJ(E(yCHCK*EZBQpWYb^Qb>G7~E_2R3qG-8G*?JAn%S+TW8y-J&^HY+0S_>^23EN&ceDh-s_R3>Tl%V zuzgsRDR0ko=0<4WmzxLdUd?R$w(i3J_ln!kRIBb5J2vxRtA*H(6#<@AQ#6l-?7d^Y zRK;dZmiL^h&x$e^eR7xfv0Vst)~jEscB6W8g0r;?%a(_3C7KhQO7flD-sy3@6X}Uj zTOH7xyDl?fQQ)7at-?Vc)2jVEozFRVsplOy)vL<3ehJIEmKy?0jnCaP(gL2BG(Cy> zbSfa;q(3U2J!VUis@v4Z%jU!%Iv=-Dt<$};#nr9lSdQied zrOW$gX4;|UPa>F3x_#gIxo%DK5&syzfW0N5f0(v4zUi`tO`K2l9>=SnU)bJCoXMHCYs2FZ{)}nc_X@2NKV6#| zCvt!H+puTrg3O&PpSVAr{Kt1+h3Ilu1%co59%Q5+QZ8T(IJfMaYSMjPMZMFX7B#Fi ztC2_vN~sF3;d?lD;*Z<)o!f%vSxtP!D8F62e*1HcYrHyAE3UVmHL-d0(|1Rb)~c^DkSFc|UIRnqbF*vpNlTten?LpYGiA*)ILtCohhB%ezu98Hh@Y^v`&+&bogQ z*)EGy!rp;MldYe!c`;t^K44c zj>A?Kk8gNA&-fP1ucIIIWFZT`nwWNY>GQuig-fNU2zvGZG})fDOI3Ld|K8b^>+iFO zy-@SKId$)Xu>Pxm>b2Sr`X~rL^7;FeNj^%;)&J#G<`;rjX7}44(@=SpX2>PFY<_|D z)01bW&3RoF&d_7a^L*Y7vrbLL+=F?&doR3N@AJfVs%)ptgXe$PWrWrjGsx}KTUo(y z;`y9e%2ft38ppC_bZ=()Mp`YeJ*Rl{r_F)7udi+w8SzK!1X@0M(dYd2EPLM6D-1=q zT}AA@E>GQh@t}!y*c7QG7vp=nz%2ggy@K5tS%Xao>B}*opIq>vbOyYi* zf8R@Y9ZZShp1DV|?uoo8yHAsUnHA%nGSlf<$MR*@ELUOM-E*jG-K)hWF0FaZqE!|$ zBI^DPCl;#M-9Gs8#rk>0liueB$5v`bChUJy7eBjW@w{%yFU_{6zTZyat7})BXT#Oc zA=UNn(B*{PRVw`=1rxodc5kRU%zMGyqUvff*PCxF{JCML)Tig0&Cju5|9Vm1GWvYi zKU1eovo>GU-QIX+!`Gn* z#I1}{t%o=NS@-v^heUOq()0&uc|J)To<F>61 z{dqmt`DBnx)8)qoDmrmX9qsqeseQ5Hi(>TF!>{(=nyL6);hf*>D9;&e0Y5q)b=@?# zOY1XP+2*5T@`iDhAjA1}4(`blHQsadJ>8diAL=$;D^c=y^;`Bdzf&=6-)(x92|2P{Siz~gJ!j?PHno!TCo6&@8axA^{8^EI z!+TTJ*B%q*%V%#FyA|x6B$&c&y19IR@D-QTpUVGD%0lNnS@0=XUySpKJF9Cz(c^;5 zg)tZHyF7Dsu4aa{<##-=pYq!N)QZ-9Vg>72i^Z6X4rkXeU5q__HF9^WkKTc~QK7aW z?2ONL&ayMMTMgG~sBC?@rionW)_xpoH84F8wJ7YJm{d0fwy_2@G zlUo?~|2cAFS>x%dtcm_Jud09V`n_PENy_9}_LTXR_v#!sHrea`ivAinZ~mg04d3f- z%xU#&tk)=%JoF)KMbbhArr3E4q*)FasM!C%H>pIxCACp`zv$H`$FxLu{mA*H{NPG* z;=c1crwa2rN6kKPZqJ|cRh921TZpWGWbx|W;>Sm33CtIK&N}zt@h?ZCK84q<_G4*y zBb3hE^tf`vCLxw^)9-?cUl*P=_Sh1C|8auRl_k-}=i34fb8U;!xppr*a!T2zo_Tw> z#1+In-FxcuS&k=7IucTk(znT9I{ZcP^v%O*U%6`5RDAoDdh>C@+)r;fHa?keyLEx} zrQRQW62UXRIzQdM;|+VL!N1Z!g@-%E4zFZ!sbUp*^V|2^g$jXrZzhD<&v|@A??{=} zhKbTFar@fq8P2hK&DC)_vhsxft1aiweVri2_ouCQ&KuEn8{ew*?Oopg_0idDeZ~UI zlp8c%cBmL>}Tk@eJc5N zt1^DgRL#2D%)S5QpFGfBvSG^CsJ=G^zGC_XmprE5D46Zn?PGrVwDFuXijqsLHeFd; zGBK(0`(q{9$>%x!&iq_|@W$rXyH>88d28;GtL7;;?|N2>{>Wd;Y@m9#)i&tMvnzo= zzI}+f?R-VmKA$P{G2^V2e@pB5?Ha1SoDpw|zRWi1pmMium{H`^16MEKd)DwuYgay*ru^xY(!D%~uN=%gc`U?%efGTzTYKEE%x^VyWF%d20X z)?r`@lz-^HSiACC<#VS8lOC}CJbF8A^6qINyIN{C3jM!$uZw@BT5scToszb8!B2jt z=UC2va^+iJ(#Eiv-&6wsC^*lRZoSSN#Hae@!O9rUmAwB>_@rDrxLvE~^rw;>+sHd= zf4=e+y?h}d!XJMB>bsN^Gm0n9ijG?GCQD=9e*c8$%#SY_L^I#dQ`PpDfY#7d2Y8b#Dao=l}Sr%xuMXF+a{A z$YaWiOCp8IEcYc=KaA=8koqOOchB#t^Pi&sHoRChMRxYNRlgUNG1^LKEwW#3{hJ}9 zRVr1M*OR}p&(}}w{nKL?oFpzNn`&)jovV6mnWyQ|^`Avj??r44nJ-v7?RxR?Zq*kR z|LXQkt=N>SXk_5nq4#c1%ZmS-gO98WV9Q=8o4u4}Z2(XF&Mzvu-_IV9Ja)u*(;qk2 z#f}0C6`noX@zH=ql=a_2EzIM304d&Nv0u?Z(kH!C>YuGo;ZgrOq1 zs^cM#$7+i>bw=&A|7PFUy1u~tjF;Xd3kg2qDGJ^s6842!7r3=KU9~S*Hzzq%&LRJ>7fTdaasNq`BF_IJaEu zvRy%|R^Qw6`NY{xQ4i;97@pA%pOv*>`}wYosuw-97KvT@p;I$g=9tU#CEWWmd<9}% zb7pVewdGO&{Rva|1bNJJNKBg4DtT?%;|)i{ZoF6+ao_r#we8yA;~yR5^!ru_7FoJH zFiX+ivQbr`k?-i9*OR^VBVwOxe6;318h+WnHu_z}|A@}?**m=~BXq)<(mW2NCcSrj zb>K^+&ZPBs`?s%qGI8&oyUhIaJfdex?RYyyd(jbxg^i4&J05d=Q+zOYuj_>$^OvYT zHov^?s9UpcRgz+s`-7T39e1M{JC%@Q&m49?|oaU6zMpRrVmt9q zLg-DQ`R$(dpJUl1{wR1I_;xy<+a>dk^TM+y6sC(jk6n0l#mu8IxhMPAE;Bo{+vBjF zQ*P4QV3~vG7k6p9R=!!W_uGR-f^3OO-?>FOb3--GoZ{KYw`shZKQlU6w z(V7mcw+D3I%f@UzRm^qU?wJW+o8&vuuk%tXwLA4awgq-q&%Nq@>9ML-hvw#ebEp1I zpOdyIw5k8tCC^=dl@zYtIX} z_kHyh!+`nHZ@;TBEfZ6Dy87V!aP74hI(E-A%-{WLkLXQF1D)PqxlBKKzD(z8vaM^s zQMs61*;akq^y^PHo)w(G!n}O;|7A}k9@~G}>%P_Y{EY5fOE#S@;W=-3QDN>kiFK#% zP7(ddAV+rw_T`Ogh?PV>Nc5nV{Oq2TQrnq=Zm+HC) zdVbYYSk7clS*>bZY=bn4d-OxTGk`ZNn zKV+)ZZM!AWYifD#DVjKc7Ffx%nQMZ?{+Uv*b=w{;o&9RosXs4wGVs=k&lSCFe{pfu z@zlR93Yo?RyfSBt7hG6=-0)?L_YI@7^Nf#V2}jkp?^-pP%l(&_wMdKf_rv)wqIP+! ztXe4Fa&?iB1(#X-&%CVY(cb6UVUfn16lVvkM(>?=RVCj zA!xan%d&5Qui1XDZ|j%7UKIL^`-c?Y>HA;0?le6$S8d#7bAvtXPZW>K#!EuCR~Ux- z>U#=ApE~axpY>U^Hox`I#Ihcp5ALft|HU@VXABD{soJCW?L`Z;H&``EsKVk`6YGxn@5h!DPF>u%{hJ9_fL^(}AeE)b`=b+ewANDh@6&amUnf0SnCGaB0q*DL9C0=jh z_n4me$Z4PTr~d+js(40})4Ds8oTZem32gS%TvOQM&3jhkF!Ma&ujzL_*$2F2fw>z?2N5geI~ffwQ23ex<z9?^iPuIuqXXBxbGj>ShZ$9E%W&6g`!Exa!Z=)+t^G6~iRh_iZvh z?QLDL)UMdQA|%~!%dyOZq9Ut9P2x_LZPfTO>A`{btk?GbU@KU7sBY&3C*wWJ{5vD= zl+^R3NR{zrzID39Yip5xb_S8YcOC-mj< z)v9A%$W`q$dEXe;BzrcuZZALadAB$~S} zo!YxdysJokLS95`k)Lj~)B3V9(uiJ(>G%KDneF<@LA5 z$h=qmb>*#(Y+KH78%HG-+@F%NdzD|fWQRY)k?y@4_dGonaOpVLhI4%XC#_r1amr5m zyY9W<8KSK5_uuY#EOmGDtfi^k7N4!EI2IoGw9cFJ=;fKozwAz(oVcv8xny>rjGk!7 zu~%wBHM%X^HNR@QC;hq*T4=ja>zKY#xS_!Lrz&E}t2m4|O?Y4@_;_8(gXn9?@(*HI zWOH?s*3H_T7!nlz`JIPrLW$U_(@%ru$ga27-u*JvI)Qi3%m)s!lcW@vwWXvcoIJzj zeA3KQVCydRC(|8!Gy^h1gwjpo{uG~|{Vn5OJO8fhvD|)=hi}{FMBJ6M4SHtt$8`HV z&03k;S(>ks3QQ#~Zu+BAz@)ldi>o#H+JLB1f@Dq*6 z(RVupT~-7re^;GYTgTgW>1vT>-N)CP*b62ct^GB9p3~mH??jRxwEOo6r7Dl-n2n$m{o!7|kmFLz+^zCkp#T-|<=* zWg~qsaB1{$ZWh%?CJQp>r}gICuZ;Ee+!=bi@|5Hm<1a7I{kcA&{NxM0;NFk(lyf8h z_`fpmEYMYdKY{br2`-pq{H0y7p=>&%OoXm;;5)5ecQ3KiUY^(r}E zbQ>7nTiZ~Y-aF^&??WE1(t|ydmro1{d=5nce$Gp6;XZaJg@2@UeH!aaQVO0A3+t+rkRVr*Pfeox#{)p0|92ARy9~25zr1r$t1XtyoTzqo(uA#0;|y*^tj`HA`b zdd5?g)qa1X*KzK7D7{1S+vFKm?RTGCa@9H`z2Q>G(|hMPdQ9;S_fEaBu{+-X--dOU z@30C=@LrUcmw%t`>8YexZP#zOd!b&{Nn;VYhoIevhW&YyS z9jXQ558F7o1GaW3EtzFhyFjXWY0jdarc{>;PuZshi9XgNIR`;fc^2ZF5e@-|u;a#Y?@ODMvAHjuO zZWBTd?VYLo*Kgfafy{%8jJ~^8{%B%VTb#YDkMZ86pL!w{OICfzt#H2i;?3m?I;D0q zDzZCTuixzxJn=C~`1J<;Rm~FuPp>wfE|YHa)JZtj;+z*R|E$rhuUZ?6=zgsZPxvVjDx2IX(;*`k0+Dm)| z+f(PQZIux@>}q&v=MtH}?wt2$i0|FSI7vz2g`K)PHXL} zuMSLmcbp~GUE5naa>>={?8)22H~hb$7J3^6FJp3Me)r(sj)N!T4S!n{?YzdjlWV4JSpO5Q@L9_aF07QAe`n8%zWgh$ zOcEW(<7_HFd2g}tc&Wet;fIEmR-W7}wtN}q50qTjJ68FZjeUBr`hhb(XL9e@ZSVHE zaVa6ycTuI<$(~b}WZjMT<@`UjO+h(m$swD~{XE~JrUi&RO%T23+0`A%{3mAhyAyL+ zlA4}<$e31s$ZdYX+G%Eb-$Z}rww+sLcx4^K#CLi<1~28c4;va;waxXKF@K%}Q^V`p zsn5vxlZr!U@dauO5dEmp{8TCpJgimA{natS|I&IBU z@kvpYtG>o;Y3#Tl8TW+gTVUsGne&IA&vbbh#HJ({vu{zV$HQ6qx~!Z56}HxlnJeUD z-X9nKH*fER*YDUi@T|IZRH8b+_)gHP$-3LFPh0gbL_6cV*UK{@pC2+h&*yQzKk*Pl zOUk7rF@s$Ve~(7ZU|fGr@kHmxf+)u((Fn$@)ZLNOHY=U}rxCwpH-oRN<+eGTI~%+n zw~O=lE^^u@lQr#M@@>Cq8hP_?xW8B>7c?zug@wY&GrLr3_!sho{uLHDp}?1?tJ~tI zacA+8;4lWgFLGbp_68=bmFYY@C9nQz%!Kd7bFTd54?l7APyFs>=Z|}_T=7!w{kxON zE81Rt-jaZa_gAl%@a@{RE;h|a<@vlV3!L=Zvc5>%S7uR zU*yn9i*9!957p$|bVat?#k_OfBcHEYPfV6)`U@y;oe|g+8?&*EUxmH1H;I8|*}LMi zcl)9~{cL{uVEb_fQ{B|NOShlVzkBMV=$jAkKkR8b>{hdH#}~s3(swQ&3W!p>s?4^3 z)8=1a+5~RBPL&J|WRQH3T$A=xU+dvUU&RTG|E8Dy_;GT^%qvgKj=q`siP>0+`TS+J zj}Cov4ju2<>o2$O@BY*NrS%`f+7=e6m9cI5k$=#r%}}V{XlIVg#KOMkOTMl>{^4tz zgYvE=e)~%ADB8}L>EW^cs#YjT1#1@vqITV zxuJn=TXohV>zJ_4z~_Gtvb@f@cednbxG|4(n8w@-dsme{TX%NB-qv4F-l=UX?kfpy z$|*{+ns@S)$3vyc<`bVdm-xx7n2S{)U=?lmlYr_24GrpGK+-T(3T+7$)H2}ia6dF$mJ?^LYb zD(Sv(8t1l_)ttS~hKDyfJ7)20l8Rk2$@y)7Wd1+)-zOg2YAxFOac=$W31R$SglpI4 zr*<6;-Z;f#8Ox2UmwbHR#T1?N_TPByon8L(ncIH86{wHdr<%H7VB%Gq>wPZEBsa3W zb3DLwTbPAc%H-{tdsfG?8;j#UIxw9H`O>YhTX4_$G&{S=uUh?I##F~t9yr9g zxF=_ObWY9Qcu=S+xi3JeXb%pmwH`n@kh@H4(!JQgolDk21Z}}O4 zv=VmrO`n1nThCf`#CTED=l0)gSQ+>2Z^`=jsb@!}z@h1P_c(YgR63$%v*^-`sad!4 zetCIqlk_%;x?A_T;_+HG{bH3^oyr-kE@w&_v`cILngyO&bYMZ2f60auU$eg{o^;c@ ze&v14q8SbI)me(pIxhA4&@B7+?UR%2X)--imT%L&oxt(+;NoYigIdpXg(KRmTnQuD!!i!*FPQiqj&-%PJAeAEX}WIzF5E#NpoobLQTh z8sg3~f4AH|)3urN*9Xn4dXlnm^40)}W|i9!F$EVHTWUBApMTvt>(>pH9XooZ)_0be zcj;V_(eF3!xRhPLJ?>XS!h;JvoLQ##x(k9&b6q?p*D}A_O8w?5zE+7T33sm)CD;VF zKYy3{`$<}QrsBQ6D@!G={*O+X@6a&O)%(^~yB}^@oO>tV?Jv$gSJ=K+=!5b6#Vuy1 zubgq0P`(kssg*RX=EgeXsqWj-ZcbjxxujU`^y81#y4?)5J#QlCKF|(Zn7Airsj0%1>ReO6R zAZLeMm&mf`yV}oOaLM~<9Qo?V!Lm0SuRbZu_@sJG!OATwe_{4(SFXbnYbVcH>N;(|%XfkfG3bZ`+G^Ba%6~-621_jgY+4tR<`BJlJt?N9kq6ckz>oY=@-7#h3# z@5j4q0>iR)@2)G?Gr2bLrQ;mUp8VSn&%{(o-pfw7*TBs!uR4LHo&CC7sm{bVIzJLi zr*FEw&*HRO21Ba!f$bKZ5`I&eC0Fmfy~1cl!GxgGACA@6so#C~@4M*6({jZ|A<-uk z^smj8TG|_-k<@AgKnW=?*$jNZN|_s7kj4zg;rXU3VX z|LAYj5!&h?lYTexv$fFs#I4LH1frj7KRNnK`EOjf%@P+wnS~40qmN~jPW%`!+di`B zk#qg}%9-Kv%ic=GTQiqmy0Oo<%-rM4%;)cgmPX$c~hjoy{rrMu zmwi}r;jG)~3=ZGV8m2kT+`o7?M9l8k{%_$$mrYe=6;aa9yXJT5Ifqyj-xS>DYjlar zSNCWo(~HLn+po8CUu5dIuy&u&yU*@FvL60fuU)%nUD~z2YhSjsPBPV7re*6o)o`=s zSNXb&p@y^TH!1QmyL_#iw=|-@C40YUmi-gES7+>RtP;OskQOuV(NpiAj=BbGUMoHf zw1}KB>2ddts9)vP`+e@{EM1c?f7BwWr@eQs|JPd=A~*0R+g@EBcw=6N-=#_w*3#bDt@%>L&Q}3Z67Axm*rf$C?)hR*8{N@q4HP3s@EGE_R256_PHa}=<-3XM}Kp+ew6()VQTe)bqdqCbFV+2RySAi z(uKP!eP=SZe%MyTmOgPu+`S{(Hdfs;C z75hz{p8ZE4s>e)h78 z+gb{u4|jc8Y`-pJp4=q1H5<8Q|F;EQ2wHa`$4P4K$qCQ?-ah)h`}mIUU)LH)T(F2O zb&!0Y)o}2(CiC|2UriNjR5o67W1FC)3^>Ql9_oZdvuJM{@U8 znDyOF%slm{@R{IZ_8n7#^cFlaW!<2AcGKqbddBN5oPNH(Ask*KQ|jc~_k3zN@4*t6 zi92h*?m5!`V0P_!rJ9ltp<6otWgY#Y{+}Z#jpetylMI{d)*Zb&UdK5beq8x!9eYir z??#tu{WATfyBi+d@bv7cX_{NB7L_(TH2UBD|1(}B-#(P^%(S!GS8tz-$+VxYy^6~< zJ!YBan@_Yn$!EXs25Zgk!=)9@wRwDOGcDOtgE_c5{AamqE{_(F{d>ZdW9h$REq%;8 z-cIcNVA%KYg3gTQV9O1G56<+>KdCe?Zj=9|k?_;d98oc_Sm2R~SzkUx`~w)~IR zy@>f`du4wdT^U@T@O2WG=!+ip#tqN@q~^ZKh}h5XC%IwD>YMM9b|gQx|F%5%YJ!-b z!4F%DPgzgCGCiERUsS(*{TKF+r}h_Zt5&&^@tNsy^ofojzXpR=5C zyX6iejn)@sjrX2+-Cf4rwe>*E-+uByD*RoAI0synUl z=AV}TL6>EtUQJFt+p3llsio`#*<=kI8d%+f|l^qX!Vs5b< zJO3eIZL*V0ug^0+juUIX#obK|ba)(b;@q?MCv^fZ^ndup#A<2txJT)t<8OKI$`!9g z8FUtKn`msloZO%Oz2ADf^RfS*UYwcr-k(3!&^xo+ddbAJm#NARRT}jlJZ64ywwh1< zXZAs}$BZBDTiXX+FxSkRDJtI^FDfj`sv*l`Yux=#T+%CcyQ-0zc;=^LO9ZCW=N3s< z6sb9vZ=CA2UWC7-r@lmUZeC5kh*;|iUy(av#%0pVjLy8m%UIrg=!@QaY3fzs@Q!aq z20s&nY%1!G)vEb+=sIVwJs4u|6ep$hMyV?#&gR>T|8e`?Oe*yD3fePm^~-`1u`M#w zWy0USh`(zs8>Q{GXx+)_C8ukax}Bbud}&SR;pU`APin6VWe7Y@bKY4}ZMVtJC-cqH zN=Mb{tE=;kj;w3$?%ue6X~RL!(zTLOEGjt@Lw2lN&VP8aSnb8_*AKn?J*{>7MN_Bv z@ICrrU7zNKo|<;K{}ZRlv@Jm^3}Q>TkIpfDZRzua#hYuHaAVH=S2s%Sv&vhQvlA_@ zH1mf=)kIy?a}8?QUTtSs9Ps1&sxsxjC*FI^y0Nst@ABp~?H;QWPRaKO38Z)G^%^_8 za4#&r)ie2n!GDWSlKV}kTrY}KpYsU7&yw|hR5iS6MHVW!+qdIH|Ht6dEazjS;?xVrm-wFWXgvI(ji3U(zj zZ11@e@JQ7E@!NGum%je>xV$n~#%Zq)+rQ*Qvuk~`_8cueZF{Oy ztF`slLhkKzjTQ=B-e%acc6~wB6oc|FrqkH`xDSednB@G#YOb~AM6nCvzaK63QIM&< zcI;*8yW{^0t}VZQ?N^qjYggvf;=@q~MGiKFyTmV>SNc2t?Yl+#_dN}(q;l7K3M=1v z@_d&<1RLx9FL!c9Zp(TU@Se8pp7{M$Rodxh23OaJGQYW3o?pFUygZ*RI&ICJMaFlWHZz+1_|cs4@5=HEjbgLaR5m-Q z$DRE7dT;;x58XGq@_H3Z{M>2_b-fi0=Q{uQ+gc#Ade5t+DH}Vl&xq&^s?a?eC?a!c zOW@31j@yi;tHt(-#pga_JY&Xaa$fX<$$}V*2AeyA4N1FX*WJpO6L~Q2zF|n-Huc^Y zy|0yLDf-@;tGi#mSvORxDL2aJMnjE%LCEvn1$&SF6FOb}^~)udbJ92WtMXPCD2j7( z+J9rVezn)^`@uKjPVa1Vjq(pJ6JTDVJ3GPMv@5$Hejm>-wi%CfH=23R3=8wQ{;6=* z{7DIaEyAXK{<1pqt0zy9dmHD+gR9PGWo&jk)$wWiwd#(O`IjbtvyM8jWXJZ69M-K_ z&st;>{~nfl@^|OTrq$6Op6|YDy)1tHF7;@YGZnhlkN^H*4_Oj>E1A3U^!GUuXKa^k zzW(kO%k2-$3|?pUxS5}6445n%7tUF9cGkK}0h`41z8lOaKKx=`;iG>{eBbK+FwaR! z`Qs7EALM^)(fqZirsxas3z%q5%0Ksuf$QVBUw1cTIK-8v{5!+PSu3v+Q7l#%_G9Ma zt#O@>S|`?MyCt~p5O!X$@4B7+27ag6++lVPA7wTEtyodM?uUM{VR*k3&%BEp6W&fK zvf84T=a6x5H81<|79so1Ea8X0?Nl)STX|br>AOSq6cyDyQy(|9oa2i~UYEtQ#Z&s_ z*BK|PUlzYE+^QC_OXkm^1)SRVGt;wg%6Lw!SJ9hrWog&-)+MF{fSg`yY%l~oe0l-JC&o>JhyALS8{$?Z6ST=VA0n&!JHR$i%rz7 zUdsM9muK=G(G#H6CQ-Bh&P(vmzxBuI+$*UAHH(iWnJtW2!1F_FugS`d2M(NWa#QX# zne%gk&AAOv@U()+yV}}CY`4LmJ)tb}av8m#1cN#oTxbcX0ACPoI}3bM1NW^t^Qs*gk)5 z%Z{0P5G}i52f3^MD(@+kV+aX_lbBd!?gx9B5-aXQu z^7_ZFIzLTk*MpvUFCuv}`df-Ue%)lBtT=zw^j?0yeci>uU%xv1;;rJEF!_7PiD%)f zj;#t~TYJ{FkfBLE>dzUuw}00^zVrO^iz(-sv@QOrH)mNisJn9=aZ-_MKCUM$sw6Yz z!;iE8g;(C(kCUc4Sj~*=|5Lf<{!Xt2Z1PcZjmM_4Uc0z()p60Z&$jX31h?cG2i&$4 zD*kAnzbxeT>40r_v#(m6(wz4*NuKp&<2kdH&Fk#;)+M~H+?DQqOY4%s%XlHFO$t3x zB~pJIPRudN@;~?1=B{LGSg&yZ-|vx*Uk@!f!TV&--*wXu`sQBGY0IfMFPrji>X(DQ z?|+MFh$z1n)@bg%tyZ5a?)LGy;q!l5XWuJ3a{K)3w+Ne%H9sXi(99xq^P=8{`T{g#qxWrm;JcKz`Bn8|Iv9d zn$zSnZ9_zNY;VcA)V2Ry(2ZB`roOp-K6*jkYPlQF7=td^alGU@dT9H(-FaVh{SF4+ zpCixkKl_jOJ8SWVnGb49PRYpn{x$j8v}^y058u=ru1sDXYsbCGY!UK( zc}DYoZMmqr*ixJ+NMpr6qYT_ zbtyP(e#9%}v7W{few`e)l4%<y8Os`}BW*Tk81x>?v)} zzmB%Ar4M|b#3TP(?ArDCn0M^*nI@ka+vl4##Xsg+>oDc!L%XHc-*&$XHgOQLl;GlQ zow6f|Uo7|8yZ9SjakCO1819bElZ};6E!wTkAHRS8bq_(afLU)^>YCH9etNQCPO_Y1 z%K9Y}Z~Z*`?4-u#!w(nubnwcank2EygD)}Ubb#J3zUHoi#W|;S7G?Z6wWf99Pt`M>H;y?i=D5e;YdUko&WE#iJu<&wnYA~dKP%wo z_IXBGJMuH8omymfTJp>bwVD+i?3-=5FGt+>{J-qYbNM4Y?;oXdH~tDd6dW(!YwYuj zx5KXOmh1zrPZN9ObT=y0{9Lr5oAY$GyF_dk)_){$8f4+;zD&Ge{-pepBIw7@tL--U~SR zuUNpbeN#pH`nwAcob~_icBErV3WMvJ%m3yt`XRYz(Y*@$-4`#E{=RstR(geWLE=WX zwKeZPT}d)!N}s$hJfufYbm#s%Z7HkYC%?7opLcn}7SUbvkFwq7d$+9e_EPKH2j_2+ zJkQ{GIA_tsj7Q5`)TR8D!vzB*4opu`vzo^h{&Q~Zk?GfG6|ikdGV{{-d#7qkx61@s z$m@2{{Zkpw>)Psbim*Gb(g~k=_wxL&T?>TWLRUu2W6QYs_uW~) z+X`~~ca@o+t34^<;g)~+Z(^m9l~IPYqFwsd#_q>Dmz(`R#IWGYw1564Nk#0TTgA%qAKU)DdCdJ;`bn-jlijat zjNd)U(wBbP6L#aT;q6$X9)+{Lg*rinOJnyfc2BzDx7r~=<%oGgXOF}d?_`|^J&nui zZmf^v-D*>>`t8r3Anw+kYts9kFPbRso*DRHd4lWnw>LRlJ+pRjpK$80`F5i!FYtBn z{iln%kKNw!eFjV3rF9Pz*VbveJMNmv@;$b2)9W=7-FsULq6K*-L~;F?=;v-SEA{3o zW67BMRTqWQSR2LWohn@+_+kGq&6y!vjBmN^JkzmX=U%f1+mW;XMJt>6W3_8MJ2tJB zDErjaI{jv#`s`JOC$_(55O~f!S#rLN#?A7ThdLfT(u{f|;J?q1=R`fP#qN8yX=|;5 z=J9cyoA2k#ccEh4rbTCC1>Oc+|Mssa+sh~DRZ9OWeOBQK-ffvsMakuKBc5)oh7pov(f9=Yk7H zMhBE?=5LI*{1&h#dF}oe?k9~b)=juHYtAM2FUeiMzvTtp%~)MgvgGiL*H0Etmwv^e zl+Lx3(Vj1{bb%BH!?8r$$*OB*9`3FvFJ7J`6mh8g5Tod{lR9osb&lGu*u2kG?ueO* zh=`uy(XS`ZZaJiR;E=ic;>W&MJA)$D`>{n_{eN5aTllTYNkQ8seWtALa9G2{+g!Sj z)3N59*|r&pN6hwW-SKxi7zWUYO?7Mk6{eC?W5$99AvSFuL{GrZjX_LS9 z0=It{+wLi;x#LvF91>k|syF_N!GQ(Q-Y;(cK5}EXuW4)f=4oY{RVF{v5I?WsFh6#w z4BIz>!u&rgPTnynnQUdd<6m0Az9|-Ij{BE~Y|5BW{(It;nbFDL-~Qv7pupd-@tx3; z>+?evEQpe_;5r@3G=G~N&*l%jkGq7IP3b-8qq_ION3)DI5KY z{iQ;;J8!x!W)(VUwr|G%o4e0WHITlzX?gC%r?+SNJO7PPx&0*ROq#`yQ_WKZm%hzB z%xN*-L}hPE$9B;mV;jy#bM7v--=1Nx{L(Gm^jG~JlSS>O^>w>gn6>QEkG!}1!mOzH z&~nb@w)dSxeZPL@51iuXpVjj|rjGM%0_X2dG(j!Ph~FGziT%MkJ~T4 zmHK=|ptIVG1L9vtJk}6t>-j)j;*b)}I$OL$A z$QNAFE#}j=?L$`Pe81yzEjH6Pt#Ms;;@iH`zc22W6-H-Y-<0Re(7O2P3;(c#tD>Jg zVk&2DUtQE_7tlLmEU}3Emm8)w$j}^M>Mc2Y8_8gUIX*@f`FIViop+# zMQw@IKeMAnz%=oGS~CX^=ZfbOAKM-`E&NpUYVEN{Ogr`7D>)T7^j+E$-MuY##qqlt zZ=U@676u+J+IKP z#q2A#w&YwmSlY5kc1`G~NfFYGy6abYTIamzbi2zeGI#gktaY6tl>z?)SoUv!ywfg5 zF=VUvb;Iww^du`4v>sf(EpepUQ{QIVm&@ngT$@ohi?8p42UpSa_%Z85Q` z@l{^t|AjMho^PBHlR6GuIGZu}%72yLr+#v6x43%6 z`gyA(=jDH8Iu5+i9}HYy^s_2zV&P5-bc8zAmm-8HqJ30l$w?0^^=Jq}7qQmol&r0uSI7o`G ztkO~7PwLldEZe+??ftTK*UT6n_r+v=OnSeuIdir8^Hrjg3wHOoZFV)^I;p2lvUb+< zFP2yCX=E+koLSaT95A=)VNs*|G`p3j(=5KZ9G@g+n&&uCWL5664VTV7UM!qBXO`rz zT}CBS3#z53IxfunSKH(MV9ILIUu$>GSh94biqc8;H|zgY{LVOZ_|caS$)~br$t@In zJ>k&XoDN>c>Vr&*vOLmppM<-<7I-p#wER5j^_9xJ11`;(x$CWJZ#!6Wq^K-gHA5p- zV8$A$8`m%ANnMejx!8Elt5?@PO_)=o^Yp9N)QBHvp6vd*`QXv-qPcT<4*poAeBUW^ ziM`J5+iTYPNpZY?^ZGZZ@yX@NE*rl7zFu*uqFUBVhUa1GcEN2w_xXnPzK~gW_fSa5 z;V(YkZAqP}kMf_|9(--tQT4%PhuAw4-6a#>Ij3CHR@MnBi{Cy&U6Et@HV(d3g0~*K zUcbFX$eWv0?DS^?wiol4UAY#KHgWYU-q0t345sB3cenSjvBqDrSibG*D-(`c5=w!e zlo8E!p7a5}7 z67y~|E&z2 zmiVdT-p4s@^1a`R`u5f{^19D?vZ3ZppUXi>{@HuDt%Y=XBNFX+)He7m=w_OI;q`tN z<7|bgX0igC64#X9dHd-d%ki2UTO$-o0xCtG?DqGv^3KkFwYIpuM(R$a;)ETcYK#q* z-C`YDowtt4dThV6-zG-0wB5F{YRMuFwuMWAW!Q=pWKM8yIo2DqKlw)A=V!)WH4aof zzMXQ+YktE1V$RkdUt4%R9$k8%6Xq>#n9~~hHF$B_)X>@d+c)1{JB3@x_2H`1_usdw zM2VVxaeZ@oX*i#j(aioHf!IHjge~M6#ow%E_HLYS^j|HgIQCA?=I>E^5aE`Ze{IwJQ*M&= ztl#68_|EUH)B3w_Wz>=*3y*gPD~SL8r9A1#Y!i>riY?5!JEtmW9-CZi9un_xk}1X@ z_o2{irLtVVd0g{zl3YC=afRd`JyN?U@sZ=z%A-4u{1@T9a^v6O$s5C-h;LtZQFuk| z_T8Vjt0(K*Z@ya_w(88agPV8#YPg`ZXR~*d+?C^A-WeYg4mN$Zdayb9^!vPvw?6a~ z@<_&q$CP}T)4O~9iM_1uVJ`zrjq+WsI4U1LEw57IQ7d2b^UQ*JrYC*3=kC~3dE|c% zR|Geo{mQ-8CX@dhU-t8HDaTFyC0qgf1&(?Ln&oX~NWZ`4>5qknw;pi*{5#)!TP2s5 zRi)7vRz@YEmIq;vzAD~77p$JQF-=s{$j5)XzJAhky9chbx>duRUp#!@@|f1%jDk861-{b?=^6PF=G2_rJ5-r3xSTFT0Wx zyh%J_#hC-;5+R#U+%5W8csqdeV)&1m{AZJjZ#Nrpy7(+%tz!5sqP><+JzLR^)vk2D z!R_VC{ayv{nfbuy+vF*odZLZ~OE10(uHO)CeD+7n+fP1W+Y6;GhUcvnU}4yH=e(5m zVznRAE!|bi3zs;q%DSLy$-u~0Yq_N+G(K1+ps-5kfc&aVo)X>Xr_Xl96(@4u_%Qiq zO5Evf5?hSFEa%K#Td{gohtPyg9qH4(KiJ7AANXFRaQ8}T*`4x>I$C|R+z#yc zab2c(i|4!g@2aJx26y@O{wf~%v)S3sY%8~)W>`k3&(BF&#>MRCW_>6=zF3sIba8w3 z)~k2(G!y!Y-q*3kzd7%5OzPT=T{9W)_l9?ztMw9ic}^%>cvrmQs;PH>B-Xg=^;|vC zah>f`Xn%9 z{*qS==cuJ!i9T}DJ1|Q0wYQglJcp0ctX|%R1^=R+RdcaeKfhhSzno!b#p9H&>yB5- z0xv}5Z|l6oug~dO`zTL;=DRhG_a{~z`dfX*|J9$8vzu4H$awI0k+*WgqBB!#LRNA3 z-uU$0Og%ASS+m$-FD9Oes@IbrJ~z#a-DUCRz^1(>-{tGRGTe^Kjl5@g&0|${2ScQ# zxV?d&Bd2Fc(08834E3NSC*KSoGlgG?(ytFMP+2!)w?y~R9W&m11(S{e&cHdu><~B8cPP4NVv&UKuEQRQPqL#q-TL>Qd3u$d z_m)`t`D2!%Cyq_q7P(lpZCUcdOsCg2cNWI*i>-cnOym~FC3iR7fJYxX?M-*)lv zIg`(Ye1?mhGOO(r*XSOs-m~fbwVweAJX~*5!Y(HrjJhIy_^QQ`!1PbHCnr?~#A}}w z_2E>y{~%BP*7|4vB+uO{{&=iS3&x&?zfh9i|HvhF~6Smp%y=$47-4x-n zNCVLzHnlC2FJBj!m#;fjo#pi&!@A89%3EAl*l+al6o^x{?h7qjAL+D0U02!VW=#B} z5}t_IbekK24?0gu1^KEc7r)T!@N9jdv)7&FdUNg^&o$M$FJD~Em3(%tJFw#WimlmZ z4?eyV_Lur#P#0Gh6t}r*&CB!u%eHb_B&qe9Zxd8H-~TS}%oSm!<*|t$lh(*-O>)pX zXksh5U_sgRqW;{sjhvEBe`1u(?tFT`CiBzF{Ar3SfBdYS`ldVQBvZP3Pu8^?wHq^H z!`^>j2q~SRVI|EzmhVFA0={O?J#EdATG5oF zu$M2SdhJ$$9GO79C+6*ir#N;?H}@V++rjYYg}z$LfvbjJJ+1e?>u^gq;dPwy<@Ck3 z&#!0ZmfFnOH_^G^WA(Pjf7%PmwoLZ$uv=gh_WYotdBz=^WAja9TaEX>-f;MRRQBz; zXJ)L^o>sR;U(oE@f0bMH-D(HcIlH7PuX!ODo1!uK!j;oW-(PdEX$sb^Qhng5ld$}w zTPNRY_ZDYf_qzEDtk*C{@$Bcfe4hM4Uy0eBsprfId3}$qA9~*eqc1Ue>RmBw z%kmwElqczaXMU3~`D$%mvFp8>q<^7X95g28cZ70u#{XYs9=CH_PO)l=-zG-ZBPDZN zO0)l-li=Ltp*FvgWp>ZhZ`+kMKeKsR$!)w@%oe*?z5e&B&(SPJK2tfL_xR;a(_{SC za?s5rWCzdN=UFZr%u|^pBQ@Ip+_G7^-$L@I_|iR=Klrbm{xQSpa|QpN*s=ZcI7k;5zSXfQobG z+uaMwJ4aU=SlCvd67tjB(+J1JAMDphY z?EgPq{Cg>rBF@*0};*UY~nWO4GDXI<~f$deOfF1|>9 zQ@yz3+^4HQrk~sRWxubA>SoVOfmII|6e;aq6e)LH)&Jf*+mwg3U#?8LxBp!J&xe!5 zPJQ8CbWeTq!>+|WcR3X&CajwY{ZdeO0vs`g8%2_^PT3(FV# z=gi#~dq(Z~{MjE~OFBJx=o^`^($?1JOpn#U)@YUo4%L!UjtlB~4BqbYaFe$DyRXD{ z!^BT9H;$>~oG#98O>X(bF!xOO$=$C4{w$to%lqHE@@m2U(qq#m@V80Mb`O8zq4JQW zD(%yfQ_oj2uh2SrW81?z$1~Z{>hkCOa$F*(_@1%YUn5~uzTu+L_RFsJKjp>to$^?* zn)%wkE6S3;)23P9yR;zC{C>jT&0P$On6{keUb=6t`l)%-(znL_v=#kdo4fnA={C)s z^Dn1(Y`$X=zRJA5wREEIbY7L<9ZyA0^75aG5LZ;5w76(heoW2Z72Yd#-i02no6@m6 zNZ{8G=Q__j^DU1q7gV0(M)rEV)wkxeygz)sI``WBrk1A*_7}~V zS7v!z%;Ep-H^u)m_2=zrz1gEX-SFnaIkWTxGCv3^9^S0$lzHp5+bS_B*CY4CmK|NO zeC{_}nYEYiK41UCw?O=e=-%y~QJ4IlUTXV)plIdR&0X_-TEiXHve*9EcD?@6s}?V% z1E)_G{)sr&-_i^XzIT=$;hwB) z|0_n&XoC-PPqun_tnWAd{3|i@{q;oh7Wro{x%yyNZtkITGMU!OQopL^Og_!LKX;GK zcOF&O<@)RIO7Yk(`QuQ2cuU|bosBP6WqxE{)nHY(ZjRQKvZn`c6yD!?`7x97p$$#@);+(Mb~=ID*yLl1jXldSAH&uKu|0b^GJl)AOHfVO;pS z`t(PYT50Pk9M9NWPRvs0amdtKr?dL>-y3Qc?HxZ2xmV6>+49Q%XlKc!QB$|h zcsY%C#Vc7E>C>jwP95`3$Q^q5wX;g@`k6)iJq5~wUY5+QQ%pa)>Ff%X*s|7Dc=yNT zQ#BKko^C7D+0hpDF34!U;ku< z3clL6ZaV*A4YRaOU6SgnrSpq+Zaj3hut1UHY`yI@Tj3~~i#(4`=PXz9*WTJcaff;L zg*HFWD=RNuU;euyCW97TU zP0Ko>D~_I$emWu5ef?#H9b4@c?8R;$a#(g?`(L)-7aG+Lx~!`DE&WJx&ExF`|IX@m zUTG3jv^o8KMQPUUt3eaLR1_Va@k!(Uhog&T+-|@2;7*Zh)W><1x+T89UmEqj-y7V{ zt^Vgd>q-YjkEwq%+o!)1dfIAWlAyO_!=F5@%u{JWff9l?zrwPv9eb|J_w>y~SEtG6 z&N{{%K3JIlBLAJhso0W=H(RQwH@@b+@67XtKijq|{-0Cg$=V3nH1@ufExy}kty}iY zulHK#N2@!rD_R9oGmrf*N$z224^-&hW_5>g#kRXg*0XHM=XzT(Oa89mvdZGc4A+f$ z8W#WLd6O2o(&01P;<&FfdtaN(%R98*u(M>T*z0|FIc_LNzEpMIoYE5;#&F+7{L8g7 z^^*1KeEFd~EehN{;6))v)+ryi)>b|OpI9}y<-}&t7 zgF5S@nJG7<&wUV?l)u_pf>C$L=U>@jjWa$>d!?$J!K1rmt*K>$dGWv88SH+&Q{Vb- zwwqYD_{Y`S$MVX%g1JxeIGUGx&&_2&*S&OGj*!n&KSn8=_!C;G6F6R!%DcT_E9sU?or#ht^ zwcajje1rFXZ=>Uf7eD{a`XTW&QN<{D`G-Gymh|hq-Mv(8`iIl|8+HDQHEemD#{18) z)?H_gc};l8^~U7~HI5hOZr-&uzw<=CsBwPD=QkqvPq#Dl1O^?P#I-9Ye1pU<1Gkya zEa%K`o9s8yd1kZmwH-`FYKvxnp7#3lN5&7L3=zWZd*6JCJ?mFmb@s7iaf@Js&k0Z8 zx{@bT7WrMjSJ$?~;Z46HD9UL>95KyfFS>mETv230ZFEEba?j zf3WY+iwTJhoA_>=KA#zI{M$S8Po`08wm#=yRFYXKebB0n^W?$Y2A8$*Z$HK9%=n{r z?bX&(D_)CSTM!`>wZyEFb4T&^wMF9faUT~I_g{#*Y4Y=ofUp}^M`B7ggH4$FyiZd$ zcsN!#y7Vk85q6w9fypR3sOC)N#1rxD63%(?H>QURgdg6&;i3k6Pz}%JzXb`WR~P)A z!}Vs7G-va|+Po)w)#g|F6z)8DbIG;f+xBJ*|0Y+-tXMbe(wf)jw&%&nU!T)0aH)hN zTkpz|y{nF^znN4mzf7$8;PWj3zQqglR@|6rY8$rC_i=o~FSFnEDF$yYWzD`hQ@1GM zncH=-!jRkBp1rY)bk&bB(LAhlL|?w)+Qa|`k^2s1HB7T_7}?usy{bC)@n=Plrr+ek zUnlhzUH*Tt@ZQ}goWZ`YwpmLZ$#mtN%KvY9`sB8sFI1d#8S4)&e!TeI44&hgWGlOG z$G)2HeR}##$pxG&?PC0;s_waNofRDI)0mSND!om--t~5)jlasi)#n(B4Yn5Tl+<~8 zprUS(BJ=b3eBX(moaKbolk!qMnD2OB(|FUpeY^BaMu~ZD@r7yJ_Aev8S)P=9wr2U) z{0U$8y*ah=>$mTZg%9iGnU$Ml>=ZaF8E!gJ;m5bLYn`q8TCDEOK6xvtRC3?VNed53 z)~iiD6YYKT$Xf#zCAODgEY3|2+#4OHcRkb3n-Uk0T)4OC^+cuh-mAIelH0RGmF+uT zp39n|mNTLDV(+c68+J{T--r5NG*wTF?YdvT;qeh6iCrIT>;D}PH2v`0-{}44bWhiH z3rlpA<~UAF^}7G$oYKKyzAC|k3{Q_oR<>}sl-|C($g}_Fnkx=#wpTYlYhvA#X0~_R ztyRo#vt4hCa=t9ry4Twv{_rp3Lyk3b*+l9^7tL4va(Rx+rk@tSy|Wo*)zyC=JagHy zf1^ob(xFvO=Kl{b)-?=2#$Wm;e&ORwP5;06KWM&cbJimwJ^AhLd0%b_9jyp&?r@r) zX!B-YVdUSxdWX-?3}!k}!|OBat>tc0Zg)+^c~>l#d{`?`cxAp+bnC8!pE10dlCw<@ ze-qQXvo~A&_!3vcbF((w;(O+j!ty>nXQ{hXw_J<XxZPak_{~^B^x!&b2x(FVj9-q3Wl)=4+ngBk zH*>@CsebQ&_&iDda^_moD$kkk^R`z1WL>Uf$gS{sx6BgVuAJjB6UrER^4iNYN8hoBiB?<>;kK z#cZDG)fc~sKIG&UIbWXT9{TInai8ARs@cmt{k(JIF66baYM#h=Gf9VWj%D3Pg$4U^ z_MYT%*!a?x!Crak#2rjV^PZ{1uZlUir)uLN-8a8~ooo!RP(N<_{B=^%vKKkkM#sD2 zj6AJpg>t!flw5mzO>!pHyJ0E_jfLm z^SpOX``+xk;#M2oy6!X<`7BH4_{PWMRkiD_C2x54<^JGQSq-6DAAfG>alMtz&q|9c+@g(aV9iu%3%_tvTTGYi{8>g3qpPB{L>*#bG{ufF+I1vx$j+M$*Z5G|7C&9chyI)!#tVtQvGh6wVXaR>P=v5gwmVmVS(|d zzwlf%(A162-S99_MCN1}W7Yc=F5JJ@2EXXk7Mt?W-{;2CV>?&IwwWxxyK#m`{`$Uv zLjrR$HFnER?_X9K9C^KbW6JsPs|T-c4^3Y)cOC=BYw^=R|9Fbbe0;fP$G6*BGj3!j zg^RxZS^co!#k?bPm(E*o`qi?4U&1fr3d=M@7g|4;3Y@o`J1M%K%{_-#>sKV#WYOI! zx%J!$YxGrrR0*7!9KYl4LK86^Z-y0@Lo%;So8>X*xaq@w-+K46ZL0#TSI#%RY+SUv zY4Oi1&-AXoW>i1KRui}J)!yvo5f6W@U!i-zPQy;bdDF@RKRw>3RF`wAb-Lbs@7X(j zHT%kG4;I^f?m5b+irmxnnnrvxf Date: Thu, 25 Mar 2021 18:32:47 -0700 Subject: [PATCH 04/66] Implements conversions:- ascii, ebcdic, ibm, lcase, ucase- adds (simple) tests for conversions --- Cargo.lock | 117 ++++++ src/uu/dd/Cargo.toml | 1 + src/uu/dd/src/dd.rs | 370 ++++++++++++++++-- ...hars-37eff01866ba3f538421b30b7cbefcac.test | Bin 0 -> 128 bytes ...eef-18d99661a1de1fc9af21b0ec2cd67ba3.test} | 0 .../gnudd-conv-atoe-seq-byte-values.spec | Bin 0 -> 256 bytes .../gnudd-conv-atoibm-seq-byte-values.spec | Bin 0 -> 256 bytes .../gnudd-conv-etoa-seq-byte-values.spec | Bin 0 -> 256 bytes .../gnudd-conv-ltou-seq-byte-values.spec | Bin 0 -> 256 bytes .../gnudd-conv-utol-seq-byte-values.spec | Bin 0 -> 256 bytes src/uu/dd/test-resources/lcase-ascii.test | Bin 0 -> 129 bytes ...nes-6ae59e64850377ee5470c854761551ea.test} | 0 ...dom-5828891cb1230748e146f34223bbd3b5.test} | Bin ...lues-b632a992d3aed5d8d1a59cc5a5a455ba.test | Bin 0 -> 256 bytes .../seq-byte-values-swapped.test | Bin 0 -> 256 bytes src/uu/dd/test-resources/ucase-ascii.test | Bin 0 -> 129 bytes ...ros-620f0b67a91f7f74151bc5be745b7110.test} | Bin 17 files changed, 465 insertions(+), 23 deletions(-) create mode 100644 src/uu/dd/test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test rename src/uu/dd/test-resources/{18d99661a1de1fc9af21b0ec2cd67ba3-deadbeef.test => deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test} (100%) create mode 100644 src/uu/dd/test-resources/gnudd-conv-atoe-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-atoibm-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-etoa-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-ltou-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-utol-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/lcase-ascii.test rename src/uu/dd/test-resources/{6ae59e64850377ee5470c854761551ea-ones.test => ones-6ae59e64850377ee5470c854761551ea.test} (100%) rename src/uu/dd/test-resources/{5828891cb1230748e146f34223bbd3b5-random.test => random-5828891cb1230748e146f34223bbd3b5.test} (100%) create mode 100644 src/uu/dd/test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test create mode 100644 src/uu/dd/test-resources/seq-byte-values-swapped.test create mode 100644 src/uu/dd/test-resources/ucase-ascii.test rename src/uu/dd/test-resources/{620f0b67a91f7f74151bc5be745b7110-zeros.test => zeros-620f0b67a91f7f74151bc5be745b7110.test} (100%) diff --git a/Cargo.lock b/Cargo.lock index fe0440d73..3da5e17fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,14 @@ dependencies = [ "generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bstr" version = "0.2.15" @@ -504,6 +512,14 @@ dependencies = [ "generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dunce" version = "1.0.1" @@ -563,6 +579,15 @@ dependencies = [ "typenum 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "getopts" version = "0.2.21" @@ -581,6 +606,16 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.10.2+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "glob" version = "0.2.11" @@ -609,6 +644,11 @@ name = "hex" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hex-literal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "hostname" version = "0.3.1" @@ -703,6 +743,16 @@ name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "md5" version = "0.3.8" @@ -818,6 +868,11 @@ name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "paste" version = "0.1.18" @@ -946,6 +1001,17 @@ dependencies = [ "rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -955,6 +1021,15 @@ dependencies = [ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -976,6 +1051,14 @@ dependencies = [ "getrandom 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -984,6 +1067,14 @@ dependencies = [ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_pcg" version = "0.2.1" @@ -1502,6 +1593,9 @@ dependencies = [ name = "uu_dd" version = "0.0.4" dependencies = [ + "hex-literal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "md-5 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.7", "uucore_procs 0.0.5", ] @@ -2359,6 +2453,11 @@ name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -2379,6 +2478,11 @@ name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "wasm-bindgen" version = "0.2.71" @@ -2503,6 +2607,7 @@ dependencies = [ "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" +"checksum block-buffer 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" "checksum bstr 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" "checksum bumpalo 3.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" @@ -2535,6 +2640,7 @@ dependencies = [ "checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" "checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" "checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" +"checksum digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" "checksum dunce 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b2641c4a7c0c4101df53ea572bffdc561c146f6c2eb09e4df02bc4811e3feeb4" "checksum either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" @@ -2543,14 +2649,17 @@ dependencies = [ "checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" "checksum fs_extra 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum generic-array 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" "checksum generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2297fb0e3ea512e380da24b52dca3924028f59df5e3a17a18f81d8349ca7ebe" "checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" "checksum getrandom 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +"checksum getrandom 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum half 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" "checksum hermit-abi 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" "checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" +"checksum hex-literal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" "checksum hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" "checksum if_rust_version 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46dbcb333e86939721589d25a3557e180b52778cb33c7fdfe9e0158ff790d5ec" "checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a" @@ -2565,6 +2674,7 @@ dependencies = [ "checksum log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" "checksum match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum md-5 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" "checksum md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "79c56d6a0b07f9e19282511c83fc5b086364cbae4ba8c7d5f190c3d9b0425a48" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" @@ -2580,6 +2690,7 @@ dependencies = [ "checksum onig 4.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8518fcb2b1b8c2f45f0ad499df4fda6087fc3475ca69a185c173b8315d2fb383" "checksum onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388410bf5fa341f10e58e6db3975f4bea1ac30247dd79d37a9e5ced3cb4cc3b0" "checksum oorandom 11.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +"checksum opaque-debug 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" "checksum paste 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" "checksum paste-impl 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" "checksum pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" @@ -2596,11 +2707,15 @@ dependencies = [ "checksum quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" "checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +"checksum rand_chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_hc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" "checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" "checksum rayon 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" "checksum rayon-core 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" @@ -2648,8 +2763,10 @@ dependencies = [ "checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" "checksum users 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" "checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +"checksum version_check 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +"checksum wasi 0.10.2+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum wasm-bindgen 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" "checksum wasm-bindgen-backend 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index dac27d833..f91f0c045 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -21,6 +21,7 @@ uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_p [dev-dependencies] md-5 = "0.9" hex-literal = "0.3" +rand = "0.8" [[bin]] name = "dd" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index ed9e0508f..faed1a9d8 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -21,20 +21,113 @@ use std::thread; const NAME: &str = "dd"; const SUMMARY: &str = "Copies, and optionally converts, file system resources."; -const LONG_HELP: &str = " -TODO: This is where the long help string for dd goes! -"; +const LONG_HELP: &str = "TODO: This is where the long help string for dd goes!"; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; // Conversion tables are just lookup tables. -// eg. -// The ASCII->EBDIC table stores the EBDIC code at the index +// eg. The ASCII->EBCDIC table stores the EBCDIC code at the index // obtained by treating the ASCII representation as a number. -// This idea is from the original GNU implementation. -type ConversionTable = [u8; u8::MAX as usize]; +type ConversionTable = [u8; 256]; +const ascii_to_ebcdic: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +const ascii_to_ibm: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +const ebcdic_to_ascii: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, + 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, + 0x20, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xd5, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, + 0x26, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x7e, + 0x2d, 0x2f, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xcb, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, + 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, + 0xc3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x5e, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xe5, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0x5b, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x5d, 0xe6, 0xe7, + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, + 0x5c, 0x9f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +const lcase_to_ucase: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + +]; + +const ucase_to_lcase: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +// ----- Datatypes ----- enum SrcStat { Read(usize), @@ -45,6 +138,7 @@ struct Input { src: R, ibs: usize, + output_progress: bool, } impl Read for Input @@ -115,6 +209,7 @@ impl Write for Output } } +// ----- Implementation ----- fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { move || { // LAAAAMBDA! @@ -140,8 +235,17 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () fn dd(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> { - let (prog_tx, prog_rx) = mpsc::channel(); - thread::spawn(gen_prog_updater(prog_rx)); + let prog_tx = if i.output_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; @@ -165,7 +269,10 @@ fn dd(mut i: Input, mut o: Output) -> Result<(usize, us bytes_out += w_len; - prog_tx.send(bytes_out)?; + if let Some(prog_tx) = &prog_tx + { + prog_tx.send(bytes_out)?; + } } Ok((bytes_in, bytes_out)) @@ -190,6 +297,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let i = Input { src: in_f, ibs, + output_progress: false, }; let o = Output { dst: out_f, @@ -221,18 +329,18 @@ mod test_dd_internal use md5::{ Md5, Digest, }; use hex_literal::hex; - macro_rules! make_test ( + macro_rules! make_hash_test ( ( $test_id:ident, $test_name:expr, $src:expr, $exp:expr ) => { #[test] fn $test_id() { - // let test_name = "6ae59e64850377ee5470c854761551ea-ones"; let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); let i = Input { src: $src, ibs: 256, + output_progress: false, }; let o = Output { @@ -258,44 +366,260 @@ mod test_dd_internal assert_eq!(hex!($exp), res[..]); + fs::remove_file(&tmp_fname).unwrap(); + } + }; + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $exp:expr ) => + { + #[test] + fn $test_id() + { + let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); + + let o = Output { + dst: File::create(&tmp_fname).unwrap(), + obs: $o.obs, + conv_table: $o.conv_table, + }; + + dd($i,o).unwrap(); + + let res = { + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let mut h = Md5::new(); + for b in res.bytes() + { + h.update([b.unwrap()]); + } + + h.finalize() + }; + + assert_eq!(hex!($exp), res[..]); + fs::remove_file(&tmp_fname).unwrap(); } }; ); - make_test!( + macro_rules! make_spec_test ( + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr ) => + { + #[test] + fn $test_id() + { + let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); + + let o = Output { + dst: File::create(&tmp_fname).unwrap(), + obs: $o.obs, + conv_table: $o.conv_table, + }; + + dd($i,o).unwrap(); + + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let spec = BufReader::new($spec); + + for (b_res, b_spec) in res.bytes().zip(spec.bytes()) + { + assert_eq!(b_res.unwrap(), + b_spec.unwrap()); + } + + fs::remove_file(&tmp_fname).unwrap(); + } + }; + ); + + make_hash_test!( empty_file_test, "stdio-empty-file", io::empty(), "d41d8cd98f00b204e9800998ecf8427e" ); - make_test!( + make_hash_test!( zeros_4k_test, "zeros-4k", - File::open("./test-resources/620f0b67a91f7f74151bc5be745b7110-zeros.test").unwrap(), + File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(), "620f0b67a91f7f74151bc5be745b7110" ); - make_test!( + make_hash_test!( ones_4k_test, "ones-4k", - File::open("./test-resources/6ae59e64850377ee5470c854761551ea-ones.test").unwrap(), + File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap(), "6ae59e64850377ee5470c854761551ea" ); - make_test!( + make_hash_test!( deadbeef_32k_test, - "deadbeef_32k", - File::open("./test-resources/18d99661a1de1fc9af21b0ec2cd67ba3-deadbeef.test").unwrap(), + "deadbeef-32k", + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), "18d99661a1de1fc9af21b0ec2cd67ba3" ); - make_test!( + make_hash_test!( random_73k_test, - "random_73k", - File::open("./test-resources/5828891cb1230748e146f34223bbd3b5-random.test").unwrap(), + "random-73k", + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), "5828891cb1230748e146f34223bbd3b5" ); + make_spec_test!( + atoe_conv_spec_test, + "atoe-conv-spec-test", + Input { + src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(ascii_to_ebcdic), + }, + File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() + ); + + make_spec_test!( + etoa_conv_spec_test, + "etoa-conv-spec-test", + Input { + src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(ebcdic_to_ascii), + }, + File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() + ); + + make_spec_test!( + atoibm_conv_spec_test, + "atoibm-conv-spec-test", + Input { + src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(ascii_to_ibm), + }, + File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() + ); + + make_spec_test!( + lcase_ascii_to_ucase_ascii, + "lcase_ascii_to_ucase_ascii", + Input { + src: File::open("./test-resources/lcase-ascii.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(lcase_to_ucase), + }, + File::open("./test-resources/ucase-ascii.test").unwrap() + ); + + make_spec_test!( + ucase_ascii_to_lcase_ascii, + "ucase_ascii_to_lcase_ascii", + Input { + src: File::open("./test-resources/ucase-ascii.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(ucase_to_lcase), + }, + File::open("./test-resources/lcase-ascii.test").unwrap() + ); + + #[test] + fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() + { + // ASCII->EBCDIC + let test_name = "all-valid-ascii-to-ebcdic"; + let tmp_fname_ae = format!("./test-resources/FAILED-{}.test", test_name); + + let i = Input { + src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), + ibs: 256, + output_progress: false, + }; + + let o = Output { + dst: File::create(&tmp_fname_ae).unwrap(), + obs: 1024, + conv_table: Some(ascii_to_ebcdic), + }; + + dd(i,o).unwrap(); + + // EBCDIC->ASCII + let test_name = "all-valid-ebcdic-to-ascii"; + let tmp_fname_ea = format!("./test-resources/FAILED-{}.test", test_name); + + let i = Input { + src: File::open(&tmp_fname_ae).unwrap(), + ibs: 256, + output_progress: false, + }; + + let o = Output { + dst: File::create(&tmp_fname_ea).unwrap(), + obs: 1024, + conv_table: Some(ebcdic_to_ascii), + }; + + dd(i,o).unwrap(); + + let res = { + let res = File::open(&tmp_fname_ea).unwrap(); + let res = BufReader::new(res); + + let mut h = Md5::new(); + for b in res.bytes() + { + h.update([b.unwrap()]); + } + + h.finalize() + }; + + assert_eq!(hex!("37eff01866ba3f538421b30b7cbefcac"), res[..]); + + fs::remove_file(&tmp_fname_ae).unwrap(); + fs::remove_file(&tmp_fname_ea).unwrap(); + } + + //use rand::prelude::*; + //#[test] + //fn make_test_data() + //{ + // let mut f = File::create("./test-resources/random-walk-through-the-ascii-ranged-forest.test").unwrap(); + // // let mut rng = rand::thread_rng(); + + // for _ in 0..65536 { + // f.write(&[c]).unwrap(); + // } + //} + + } diff --git a/src/uu/dd/test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test b/src/uu/dd/test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test new file mode 100644 index 0000000000000000000000000000000000000000..7e1cc9de8f128c86383ef196743f3133260c16fc GIT binary patch literal 128 zcmZQzWMXDvWn<^yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13l|!?3wfDZr{3l=l;Fd Ruin0S|L*s%zkmM!0{}7Ge}Mo1 literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/gnudd-conv-atoibm-seq-byte-values.spec b/src/uu/dd/test-resources/gnudd-conv-atoibm-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..3835d57cfb07e4c9021a7b8e0e2447a212c3032b GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=F{MD=0u35isgD!SFT^XdE@r2yLay2d-&k-qu3|UpE*`L zJGr{JyLo!NeDV6#+c#ddKHh%50scXO@85m=@cGmHn2_MG(1`G;$gf|%fBX5v(7@Qp R)WqD((&G28zkmM!0{|_%e}Mo1 literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/gnudd-conv-ltou-seq-byte-values.spec b/src/uu/dd/test-resources/gnudd-conv-ltou-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..5aacec0cd0d7dbc96c65f8cf65055b769c09a3d1 GIT binary patch literal 256 zcmZQ%U}j=vVQ1sy;O64x;pY<+5Ec>@5f_t`kd~5_k(X0cP*ze^QCHK{(ALt`(bqFH zFg7wZF*mccu(qcc@%IZ12o4Gj2@i{mh>nVliH~zkz$ITD zRa;YE*Vxe9)Y{VC*4feB)!WnGH*vz`NmHjxpEh&G>{)Z?%%8V#!Qw?rmn>hla>eRZ zYuBt_w{gSfO@5f_t`kd~5_k(X0cP*ze^QCHK{(ALt`(bqFH zFg7wZF*mccu(q{)Z?%%8V#!Qw?rmn>hla>eRZ zYuBt_w{gSfOMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6fOiE5kO-s+n%*xKm&C4$+EGjN3Ei136tcs3_jf+pfEni(z ITUXBo03Agm3jhEB literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/6ae59e64850377ee5470c854761551ea-ones.test b/src/uu/dd/test-resources/ones-6ae59e64850377ee5470c854761551ea.test similarity index 100% rename from src/uu/dd/test-resources/6ae59e64850377ee5470c854761551ea-ones.test rename to src/uu/dd/test-resources/ones-6ae59e64850377ee5470c854761551ea.test diff --git a/src/uu/dd/test-resources/5828891cb1230748e146f34223bbd3b5-random.test b/src/uu/dd/test-resources/random-5828891cb1230748e146f34223bbd3b5.test similarity index 100% rename from src/uu/dd/test-resources/5828891cb1230748e146f34223bbd3b5-random.test rename to src/uu/dd/test-resources/random-5828891cb1230748e146f34223bbd3b5.test diff --git a/src/uu/dd/test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test b/src/uu/dd/test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test new file mode 100644 index 0000000000000000000000000000000000000000..be75933c8be3ad3965a405ea2d0ccb625110f055 GIT binary patch literal 256 zcmZQ%U}j=vVQ1sy;O64x;pY<+5Ec>@5f_t`kd~5_k(X0cP*ze^QCHK{(ALt`(bqFH zFg7wZF*mccu(qcc@%IZ12o4Gj2@i{mh>nVliH}Q6NKQ&k zNl(kn$j-{m$aT7=F=93Ve+J@Q>IUw zIb-&$xpU^vTex8HqNPigFI%}{^{TaN*00;RVe_V~Teff8xnuXPy?gfWJ9yymp`%BR zA3J&C^r^FF&Y!z@;qs-cSFT^XdE@r2yLay2d-&k-qo+@vKYRJ&^{cmU-oN|!;q#}j RU%r3)`Q!JmzkmM!0|2gee}Mo1 literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/seq-byte-values-swapped.test b/src/uu/dd/test-resources/seq-byte-values-swapped.test new file mode 100644 index 0000000000000000000000000000000000000000..c86626638e0bc8cf47ca49bb1525b40e9737ee64 GIT binary patch literal 256 zcmZQzWMXDvWn<^yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v`MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13 Date: Sat, 3 Apr 2021 12:59:03 -0700 Subject: [PATCH 05/66] Builds out arg parsing. Adds support for if, of, & multiplier strings) - Adds support for calling dd fn from cl - Adds basic cl tests from project root - Adds support for multiplier strings (c, w, b, kB, KB, KiB, ... EB, E, EiB. --- Cargo.lock | 1 + src/uu/dd/Cargo.toml | 2 + src/uu/dd/src/dd.rs | 539 +++++++++++------------------- src/uu/dd/src/ddargs.rs | 303 +++++++++++++++++ src/uu/dd/src/test_dd_internal.rs | 329 ++++++++++++++++++ tests/by-util/test_dd.rs | 58 +++- 6 files changed, 891 insertions(+), 341 deletions(-) create mode 100644 src/uu/dd/src/ddargs.rs create mode 100644 src/uu/dd/src/test_dd_internal.rs diff --git a/Cargo.lock b/Cargo.lock index 3da5e17fa..3cb823561 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1593,6 +1593,7 @@ dependencies = [ name = "uu_dd" version = "0.0.4" dependencies = [ + "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "md-5 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index f91f0c045..6c2263fdb 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -17,6 +17,8 @@ path = "src/dd.rs" [dependencies] uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } +# Probably best to keep this identical to the version of getopts in the uucore crate +getopts = "<= 0.2.21" [dev-dependencies] md-5 = "0.9" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index faed1a9d8..9fb896e2e 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -10,28 +10,35 @@ #[macro_use] extern crate uucore; +#[cfg(test)] +mod test_dd_internal; + +mod ddargs; + use std::error::Error; use std::fs::File; use std::io::{ self, Read, Write, - BufWriter, }; use std::sync::mpsc; use std::thread; +use getopts; const NAME: &str = "dd"; -const SUMMARY: &str = "Copies, and optionally converts, file system resources."; +const SUMMARY: &str = "convert and copy a file"; const LONG_HELP: &str = "TODO: This is where the long help string for dd goes!"; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; +// ----- Conversion ----- +// // Conversion tables are just lookup tables. // eg. The ASCII->EBCDIC table stores the EBCDIC code at the index // obtained by treating the ASCII representation as a number. type ConversionTable = [u8; 256]; -const ascii_to_ebcdic: ConversionTable = [ +const ASCII_TO_EBCDIC: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, @@ -50,7 +57,7 @@ const ascii_to_ebcdic: ConversionTable = [ 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; -const ascii_to_ibm: ConversionTable = [ +const ASCII_TO_IBM: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, @@ -69,7 +76,7 @@ const ascii_to_ibm: ConversionTable = [ 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; -const ebcdic_to_ascii: ConversionTable = [ +const EBCDIC_TO_ASCII: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, @@ -88,7 +95,7 @@ const ebcdic_to_ascii: ConversionTable = [ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; -const lcase_to_ucase: ConversionTable = [ +const LCASE_TO_UCASE: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, @@ -108,7 +115,7 @@ const lcase_to_ucase: ConversionTable = [ ]; -const ucase_to_lcase: ConversionTable = [ +const UCASE_TO_LCASE: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, @@ -127,13 +134,29 @@ const ucase_to_lcase: ConversionTable = [ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; -// ----- Datatypes ----- +// ----- Types ----- enum SrcStat { Read(usize), EOF, } +#[derive(Debug)] +enum InternalError +{ + WrongInputType, + WrongOutputType, +} + +impl std::fmt::Display for InternalError +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Internal dd error") + } +} + +impl Error for InternalError {} + struct Input { src: R, @@ -149,6 +172,47 @@ impl Read for Input } } +impl Input +{ + fn new(matches: &getopts::Matches) -> Result> + { + let ibs: usize = ddargs::parse_ibs(matches)?; + let output_progress = ddargs::parse_progress_level(matches)?; + + Ok( + Input { + src: io::stdin(), + ibs, + output_progress, + } + ) + + } +} + +impl Input +{ + fn new(matches: &getopts::Matches) -> Result> + { + let ibs: usize = ddargs::parse_ibs(matches)?; + let output_progress = ddargs::parse_progress_level(matches)?; + + if let Some(fname) = matches.opt_str("if") + { + Ok(Input { + src: File::open(fname)?, + ibs, + output_progress, + }) + } + else + { + Err(Box::new(InternalError::WrongInputType)) + } + + } +} + impl Input { fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result> @@ -182,6 +246,43 @@ struct Output conv_table: Option, } +impl Output { + fn new(matches: &getopts::Matches) -> Result> + { + let obs: usize = ddargs::parse_obs(matches)?; + let conv_table = ddargs::parse_conv_table(matches)?; + + Ok( + Output { + dst: io::stdout(), + obs, + conv_table, + } + ) + } +} + +impl Output { + fn new(matches: &getopts::Matches) -> Result> + { + let obs: usize = ddargs::parse_obs(matches)?; + let conv_table = ddargs::parse_conv_table(matches)?; + + if let Some(fname) = matches.opt_str("if") + { + Ok(Output { + dst: File::open(fname)?, + obs, + conv_table, + }) + } + else + { + Err(Box::new(InternalError::WrongOutputType)) + } + } +} + impl Write for Output { fn write(&mut self, buf: &[u8]) -> io::Result @@ -209,10 +310,9 @@ impl Write for Output } } -// ----- Implementation ----- fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { - move || { // LAAAAMBDA! + move || { // TODO: Replace ?? with accurate info print!("\rProgress ({}/??)", 0); @@ -246,7 +346,6 @@ fn dd(mut i: Input, mut o: Output) -> Result<(usize, us None }; - let mut bytes_in = 0; let mut bytes_out = 0; @@ -278,38 +377,106 @@ fn dd(mut i: Input, mut o: Output) -> Result<(usize, us Ok((bytes_in, bytes_out)) } +fn append_dashes_if_not_present(mut acc: Vec, s: &String) -> Vec +{ + if Some("--") == s.get(0..=1) { + acc + } else { + acc.push(format!("--{}", s)); + acc + } +} + pub fn uumain(args: impl uucore::Args) -> i32 { - // TODO: parse args + let dashed_args = args.collect_str() + .iter() + .fold(Vec::new(), append_dashes_if_not_present); - let if_name = "foo.txt"; - let of_name = "bar.txt"; - let ibs = 512; - let obs = 4096; + let syntax = format!( + "{0} [OPERAND]...\n{0} OPTION", + NAME + ); - let in_f = File::open(if_name) - .expect("TODO: Handle this error in the project-specific way"); + let matches = app!(&syntax, SUMMARY, LONG_HELP) + .optopt( + "", + "if", + "The input file", + "FILE" + ) + .optopt( + "", + "ibs", + "read up to BYTES bytes at a time (default: 512)", + "BYTES" + ) + .optopt( + "", + "of", + "The output file", + "FILE" + ) + .optopt( + "", + "obs", + "write BYTES bytes at a time (default: 512)", + "BYTES" + ) + .optopt( + "", + "conv", + "One or more conversion options as a comma-serparated list", + "OPT[,OPT]..." + ) + .parse(dashed_args); - let out_f = File::open(of_name) - .expect("TODO: Handle this error in the project-specific way"); - let out_f = BufWriter::with_capacity(obs, out_f); + let result = match (matches.opt_present("if"), matches.opt_present("of")) + { + (true, true) => + { + let i = Input::::new(&matches) + .expect("TODO: Return correct error code"); + let o = Output::::new(&matches) + .expect("TODO: Return correct error code"); - let i = Input { - src: in_f, - ibs, - output_progress: false, - }; - let o = Output { - dst: out_f, - obs, - conv_table: None, + dd(i, o) + }, + (true, false) => + { + let i = Input::::new(&matches) + .expect("TODO: Return correct error code"); + let o = Output::::new(&matches) + .expect("TODO: Return correct error code"); + + dd(i, o) + }, + (false, true) => + { + let i = Input::::new(&matches) + .expect("TODO: Return correct error code"); + let o = Output::::new(&matches) + .expect("TODO: Return correct error code"); + + dd(i, o) + }, + (false, false) => + { + let i = Input::::new(&matches) + .expect("TODO: Return correct error code"); + let o = Output::::new(&matches) + .expect("TODO: Return correct error code"); + + dd(i, o) + }, }; - match dd(i, o) { + match result + { Ok((b_in, b_out)) => { - println!("Completed: Bytes in: {}, Bytes out: {}", b_in, b_out); - + // TODO: Print output stats, unless noxfer + RTN_SUCCESS }, Err(_) => @@ -317,309 +484,3 @@ pub fn uumain(args: impl uucore::Args) -> i32 } } -#[cfg(test)] -mod test_dd_internal -{ - #[allow(unused_imports)] - use super::*; - - use std::io::prelude::*; - use std::io::BufReader; - use std::fs; - use md5::{ Md5, Digest, }; - use hex_literal::hex; - - macro_rules! make_hash_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $exp:expr ) => - { - #[test] - fn $test_id() - { - let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); - - let i = Input { - src: $src, - ibs: 256, - output_progress: false, - }; - - let o = Output { - dst: File::create(&tmp_fname).unwrap(), - obs: 1024, - conv_table: None, - }; - - dd(i,o).unwrap(); - - let res = { - let res = File::open(&tmp_fname).unwrap(); - let res = BufReader::new(res); - - let mut h = Md5::new(); - for b in res.bytes() - { - h.update([b.unwrap()]); - } - - h.finalize() - }; - - assert_eq!(hex!($exp), res[..]); - - fs::remove_file(&tmp_fname).unwrap(); - } - }; - ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $exp:expr ) => - { - #[test] - fn $test_id() - { - let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); - - let o = Output { - dst: File::create(&tmp_fname).unwrap(), - obs: $o.obs, - conv_table: $o.conv_table, - }; - - dd($i,o).unwrap(); - - let res = { - let res = File::open(&tmp_fname).unwrap(); - let res = BufReader::new(res); - - let mut h = Md5::new(); - for b in res.bytes() - { - h.update([b.unwrap()]); - } - - h.finalize() - }; - - assert_eq!(hex!($exp), res[..]); - - fs::remove_file(&tmp_fname).unwrap(); - } - }; - ); - - macro_rules! make_spec_test ( - ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr ) => - { - #[test] - fn $test_id() - { - let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); - - let o = Output { - dst: File::create(&tmp_fname).unwrap(), - obs: $o.obs, - conv_table: $o.conv_table, - }; - - dd($i,o).unwrap(); - - let res = File::open(&tmp_fname).unwrap(); - let res = BufReader::new(res); - - let spec = BufReader::new($spec); - - for (b_res, b_spec) in res.bytes().zip(spec.bytes()) - { - assert_eq!(b_res.unwrap(), - b_spec.unwrap()); - } - - fs::remove_file(&tmp_fname).unwrap(); - } - }; - ); - - make_hash_test!( - empty_file_test, - "stdio-empty-file", - io::empty(), - "d41d8cd98f00b204e9800998ecf8427e" - ); - - make_hash_test!( - zeros_4k_test, - "zeros-4k", - File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(), - "620f0b67a91f7f74151bc5be745b7110" - ); - - make_hash_test!( - ones_4k_test, - "ones-4k", - File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap(), - "6ae59e64850377ee5470c854761551ea" - ); - - make_hash_test!( - deadbeef_32k_test, - "deadbeef-32k", - File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), - "18d99661a1de1fc9af21b0ec2cd67ba3" - ); - - make_hash_test!( - random_73k_test, - "random-73k", - File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), - "5828891cb1230748e146f34223bbd3b5" - ); - - make_spec_test!( - atoe_conv_spec_test, - "atoe-conv-spec-test", - Input { - src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - ibs: 512, - output_progress: false, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - conv_table: Some(ascii_to_ebcdic), - }, - File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() - ); - - make_spec_test!( - etoa_conv_spec_test, - "etoa-conv-spec-test", - Input { - src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - ibs: 512, - output_progress: false, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - conv_table: Some(ebcdic_to_ascii), - }, - File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() - ); - - make_spec_test!( - atoibm_conv_spec_test, - "atoibm-conv-spec-test", - Input { - src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - ibs: 512, - output_progress: false, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - conv_table: Some(ascii_to_ibm), - }, - File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() - ); - - make_spec_test!( - lcase_ascii_to_ucase_ascii, - "lcase_ascii_to_ucase_ascii", - Input { - src: File::open("./test-resources/lcase-ascii.test").unwrap(), - ibs: 512, - output_progress: false, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - conv_table: Some(lcase_to_ucase), - }, - File::open("./test-resources/ucase-ascii.test").unwrap() - ); - - make_spec_test!( - ucase_ascii_to_lcase_ascii, - "ucase_ascii_to_lcase_ascii", - Input { - src: File::open("./test-resources/ucase-ascii.test").unwrap(), - ibs: 512, - output_progress: false, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - conv_table: Some(ucase_to_lcase), - }, - File::open("./test-resources/lcase-ascii.test").unwrap() - ); - - #[test] - fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() - { - // ASCII->EBCDIC - let test_name = "all-valid-ascii-to-ebcdic"; - let tmp_fname_ae = format!("./test-resources/FAILED-{}.test", test_name); - - let i = Input { - src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), - ibs: 256, - output_progress: false, - }; - - let o = Output { - dst: File::create(&tmp_fname_ae).unwrap(), - obs: 1024, - conv_table: Some(ascii_to_ebcdic), - }; - - dd(i,o).unwrap(); - - // EBCDIC->ASCII - let test_name = "all-valid-ebcdic-to-ascii"; - let tmp_fname_ea = format!("./test-resources/FAILED-{}.test", test_name); - - let i = Input { - src: File::open(&tmp_fname_ae).unwrap(), - ibs: 256, - output_progress: false, - }; - - let o = Output { - dst: File::create(&tmp_fname_ea).unwrap(), - obs: 1024, - conv_table: Some(ebcdic_to_ascii), - }; - - dd(i,o).unwrap(); - - let res = { - let res = File::open(&tmp_fname_ea).unwrap(); - let res = BufReader::new(res); - - let mut h = Md5::new(); - for b in res.bytes() - { - h.update([b.unwrap()]); - } - - h.finalize() - }; - - assert_eq!(hex!("37eff01866ba3f538421b30b7cbefcac"), res[..]); - - fs::remove_file(&tmp_fname_ae).unwrap(); - fs::remove_file(&tmp_fname_ea).unwrap(); - } - - //use rand::prelude::*; - //#[test] - //fn make_test_data() - //{ - // let mut f = File::create("./test-resources/random-walk-through-the-ascii-ranged-forest.test").unwrap(); - // // let mut rng = rand::thread_rng(); - - // for _ in 0..65536 { - // f.write(&[c]).unwrap(); - // } - //} - - -} diff --git a/src/uu/dd/src/ddargs.rs b/src/uu/dd/src/ddargs.rs new file mode 100644 index 000000000..86805d213 --- /dev/null +++ b/src/uu/dd/src/ddargs.rs @@ -0,0 +1,303 @@ +use super::*; + +#[derive(Debug)] +enum ParseError +{ + MultiplierString(String), + MultiplierStringWouldOverflow(String), +} + +impl std::fmt::Display for ParseError +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "dd-args: Parse Error") + } +} + +impl Error for ParseError {} + +fn parse_multiplier<'a>(s: &'a str) -> Result> +{ + let s = s.trim(); + + match s + { + "c" => + Ok(1), + "w" => + Ok(2), + "b" => + Ok(512), + "kB" => + Ok(1000), + "K" | "KiB" => + Ok(1024), + "MB" => + Ok(1000*1000), + "M" | "MiB" => + Ok(1024*1024), + "GB" => + Ok(1000*1000*1000), + "G" | "GiB" => + Ok(1024*1024*1024), + "TB" => + Ok(1000*1000*1000*1000), + "T" | "TiB" => + Ok(1024*1024*1024*1024), + "PB" => + Ok(1000*1000*1000*1000*1000), + "P" | "PiB" => + Ok(1024*1024*1024*1024*1024), + "EB" => + Ok(1000*1000*1000*1000*1000*1000), + "E" | "EiB" => + Ok(1024*1024*1024*1024*1024*1024), +// The following would overflow on my x64 system +// "ZB" => +// Ok(1000*1000*1000*1000*1000*1000*1000), +// "Z" | "ZiB" => +// Ok(1024*1024*1024*1024*1024*1024*1024), +// "YB" => +// Ok(1000*1000*1000*1000*1000*1000*1000*1000), +// "Y" | "YiB" => +// Ok(1024*1024*1024*1024*1024*1024*1024*1024), + _ => + Err(Box::new(ParseError::MultiplierString(String::from(s)))), + } +} + +fn parse_bytes_with_opt_multiplier(s: String) -> Result> +{ + if let Some(idx) = s.find(' ') + { + let base: usize = s[0..idx].parse()?; + let mult = parse_multiplier(&s[idx..])?; + + if let Some(bytes) = base.checked_mul(mult) + { + Ok(bytes) + } + else + { + Err(Box::new(ParseError::MultiplierStringWouldOverflow(s))) + } + } + else + { + let bytes: usize = s.parse()?; + + Ok(bytes) + } +} + +pub fn parse_ibs(matches: &getopts::Matches) -> Result> +{ + if let Some(mixed_str) = matches.opt_str("bs") + { + parse_bytes_with_opt_multiplier(mixed_str) + } + else if let Some(mixed_str) = matches.opt_str("ibs") + { + parse_bytes_with_opt_multiplier(mixed_str) + } + else + { + Ok(512) + } +} + +pub fn parse_progress_level(matches: &getopts::Matches) -> Result> +{ + // TODO: Implement this stub proc + Ok(false) +} + +pub fn parse_obs(matches: &getopts::Matches) -> Result> +{ + if let Some(str_with_prefixes) = matches.opt_str("bs") + { + // TODO: Parse a string containing the number with potential k, kB, kiB, ... multiplier + // possibly implemented elsewhere, but probably not in exactly the dd style + panic!() + } + else if let Some(str_with_prefixes) = matches.opt_str("obs") + { + // TODO: Parse a string containing the number with potential k, kB, kiB, ... multiplier + // possibly implemented elsewhere, but probably not in exactly the dd style + panic!() + } + else + { + Ok(512) + } +} + +pub fn parse_conv_table(matches: &getopts::Matches) -> Result, Box> +{ + // TODO: Complete this stub fn + Ok(None) +} + +#[cfg(test)] +mod test { + use super::*; + + macro_rules! test_byte_parser ( + ( $test_name:ident, $bs_str:expr, $bs:expr ) => + { + #[test] + fn $test_name() + { + let bs_str = String::from($bs_str); + assert_eq!($bs, parse_bytes_with_opt_multiplier(bs_str).unwrap()) + } + } + ); + + #[test] + fn test_input_parser() + { + panic!() + } + + #[test] + fn test_output_parser() + { + panic!() + } + + #[test] + fn test_conv_options_parser() + { + panic!() + } + + test_byte_parser!( + test_bytes_n, + "765", + 765 + ); + test_byte_parser!( + test_bytes_c, + "13 c", + 13 + ); + + test_byte_parser!( + test_bytes_w, + "1 w", + 2 + ); + + test_byte_parser!( + test_bytes_b, + "1 b", + 512 + ); + + test_byte_parser!( + test_bytes_k, + "1 kB", + 1000 + ); + test_byte_parser!( + test_bytes_K, + "1 K", + 1024 + ); + test_byte_parser!( + test_bytes_Ki, + "1 KiB", + 1024 + ); + + test_byte_parser!( + test_bytes_MB, + "1 MB", + 1000*1000 + ); + test_byte_parser!( + test_bytes_M, + "1 M", + 1024*1024 + ); + test_byte_parser!( + test_bytes_Mi, + "1 MiB", + 1024*1024 + ); + + test_byte_parser!( + test_bytes_GB, + "1 GB", + 1000*1000*1000 + ); + test_byte_parser!( + test_bytes_G, + "1 G", + 1024*1024*1024 + ); + test_byte_parser!( + test_bytes_Gi, + "1 GiB", + 1024*1024*1024 + ); + + test_byte_parser!( + test_bytes_TB, + "1 TB", + 1000*1000*1000*1000 + ); + test_byte_parser!( + test_bytes_T, + "1 T", + 1024*1024*1024*1024 + ); + test_byte_parser!( + test_bytes_Ti, + "1 TiB", + 1024*1024*1024*1024 + ); + + test_byte_parser!( + test_bytes_PB, + "1 PB", + 1000*1000*1000*1000*1000 + ); + test_byte_parser!( + test_bytes_P, + "1 P", + 1024*1024*1024*1024*1024 + ); + test_byte_parser!( + test_bytes_Pi, + "1 PiB", + 1024*1024*1024*1024*1024 + ); + + test_byte_parser!( + test_bytes_EB, + "1 EB", + 1000*1000*1000*1000*1000*1000 + ); + test_byte_parser!( + test_bytes_E, + "1 E", + 1024*1024*1024*1024*1024*1024 + ); + test_byte_parser!( + test_bytes_Ei, + "1 EiB", + 1024*1024*1024*1024*1024*1024 + ); + + #[test] + #[should_panic] + fn test_KB_multiplier_error() + { + let bs_str = String::from("2000 KB"); + + parse_bytes_with_opt_multiplier(bs_str).unwrap(); + } + +} diff --git a/src/uu/dd/src/test_dd_internal.rs b/src/uu/dd/src/test_dd_internal.rs new file mode 100644 index 000000000..c171e5b36 --- /dev/null +++ b/src/uu/dd/src/test_dd_internal.rs @@ -0,0 +1,329 @@ +use super::*; + +use std::io::prelude::*; +use std::io::BufReader; +use std::fs; +use md5::{ Md5, Digest, }; +use hex_literal::hex; +// use tempfile::tempfile; +// TODO: (Maybe) Use tempfiles in the tests. + +macro_rules! make_hash_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $exp:expr ) => + { + #[test] + fn $test_id() + { + let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); + + let i = Input { + src: $src, + ibs: 256, + output_progress: false, + }; + + let o = Output { + dst: File::create(&tmp_fname).unwrap(), + obs: 1024, + conv_table: None, + }; + + dd(i,o).unwrap(); + + let res = { + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let mut h = Md5::new(); + for b in res.bytes() + { + h.update([b.unwrap()]); + } + + h.finalize() + }; + + assert_eq!(hex!($exp), res[..]); + + fs::remove_file(&tmp_fname).unwrap(); + } + }; + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $exp:expr ) => + { + #[test] + fn $test_id() + { + let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); + + let o = Output { + dst: File::create(&tmp_fname).unwrap(), + obs: $o.obs, + conv_table: $o.conv_table, + }; + + dd($i,o).unwrap(); + + let res = { + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let mut h = Md5::new(); + for b in res.bytes() + { + h.update([b.unwrap()]); + } + + h.finalize() + }; + + assert_eq!(hex!($exp), res[..]); + + fs::remove_file(&tmp_fname).unwrap(); + } + }; +); + +macro_rules! make_spec_test ( + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr ) => + { + #[test] + fn $test_id() + { + let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); + + let o = Output { + dst: File::create(&tmp_fname).unwrap(), + obs: $o.obs, + conv_table: $o.conv_table, + }; + + dd($i,o).unwrap(); + + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let spec = BufReader::new($spec); + + for (b_res, b_spec) in res.bytes().zip(spec.bytes()) + { + assert_eq!(b_res.unwrap(), + b_spec.unwrap()); + } + + fs::remove_file(&tmp_fname).unwrap(); + } + }; +); + +make_hash_test!( + empty_file_test, + "stdio-empty-file", + io::empty(), + "d41d8cd98f00b204e9800998ecf8427e" +); + +make_hash_test!( + zeros_4k_test, + "zeros-4k", + File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(), + "620f0b67a91f7f74151bc5be745b7110" +); + +make_hash_test!( + ones_4k_test, + "ones-4k", + File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap(), + "6ae59e64850377ee5470c854761551ea" +); + +make_hash_test!( + deadbeef_32k_test, + "deadbeef-32k", + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), + "18d99661a1de1fc9af21b0ec2cd67ba3" +); + +make_hash_test!( + random_73k_test, + "random-73k", + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + "5828891cb1230748e146f34223bbd3b5" +); + +make_spec_test!( + atoe_conv_spec_test, + "atoe-conv-spec-test", + Input { + src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(ASCII_TO_EBCDIC), + }, + File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() +); + +make_spec_test!( + etoa_conv_spec_test, + "etoa-conv-spec-test", + Input { + src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(EBCDIC_TO_ASCII), + }, + File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() +); + +make_spec_test!( + atoibm_conv_spec_test, + "atoibm-conv-spec-test", + Input { + src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(ASCII_TO_IBM), + }, + File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() +); + +make_spec_test!( + lcase_ascii_to_ucase_ascii, + "lcase_ascii_to_ucase_ascii", + Input { + src: File::open("./test-resources/lcase-ascii.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(LCASE_TO_UCASE), + }, + File::open("./test-resources/ucase-ascii.test").unwrap() +); + +make_spec_test!( + ucase_ascii_to_lcase_ascii, + "ucase_ascii_to_lcase_ascii", + Input { + src: File::open("./test-resources/ucase-ascii.test").unwrap(), + ibs: 512, + output_progress: false, + }, + Output { + dst: Vec::new(), // unused! + obs: 512, + conv_table: Some(UCASE_TO_LCASE), + }, + File::open("./test-resources/lcase-ascii.test").unwrap() +); + +#[test] +fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() +{ + // ASCII->EBCDIC + let test_name = "all-valid-ascii-to-ebcdic"; + let tmp_fname_ae = format!("./test-resources/FAILED-{}.test", test_name); + + let i = Input { + src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), + ibs: 256, + output_progress: false, + }; + + let o = Output { + dst: File::create(&tmp_fname_ae).unwrap(), + obs: 1024, + conv_table: Some(ASCII_TO_EBCDIC), + }; + + dd(i,o).unwrap(); + + // EBCDIC->ASCII + let test_name = "all-valid-ebcdic-to-ascii"; + let tmp_fname_ea = format!("./test-resources/FAILED-{}.test", test_name); + + let i = Input { + src: File::open(&tmp_fname_ae).unwrap(), + ibs: 256, + output_progress: false, + }; + + let o = Output { + dst: File::create(&tmp_fname_ea).unwrap(), + obs: 1024, + conv_table: Some(EBCDIC_TO_ASCII), + }; + + dd(i,o).unwrap(); + + let res = { + let res = File::open(&tmp_fname_ea).unwrap(); + let res = BufReader::new(res); + + let mut h = Md5::new(); + for b in res.bytes() + { + h.update([b.unwrap()]); + } + + h.finalize() + }; + + assert_eq!(hex!("37eff01866ba3f538421b30b7cbefcac"), res[..]); + + fs::remove_file(&tmp_fname_ae).unwrap(); + fs::remove_file(&tmp_fname_ea).unwrap(); +} + +// #[test] +// fn copy_zerofile_from_args() +// { +// let spec = File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(); +// let tmp_fname = format!("./test-resources/{}", "zeros-from-args.test"); +// +// let args = vec![ +// String::from("if=./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test"), +// String::from(&tmp_fname), +// ]; +// let args = Args { args }; +// +// uumain(args); +// +// let res = File::open(&tmp_fname).unwrap(); +// let res = BufReader::new(res); +// +// let spec = BufReader::new(spec); +// +// for (b_res, b_spec) in res.bytes().zip(spec.bytes()) +// { +// assert_eq!(b_res.unwrap(), +// b_spec.unwrap()); +// } +// +// fs::remove_file(&tmp_fname).unwrap(); +// } + +//use rand::prelude::*; +//#[test] +//fn make_test_data() +//{ +// let mut f = File::create("./test-resources/random-walk-through-the-ascii-ranged-forest.test").unwrap(); +// // let mut rng = rand::thread_rng(); + +// for _ in 0..65536 { +// f.write(&[c]).unwrap(); +// } +//} diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 2a010b4e3..40ea7e430 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,7 +1,61 @@ use crate::common::util::*; +use std::io::prelude::*; +use std::io::BufReader; +use std::fs::{self, File}; + #[test] -fn fail_from_test_dd() +fn dd_zeros_to_stdout_test_from_args() { - panic!() + new_ucmd!() + .args(&[ + "if=../../src/uu/dd/test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test", + ]) + .succeeds() + .stdout_only( + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + " + ); +} + +#[test] +fn dd_ones_to_file_test_from_args() +{ + let tmp_fname = "../../src/uu/dd/test-resources/FAILED-ones-to-file-from-args.test"; + + new_ucmd!() + .args(&[ + "if=../../src/uu/dd/test-resources/ones-6ae59e64850377ee5470c854761551ea.test", + &format!("of={}", &tmp_fname), + ]) + .succeeds(); + + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let spec = File::open("../../src/uu/dd/test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap(); + let spec = BufReader::new(spec); + + for (b_res, b_spec) in res.bytes().zip(spec.bytes()) + { + assert_eq!(b_res.unwrap(), + b_spec.unwrap()); + } + + fs::remove_file(&tmp_fname).unwrap(); } From 5c8c7efe687ab0ff83bbb504b030d9febcd7fce6 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 6 Apr 2021 20:44:37 -0700 Subject: [PATCH 06/66] Starts arg parsing. Cleans & refactors. - Moves all arg parsing & tests to parseargs.rs - Moves conversion tables to conversion_tables.rs - Adds ebcdic_lcase and _ucase tables - Refactors options: This **Breaks Write for Output** --- src/uu/dd/src/conversion_tables.rs | 139 +++++++++ src/uu/dd/src/dd.rs | 257 ++++++--------- src/uu/dd/src/{ddargs.rs => parseargs.rs} | 198 +++++++++++- src/uu/dd/src/test_dd_internal.rs | 292 ++++++++++-------- ...nudd-conv-ebcdic-ltou-seq-byte-values.spec | Bin 0 -> 256 bytes ...nudd-conv-ebcdic-utol-seq-byte-values.spec | Bin 0 -> 256 bytes 6 files changed, 578 insertions(+), 308 deletions(-) create mode 100644 src/uu/dd/src/conversion_tables.rs rename src/uu/dd/src/{ddargs.rs => parseargs.rs} (56%) create mode 100644 src/uu/dd/test-resources/gnudd-conv-ebcdic-ltou-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-ebcdic-utol-seq-byte-values.spec diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs new file mode 100644 index 000000000..62c75febc --- /dev/null +++ b/src/uu/dd/src/conversion_tables.rs @@ -0,0 +1,139 @@ + +// Conversion tables are just lookup tables. +// eg. The ASCII->EBCDIC table stores the EBCDIC code at the index +// obtained by treating the ASCII representation as a number. +pub type ConversionTable = [u8; 256]; + +pub const ASCII_TO_EBCDIC: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_IBM: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const EBCDIC_TO_ASCII: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, + 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, + 0x20, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xd5, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, + 0x26, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x7e, + 0x2d, 0x2f, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xcb, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, + 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, + 0xc3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x5e, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xe5, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0x5b, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x5d, 0xe6, 0xe7, + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, + 0x5c, 0x9f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + +]; + +pub const ASCII_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const EBCDIC_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const EBCDIC_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 9fb896e2e..cd04ee85d 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -13,7 +13,11 @@ extern crate uucore; #[cfg(test)] mod test_dd_internal; -mod ddargs; +mod parseargs; +mod conversion_tables; + +use conversion_tables::*; +use parseargs::*; use std::error::Error; use std::fs::File; @@ -24,117 +28,14 @@ use std::sync::mpsc; use std::thread; use getopts; -const NAME: &str = "dd"; -const SUMMARY: &str = "convert and copy a file"; -const LONG_HELP: &str = "TODO: This is where the long help string for dd goes!"; +const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; +const SUMMARY: &str = "convert, and optionally copy, a file"; +const LONG_HELP: &str = ""; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; -// ----- Conversion ----- -// -// Conversion tables are just lookup tables. -// eg. The ASCII->EBCDIC table stores the EBCDIC code at the index -// obtained by treating the ASCII representation as a number. -type ConversionTable = [u8; 256]; - -const ASCII_TO_EBCDIC: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, - 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, - 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, - 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, - 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, - 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, - 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, - 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -const ASCII_TO_IBM: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, - 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, - 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, - 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, - 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, - 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, - 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, - 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -const EBCDIC_TO_ASCII: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, - 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, - 0x20, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xd5, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, - 0x26, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x7e, - 0x2d, 0x2f, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xcb, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, - 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, - 0xc3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, - 0xca, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x5e, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, - 0xd1, 0xe5, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0x5b, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x5d, 0xe6, 0xe7, - 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, - 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, - 0x5c, 0x9f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -const LCASE_TO_UCASE: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, - -]; - -const UCASE_TO_LCASE: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -// ----- Types ----- +// ----- Datatypes ----- enum SrcStat { Read(usize), @@ -161,7 +62,6 @@ struct Input { src: R, ibs: usize, - output_progress: bool, } impl Read for Input @@ -176,14 +76,12 @@ impl Input { fn new(matches: &getopts::Matches) -> Result> { - let ibs: usize = ddargs::parse_ibs(matches)?; - let output_progress = ddargs::parse_progress_level(matches)?; + let ibs: usize = parseargs::parse_ibs(matches)?; Ok( Input { src: io::stdin(), ibs, - output_progress, } ) @@ -194,15 +92,13 @@ impl Input { fn new(matches: &getopts::Matches) -> Result> { - let ibs: usize = ddargs::parse_ibs(matches)?; - let output_progress = ddargs::parse_progress_level(matches)?; + let ibs: usize = parseargs::parse_ibs(matches)?; if let Some(fname) = matches.opt_str("if") { Ok(Input { src: File::open(fname)?, ibs, - output_progress, }) } else @@ -243,20 +139,17 @@ struct Output { dst: W, obs: usize, - conv_table: Option, } impl Output { fn new(matches: &getopts::Matches) -> Result> { - let obs: usize = ddargs::parse_obs(matches)?; - let conv_table = ddargs::parse_conv_table(matches)?; + let obs: usize = parseargs::parse_obs(matches)?; Ok( Output { dst: io::stdout(), obs, - conv_table, } ) } @@ -265,15 +158,13 @@ impl Output { impl Output { fn new(matches: &getopts::Matches) -> Result> { - let obs: usize = ddargs::parse_obs(matches)?; - let conv_table = ddargs::parse_conv_table(matches)?; + let obs: usize = parseargs::parse_obs(matches)?; if let Some(fname) = matches.opt_str("if") { Ok(Output { dst: File::open(fname)?, obs, - conv_table, }) } else @@ -310,6 +201,33 @@ impl Write for Output } } +struct Options +{ + conv: Option, + status_level: StatusLevel, + // ... +} + +struct ConversionOptions +{ + table: Option, + block: bool, + unblock: bool, + lcase: bool, + ucase: bool, + sparse: bool, + swab: bool, + sync: bool, +} + +#[derive(PartialEq)] +enum StatusLevel +{ + Progress, + Noxfer, + None, +} + fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { move || { @@ -333,10 +251,12 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -fn dd(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> +fn dd(mut i: Input, mut o: Output, opts: Options) -> Result<(usize, usize), Box> { - let prog_tx = if i.output_progress { + let prog_tx = if opts.status_level == StatusLevel::Progress + { let (prog_tx, prog_rx) = mpsc::channel(); + thread::spawn(gen_prog_updater(prog_rx)); Some(prog_tx) @@ -364,7 +284,9 @@ fn dd(mut i: Input, mut o: Output) -> Result<(usize, us }; let w_len = o.write(&buf[..r_len])?; - o.flush()?; + + // TODO: Some flag (sync?) controls this behaviour + // o.flush()?; bytes_out += w_len; @@ -377,6 +299,44 @@ fn dd(mut i: Input, mut o: Output) -> Result<(usize, us Ok((bytes_in, bytes_out)) } +#[macro_export] +macro_rules! build_app ( + () => + { + app!(SYNTAX, SUMMARY, LONG_HELP) + .optopt( + "", + "if", + "The input file", + "FILE" + ) + .optopt( + "", + "ibs", + "read up to BYTES bytes at a time (default: 512)", + "BYTES" + ) + .optopt( + "", + "of", + "The output file", + "FILE" + ) + .optopt( + "", + "obs", + "write BYTES bytes at a time (default: 512)", + "BYTES" + ) + .optopt( + "", + "conv", + "One or more conversion options as a comma-serparated list", + "OPT[,OPT]..." + ) + } +); + fn append_dashes_if_not_present(mut acc: Vec, s: &String) -> Vec { if Some("--") == s.get(0..=1) { @@ -393,43 +353,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 .iter() .fold(Vec::new(), append_dashes_if_not_present); - let syntax = format!( - "{0} [OPERAND]...\n{0} OPTION", - NAME - ); + let matches = build_app!().parse(dashed_args); - let matches = app!(&syntax, SUMMARY, LONG_HELP) - .optopt( - "", - "if", - "The input file", - "FILE" - ) - .optopt( - "", - "ibs", - "read up to BYTES bytes at a time (default: 512)", - "BYTES" - ) - .optopt( - "", - "of", - "The output file", - "FILE" - ) - .optopt( - "", - "obs", - "write BYTES bytes at a time (default: 512)", - "BYTES" - ) - .optopt( - "", - "conv", - "One or more conversion options as a comma-serparated list", - "OPT[,OPT]..." - ) - .parse(dashed_args); + let opts = parse_options(&matches) + .expect("TODO: Return correct error code"); let result = match (matches.opt_present("if"), matches.opt_present("of")) { @@ -440,7 +367,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o) + dd(i, o, opts) }, (true, false) => { @@ -449,7 +376,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o) + dd(i, o, opts) }, (false, true) => { @@ -458,7 +385,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o) + dd(i, o, opts) }, (false, false) => { @@ -467,7 +394,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o) + dd(i, o, opts) }, }; diff --git a/src/uu/dd/src/ddargs.rs b/src/uu/dd/src/parseargs.rs similarity index 56% rename from src/uu/dd/src/ddargs.rs rename to src/uu/dd/src/parseargs.rs index 86805d213..69b4cea9d 100644 --- a/src/uu/dd/src/ddargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -1,8 +1,11 @@ use super::*; +use crate::conversion_tables::*; + #[derive(Debug)] enum ParseError { + ConvFlagNoMatch(String), MultiplierString(String), MultiplierStringWouldOverflow(String), } @@ -16,6 +19,52 @@ impl std::fmt::Display for ParseError impl Error for ParseError {} +enum ConvFlag +{ + Table(ConversionTable), + Block, + Unblock, + UCase, + LCase, + Sparse, + Swab, + Sync, +} + +impl std::str::FromStr for ConvFlag +{ + type Err = ParseError; + + fn from_str(s: &str) -> Result + { + match s + { + "ascii" => + Ok(Self::Table(EBCDIC_TO_ASCII)), + "ebcdic" => + Ok(Self::Table(ASCII_TO_EBCDIC)), + "ibm" => + Ok(Self::Table(ASCII_TO_IBM)), + "block" => + Ok(Self::Block), + "unblock" => + Ok(Self::Unblock), + "lcase" => + Ok(Self::LCase), + "ucase" => + Ok(Self::UCase), + "sparse" => + Ok(Self::Sparse), + "swab" => + Ok(Self::Swab), + "sync" => + Ok(Self::Sync), + _ => + Err(ParseError::ConvFlagNoMatch(String::from(s))) + } + } +} + fn parse_multiplier<'a>(s: &'a str) -> Result> { let s = s.trim(); @@ -114,17 +163,13 @@ pub fn parse_progress_level(matches: &getopts::Matches) -> Result Result> { - if let Some(str_with_prefixes) = matches.opt_str("bs") + if let Some(mixed_str) = matches.opt_str("bs") { - // TODO: Parse a string containing the number with potential k, kB, kiB, ... multiplier - // possibly implemented elsewhere, but probably not in exactly the dd style - panic!() + parse_bytes_with_opt_multiplier(mixed_str) } - else if let Some(str_with_prefixes) = matches.opt_str("obs") + else if let Some(mixed_str) = matches.opt_str("obs") { - // TODO: Parse a string containing the number with potential k, kB, kiB, ... multiplier - // possibly implemented elsewhere, but probably not in exactly the dd style - panic!() + parse_bytes_with_opt_multiplier(mixed_str) } else { @@ -132,19 +177,105 @@ pub fn parse_obs(matches: &getopts::Matches) -> Result> } } -pub fn parse_conv_table(matches: &getopts::Matches) -> Result, Box> +/// Parse the options and flags that control the way +/// the file(s) is(are) copied and converted +pub fn parse_options(matches: &getopts::Matches) -> Result> { - // TODO: Complete this stub fn - Ok(None) + panic!() +} + +/// Parse Conversion Options that control how the file is converted +pub fn parse_conv_options(matches: &getopts::Matches) -> Result> +{ + let flags = parse_conv_opts(matches)?; + + let mut table = None; + let mut block = false; + let mut unblock = false; + let mut ucase = false; + let mut lcase = false; + let mut sparse = false; + let mut swab = false; + let mut sync = false; + + for flag in flags + { + match flag + { + ConvFlag::Table(ct) => + { + table = Some(ct); + }, + ConvFlag::Block => + { + block = true; + }, + ConvFlag::Unblock => + { + unblock = true; + }, + ConvFlag::UCase => + { + ucase = true; + }, + ConvFlag::LCase => + { + lcase = true; + }, + ConvFlag::Sparse => + { + sparse = true; + }, + ConvFlag::Swab => + { + swab = true; + }, + ConvFlag::Sync => + { + sync = true; + }, + } + } + + Ok(ConversionOptions + { + table, + block, + unblock, + ucase, + lcase, + sparse, + swab, + sync, + }) +} + +fn parse_conv_opts(matches: &getopts::Matches) -> Result, Box> +{ + let mut flags = Vec::new(); + + if let Some(comma_str) = matches.opt_str("conv") + { + for s in comma_str.split(",") + { + let flag = s.parse()?; + flags.push(flag); + } + + } + + Ok(flags) } #[cfg(test)] mod test { + use super::*; macro_rules! test_byte_parser ( ( $test_name:ident, $bs_str:expr, $bs:expr ) => { + #[allow(non_snake_case)] #[test] fn $test_name() { @@ -167,9 +298,30 @@ mod test { } #[test] - fn test_conv_options_parser() + fn test_conv_parser_ibm_conv_table() { - panic!() + let args = vec![ + String::from("ketchup"), + String::from("mustard"), + String::from("--conv=ibm"), + String::from("relish"), + ]; + + let matches = build_app!().parse(args); + + assert!(matches.opt_present("conv")); + + if let Some(table) = parse_conv_opts(&matches).unwrap() + { + for (s, t) in ASCII_TO_IBM.iter().zip(table.iter()) + { + assert_eq!(s, t) + } + } + else + { + panic!() + } } test_byte_parser!( @@ -293,11 +445,31 @@ mod test { #[test] #[should_panic] + #[allow(non_snake_case)] fn test_KB_multiplier_error() { + // KB is not valid (kB, K, and KiB are) let bs_str = String::from("2000 KB"); parse_bytes_with_opt_multiplier(bs_str).unwrap(); } + #[test] + #[should_panic] + fn test_overflow_panic() + { + let bs_str = format!("{} KiB", usize::MAX); + + parse_bytes_with_opt_multiplier(bs_str).unwrap(); + } + + #[test] + #[should_panic] + fn test_neg_panic() + { + let bs_str = format!("{} KiB", -1); + + parse_bytes_with_opt_multiplier(bs_str).unwrap(); + } + } diff --git a/src/uu/dd/src/test_dd_internal.rs b/src/uu/dd/src/test_dd_internal.rs index c171e5b36..bea25b54d 100644 --- a/src/uu/dd/src/test_dd_internal.rs +++ b/src/uu/dd/src/test_dd_internal.rs @@ -3,13 +3,88 @@ use super::*; use std::io::prelude::*; use std::io::BufReader; use std::fs; -use md5::{ Md5, Digest, }; -use hex_literal::hex; +// use md5::{ Md5, Digest, }; +// use hex_literal::hex; + // use tempfile::tempfile; // TODO: (Maybe) Use tempfiles in the tests. -macro_rules! make_hash_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $exp:expr ) => +//macro_rules! make_hash_test ( +// ( $test_id:ident, $test_name:expr, $src:expr, $exp:expr ) => +// { +// #[test] +// fn $test_id() +// { +// let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); +// +// let i = Input { +// src: $src, +// ibs: 256, +// output_progress: false, +// }; +// +// let o = Output { +// dst: File::create(&tmp_fname).unwrap(), +// obs: 1024, +// conv_table: None, +// }; +// +// dd(i,o).unwrap(); +// +// let res = { +// let res = File::open(&tmp_fname).unwrap(); +// let res = BufReader::new(res); +// +// let mut h = Md5::new(); +// for b in res.bytes() +// { +// h.update([b.unwrap()]); +// } +// +// h.finalize() +// }; +// +// assert_eq!(hex!($exp), res[..]); +// +// fs::remove_file(&tmp_fname).unwrap(); +// } +// }; +// ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $exp:expr ) => +// { +// #[test] +// fn $test_id() +// { +// let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); +// +// let o = Output { +// dst: File::create(&tmp_fname).unwrap(), +// obs: $o.obs, +// }; +// +// dd($i,o).unwrap(); +// +// let res = { +// let res = File::open(&tmp_fname).unwrap(); +// let res = BufReader::new(res); +// +// let mut h = Md5::new(); +// for b in res.bytes() +// { +// h.update([b.unwrap()]); +// } +// +// h.finalize() +// }; +// +// assert_eq!(hex!($exp), res[..]); +// +// fs::remove_file(&tmp_fname).unwrap(); +// } +// }; +//); + +macro_rules! make_spec_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $spec:expr ) => { #[test] fn $test_id() @@ -18,37 +93,45 @@ macro_rules! make_hash_test ( let i = Input { src: $src, - ibs: 256, - output_progress: false, + ibs: 512, }; let o = Output { dst: File::create(&tmp_fname).unwrap(), - obs: 1024, - conv_table: None, + obs: 512, }; - dd(i,o).unwrap(); - - let res = { - let res = File::open(&tmp_fname).unwrap(); - let res = BufReader::new(res); - - let mut h = Md5::new(); - for b in res.bytes() - { - h.update([b.unwrap()]); - } - - h.finalize() + let opts = Options { + conv: Some(ConversionOptions { + table: None, + block: false, + unblock: false, + lcase: false, + ucase: false, + sparse: false, + swab: false, + sync: false, + }), + status_level: None, }; - assert_eq!(hex!($exp), res[..]); + dd($i,o,opts).unwrap(); + + let res = File::open(&tmp_fname).unwrap(); + let res = BufReader::new(res); + + let spec = BufReader::new($spec); + + for (b_res, b_spec) in res.bytes().zip(spec.bytes()) + { + assert_eq!(b_res.unwrap(), + b_spec.unwrap()); + } fs::remove_file(&tmp_fname).unwrap(); } }; - ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $exp:expr ) => + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $conv:expr, $spec:expr ) => { #[test] fn $test_id() @@ -58,46 +141,23 @@ macro_rules! make_hash_test ( let o = Output { dst: File::create(&tmp_fname).unwrap(), obs: $o.obs, - conv_table: $o.conv_table, }; - dd($i,o).unwrap(); - - let res = { - let res = File::open(&tmp_fname).unwrap(); - let res = BufReader::new(res); - - let mut h = Md5::new(); - for b in res.bytes() - { - h.update([b.unwrap()]); - } - - h.finalize() + let opts = Options { + conv: Some(ConversionOptions { + table: $conv, + block: false, + unblock: false, + lcase: false, + ucase: false, + sparse: false, + swab: false, + sync: false, + }), + status_level: None, }; - assert_eq!(hex!($exp), res[..]); - - fs::remove_file(&tmp_fname).unwrap(); - } - }; -); - -macro_rules! make_spec_test ( - ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr ) => - { - #[test] - fn $test_id() - { - let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); - - let o = Output { - dst: File::create(&tmp_fname).unwrap(), - obs: $o.obs, - conv_table: $o.conv_table, - }; - - dd($i,o).unwrap(); + dd($i,o,opts).unwrap(); let res = File::open(&tmp_fname).unwrap(); let res = BufReader::new(res); @@ -115,39 +175,32 @@ macro_rules! make_spec_test ( }; ); -make_hash_test!( - empty_file_test, - "stdio-empty-file", - io::empty(), - "d41d8cd98f00b204e9800998ecf8427e" -); - -make_hash_test!( +make_spec_test!( zeros_4k_test, "zeros-4k", File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(), - "620f0b67a91f7f74151bc5be745b7110" + File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap() ); -make_hash_test!( +make_spec_test!( ones_4k_test, "ones-4k", File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap(), - "6ae59e64850377ee5470c854761551ea" + File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap() ); -make_hash_test!( +make_spec_test!( deadbeef_32k_test, "deadbeef-32k", File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), - "18d99661a1de1fc9af21b0ec2cd67ba3" + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); -make_hash_test!( +make_spec_test!( random_73k_test, "random-73k", File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), - "5828891cb1230748e146f34223bbd3b5" + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); make_spec_test!( @@ -156,13 +209,12 @@ make_spec_test!( Input { src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), ibs: 512, - output_progress: false, }, Output { dst: Vec::new(), // unused! obs: 512, - conv_table: Some(ASCII_TO_EBCDIC), }, + Some(ASCII_TO_EBCDIC), File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() ); @@ -172,13 +224,12 @@ make_spec_test!( Input { src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), ibs: 512, - output_progress: false, }, Output { dst: Vec::new(), // unused! obs: 512, - conv_table: Some(EBCDIC_TO_ASCII), }, + Some(EBCDIC_TO_ASCII), File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() ); @@ -188,13 +239,12 @@ make_spec_test!( Input { src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), ibs: 512, - output_progress: false, }, Output { dst: Vec::new(), // unused! obs: 512, - conv_table: Some(ASCII_TO_IBM), }, + Some(ASCII_TO_IBM), File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() ); @@ -204,13 +254,12 @@ make_spec_test!( Input { src: File::open("./test-resources/lcase-ascii.test").unwrap(), ibs: 512, - output_progress: false, }, Output { dst: Vec::new(), // unused! obs: 512, - conv_table: Some(LCASE_TO_UCASE), }, + Some(LCASE_TO_UCASE), File::open("./test-resources/ucase-ascii.test").unwrap() ); @@ -220,13 +269,12 @@ make_spec_test!( Input { src: File::open("./test-resources/ucase-ascii.test").unwrap(), ibs: 512, - output_progress: false, }, Output { dst: Vec::new(), // unused! obs: 512, - conv_table: Some(UCASE_TO_LCASE), }, + Some(UCASE_TO_LCASE), File::open("./test-resources/lcase-ascii.test").unwrap() ); @@ -240,16 +288,28 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let i = Input { src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), ibs: 256, - output_progress: false, }; let o = Output { dst: File::create(&tmp_fname_ae).unwrap(), obs: 1024, - conv_table: Some(ASCII_TO_EBCDIC), }; - dd(i,o).unwrap(); + let opts = Options { + conv: Some(ConversionOptions { + table: Some(ASCII_TO_EBCDIC), + block: false, + unblock: false, + lcase: false, + ucase: false, + sparse: false, + swab: false, + sync: false, + }), + status_level: None, + }; + + dd(i,o,opts).unwrap(); // EBCDIC->ASCII let test_name = "all-valid-ebcdic-to-ascii"; @@ -258,16 +318,28 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let i = Input { src: File::open(&tmp_fname_ae).unwrap(), ibs: 256, - output_progress: false, }; let o = Output { dst: File::create(&tmp_fname_ea).unwrap(), obs: 1024, - conv_table: Some(EBCDIC_TO_ASCII), }; - dd(i,o).unwrap(); + let opts = Options { + conv: Some(ConversionOptions { + table: Some(ASCII_TO_EBCDIC), + block: false, + unblock: false, + lcase: false, + ucase: false, + sparse: false, + swab: false, + sync: false, + }), + status_level: None, + }; + + dd(i,o,opts).unwrap(); let res = { let res = File::open(&tmp_fname_ea).unwrap(); @@ -287,43 +359,3 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() fs::remove_file(&tmp_fname_ae).unwrap(); fs::remove_file(&tmp_fname_ea).unwrap(); } - -// #[test] -// fn copy_zerofile_from_args() -// { -// let spec = File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(); -// let tmp_fname = format!("./test-resources/{}", "zeros-from-args.test"); -// -// let args = vec![ -// String::from("if=./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test"), -// String::from(&tmp_fname), -// ]; -// let args = Args { args }; -// -// uumain(args); -// -// let res = File::open(&tmp_fname).unwrap(); -// let res = BufReader::new(res); -// -// let spec = BufReader::new(spec); -// -// for (b_res, b_spec) in res.bytes().zip(spec.bytes()) -// { -// assert_eq!(b_res.unwrap(), -// b_spec.unwrap()); -// } -// -// fs::remove_file(&tmp_fname).unwrap(); -// } - -//use rand::prelude::*; -//#[test] -//fn make_test_data() -//{ -// let mut f = File::create("./test-resources/random-walk-through-the-ascii-ranged-forest.test").unwrap(); -// // let mut rng = rand::thread_rng(); - -// for _ in 0..65536 { -// f.write(&[c]).unwrap(); -// } -//} diff --git a/src/uu/dd/test-resources/gnudd-conv-ebcdic-ltou-seq-byte-values.spec b/src/uu/dd/test-resources/gnudd-conv-ebcdic-ltou-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..ed0686639a7ec0d1d4df7e51ef806dd6f76a0e2a GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=a}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&vL?)#J#X%U`KwoXty#Zr&h5-No literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/gnudd-conv-ebcdic-utol-seq-byte-values.spec b/src/uu/dd/test-resources/gnudd-conv-ebcdic-utol-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..13c82e5e3c8500dbc52f6820c5fe57b5ab5e9762 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=;wv8K7HwWYnSvt!c4DU+v7oiTmU%q5GLEnTsE?aI9ma%VMG z;*vkG@`68myrP1#lBkL}o2G`g7N?Fhm!W~Nl&J}anWcrbl?cm2J10jMXE#?5_dw6! zpwN);u*ityq|}u3w9Jgcg5sjmlJc^Kiteu7p8me933F!8o4a8C>Q!EA*00;RVe_V~ sTeff8xnuXPg?skzJALZxne*pv-@1F}{=L_)-oAPN?)R_1fBydi0I5)U0RR91 literal 0 HcmV?d00001 From 9e933a38600eac5ea5fc0cf15c19132befdb8cbc Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 8 Apr 2021 19:07:52 -0700 Subject: [PATCH 07/66] Completes refactor & clean-up. Implements full conv flag parsing. - All conv flags now parsed (full functionality is TODO) - Changes functionality of eg. conv=ebcdic,ucase to match gnudd --- src/uu/dd/src/conversion_tables.rs | 205 +++++--- src/uu/dd/src/dd.rs | 131 ++--- src/uu/dd/src/parseargs.rs | 473 ++++++++++++------ src/uu/dd/src/test_dd_internal.rs | 365 ++++++-------- .../gnudd-conv-ibm-ltou-seq-byte-values.spec | Bin 0 -> 256 bytes .../gnudd-conv-ibm-utol-seq-byte-values.spec | Bin 0 -> 256 bytes src/uu/dd/test-resources/lcase-ascii.test | Bin 129 -> 128 bytes src/uu/dd/test-resources/lcase-ebcdic.spec | Bin 0 -> 256 bytes src/uu/dd/test-resources/lcase-ebcdic.test | Bin 0 -> 256 bytes src/uu/dd/test-resources/lcase-ibm.spec | Bin 0 -> 256 bytes src/uu/dd/test-resources/lcase-ibm.test | Bin 0 -> 256 bytes src/uu/dd/test-resources/ucase-ascii.test | Bin 129 -> 128 bytes src/uu/dd/test-resources/ucase-ebcdic.spec | Bin 0 -> 256 bytes src/uu/dd/test-resources/ucase-ebcdic.test | Bin 0 -> 256 bytes src/uu/dd/test-resources/ucase-ibm.spec | Bin 0 -> 256 bytes src/uu/dd/test-resources/ucase-ibm.test | Bin 0 -> 256 bytes 16 files changed, 681 insertions(+), 493 deletions(-) create mode 100644 src/uu/dd/test-resources/gnudd-conv-ibm-ltou-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-ibm-utol-seq-byte-values.spec create mode 100644 src/uu/dd/test-resources/lcase-ebcdic.spec create mode 100644 src/uu/dd/test-resources/lcase-ebcdic.test create mode 100644 src/uu/dd/test-resources/lcase-ibm.spec create mode 100644 src/uu/dd/test-resources/lcase-ibm.test create mode 100644 src/uu/dd/test-resources/ucase-ebcdic.spec create mode 100644 src/uu/dd/test-resources/ucase-ebcdic.test create mode 100644 src/uu/dd/test-resources/ucase-ibm.spec create mode 100644 src/uu/dd/test-resources/ucase-ibm.test diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs index 62c75febc..9ba604465 100644 --- a/src/uu/dd/src/conversion_tables.rs +++ b/src/uu/dd/src/conversion_tables.rs @@ -4,6 +4,45 @@ // obtained by treating the ASCII representation as a number. pub type ConversionTable = [u8; 256]; +pub const ASCII_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + +]; + pub const ASCII_TO_EBCDIC: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, @@ -23,6 +62,44 @@ pub const ASCII_TO_EBCDIC: ConversionTable = [ 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; +pub const ASCII_TO_EBCDIC_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_EBCDIC_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + pub const ASCII_TO_IBM: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, @@ -42,6 +119,45 @@ pub const ASCII_TO_IBM: ConversionTable = [ 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; +pub const ASCII_TO_IBM_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + + +pub const ASCII_TO_IBM_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + pub const EBCDIC_TO_ASCII: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, @@ -61,79 +177,42 @@ pub const EBCDIC_TO_ASCII: ConversionTable = [ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; -pub const ASCII_LCASE_TO_UCASE: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -pub const ASCII_UCASE_TO_LCASE: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -pub const EBCDIC_LCASE_TO_UCASE: ConversionTable = [ - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, - 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, - 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, - 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, - 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, - 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, - 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, - 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, - 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -pub const EBCDIC_UCASE_TO_LCASE: ConversionTable = [ +pub const EBCDIC_TO_ASCII_UCASE_TO_LCASE: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, - 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; + +pub const EBCDIC_TO_ASCII_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index cd04ee85d..aa5f9e4b4 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -17,7 +17,6 @@ mod parseargs; mod conversion_tables; use conversion_tables::*; -use parseargs::*; use std::error::Error; use std::fs::File; @@ -32,6 +31,8 @@ const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; const SUMMARY: &str = "convert, and optionally copy, a file"; const LONG_HELP: &str = ""; +const DEFAULT_FILL_BYTE: u8 = 0xDD; + const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; @@ -42,6 +43,39 @@ enum SrcStat EOF, } +/// Captures all Conv Flags that apply to the input +pub struct ConvFlagInput +{ + ctable: Option, + block: bool, + unblock: bool, + swab: bool, + sync: bool, + noerror: bool, +} + +/// Captures all Conv Flags that apply to the output +#[derive(Debug, PartialEq)] +pub struct ConvFlagOutput +{ + sparse: bool, + excl: bool, + nocreat: bool, + notrunc: bool, + fdatasync: bool, + fsync: bool, +} + +/// The value of the status cl-option. +/// Controls printing of transfer stats +#[derive(PartialEq)] +pub enum StatusLevel +{ + Progress, + Noxfer, + None, +} + #[derive(Debug)] enum InternalError { @@ -62,13 +96,25 @@ struct Input { src: R, ibs: usize, + xfer_stats: StatusLevel, + cf: ConvFlagInput, } impl Read for Input { - fn read(&mut self, buf: &mut [u8]) -> io::Result + fn read(&mut self, mut buf: &mut [u8]) -> io::Result { - self.src.read(buf) + let len = self.src.read(&mut buf)?; + + if let Some(ct) = self.cf.ctable + { + for idx in 0..len + { + buf[idx] = ct[buf[idx] as usize]; + } + } + + Ok(len) } } @@ -76,12 +122,16 @@ impl Input { fn new(matches: &getopts::Matches) -> Result> { - let ibs: usize = parseargs::parse_ibs(matches)?; + let ibs = parseargs::parse_ibs(matches)?; + let xfer_stats = parseargs::parse_status_level(matches)?; + let cf = parseargs::parse_conv_flag_input(matches)?; Ok( Input { src: io::stdin(), ibs, + xfer_stats, + cf, } ) @@ -92,13 +142,17 @@ impl Input { fn new(matches: &getopts::Matches) -> Result> { - let ibs: usize = parseargs::parse_ibs(matches)?; + let ibs = parseargs::parse_ibs(matches)?; + let xfer_stats = parseargs::parse_status_level(matches)?; + let cf = parseargs::parse_conv_flag_input(matches)?; if let Some(fname) = matches.opt_str("if") { Ok(Input { src: File::open(fname)?, ibs, + xfer_stats, + cf, }) } else @@ -139,17 +193,20 @@ struct Output { dst: W, obs: usize, + cf: ConvFlagOutput, } impl Output { fn new(matches: &getopts::Matches) -> Result> { - let obs: usize = parseargs::parse_obs(matches)?; + let obs = parseargs::parse_obs(matches)?; + let cf = parseargs::parse_conv_flag_output(matches)?; Ok( Output { dst: io::stdout(), obs, + cf, } ) } @@ -158,13 +215,15 @@ impl Output { impl Output { fn new(matches: &getopts::Matches) -> Result> { - let obs: usize = parseargs::parse_obs(matches)?; + let obs = parseargs::parse_obs(matches)?; + let cf = parseargs::parse_conv_flag_output(matches)?; if let Some(fname) = matches.opt_str("if") { Ok(Output { dst: File::open(fname)?, obs, + cf, }) } else @@ -178,21 +237,7 @@ impl Write for Output { fn write(&mut self, buf: &[u8]) -> io::Result { - if let Some(ct) = self.conv_table - { - let mut cbuf = vec![0; buf.len()]; - - for (idx, byte) in buf.iter().enumerate() - { - cbuf[idx] = ct[*byte as usize] - } - - self.dst.write(&cbuf) - } - else - { self.dst.write(buf) - } } fn flush(&mut self) -> io::Result<()> @@ -201,33 +246,6 @@ impl Write for Output } } -struct Options -{ - conv: Option, - status_level: StatusLevel, - // ... -} - -struct ConversionOptions -{ - table: Option, - block: bool, - unblock: bool, - lcase: bool, - ucase: bool, - sparse: bool, - swab: bool, - sync: bool, -} - -#[derive(PartialEq)] -enum StatusLevel -{ - Progress, - Noxfer, - None, -} - fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { move || { @@ -251,9 +269,9 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -fn dd(mut i: Input, mut o: Output, opts: Options) -> Result<(usize, usize), Box> +fn dd(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> { - let prog_tx = if opts.status_level == StatusLevel::Progress + let prog_tx = if i.xfer_stats == StatusLevel::Progress { let (prog_tx, prog_rx) = mpsc::channel(); @@ -271,7 +289,7 @@ fn dd(mut i: Input, mut o: Output, opts: Options) -> Re loop { - let mut buf = vec![0xDD; o.obs]; + let mut buf = vec![DEFAULT_FILL_BYTE; o.obs]; let r_len = match i.fill_n(&mut buf, o.obs)? { SrcStat::Read(len) => @@ -355,9 +373,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 let matches = build_app!().parse(dashed_args); - let opts = parse_options(&matches) - .expect("TODO: Return correct error code"); - let result = match (matches.opt_present("if"), matches.opt_present("of")) { (true, true) => @@ -367,7 +382,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o, opts) + dd(i,o) }, (true, false) => { @@ -376,7 +391,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o, opts) + dd(i,o) }, (false, true) => { @@ -385,7 +400,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o, opts) + dd(i,o) }, (false, false) => { @@ -394,7 +409,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let o = Output::::new(&matches) .expect("TODO: Return correct error code"); - dd(i, o, opts) + dd(i,o) }, }; diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 69b4cea9d..e2f8b9934 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -2,11 +2,16 @@ use super::*; use crate::conversion_tables::*; +/// Parser Errors indicate erroneous cl-args #[derive(Debug)] -enum ParseError +pub enum ParseError { + MultipleFmtTable, + MultipleUCaseLCase, + MultipleBlockUnblock, ConvFlagNoMatch(String), - MultiplierString(String), + NoMatchingMultiplier(String), + MultiplierStringContainsNoValue(String), MultiplierStringWouldOverflow(String), } @@ -19,16 +24,29 @@ impl std::fmt::Display for ParseError impl Error for ParseError {} +/// Some flags specified as part of a conv=CONV[,CONV]... block +/// relate to the input file, others to the output file. +/// They are separated here. enum ConvFlag { - Table(ConversionTable), + // Input + FmtAtoE, + FmtEtoA, + FmtAtoI, Block, Unblock, UCase, LCase, - Sparse, Swab, Sync, + NoError, + // Output + Sparse, + Excl, + NoCreat, + NoTrunc, + FDataSync, + FSync, } impl std::str::FromStr for ConvFlag @@ -39,36 +57,48 @@ impl std::str::FromStr for ConvFlag { match s { + // Input "ascii" => - Ok(Self::Table(EBCDIC_TO_ASCII)), + Ok(Self::FmtEtoA), "ebcdic" => - Ok(Self::Table(ASCII_TO_EBCDIC)), + Ok(Self::FmtAtoE), "ibm" => - Ok(Self::Table(ASCII_TO_IBM)), + Ok(Self::FmtAtoI), + "lcase" => + Ok(Self::UCase), + "ucase" => + Ok(Self::LCase), "block" => Ok(Self::Block), "unblock" => Ok(Self::Unblock), - "lcase" => - Ok(Self::LCase), - "ucase" => - Ok(Self::UCase), - "sparse" => - Ok(Self::Sparse), "swab" => Ok(Self::Swab), "sync" => Ok(Self::Sync), + "noerror" => + Ok(Self::NoError), + // Output + "sparse" => + Ok(Self::Sparse), + "excl" => + Ok(Self::Excl), + "nocreat" => + Ok(Self::NoCreat), + "notrunc" => + Ok(Self::NoTrunc), + "fdatasync" => + Ok(Self::FDataSync), + "fsync" => + Ok(Self::FSync), _ => Err(ParseError::ConvFlagNoMatch(String::from(s))) } } } -fn parse_multiplier<'a>(s: &'a str) -> Result> +fn parse_multiplier<'a>(s: &'a str) -> Result { - let s = s.trim(); - match s { "c" => @@ -111,15 +141,19 @@ fn parse_multiplier<'a>(s: &'a str) -> Result> // "Y" | "YiB" => // Ok(1024*1024*1024*1024*1024*1024*1024*1024), _ => - Err(Box::new(ParseError::MultiplierString(String::from(s)))), + Err(ParseError::NoMatchingMultiplier(String::from(s))), } } -fn parse_bytes_with_opt_multiplier(s: String) -> Result> +fn parse_bytes_with_opt_multiplier(s: String) -> Result { - if let Some(idx) = s.find(' ') + if let Some(idx) = s.find(char::is_alphabetic) { - let base: usize = s[0..idx].parse()?; + let base: usize = match s[0..idx].parse() + { + Ok(val) => val, + Err(_) => return Err(ParseError::MultiplierStringContainsNoValue(s)), + }; let mult = parse_multiplier(&s[idx..])?; if let Some(bytes) = base.checked_mul(mult) @@ -128,18 +162,21 @@ fn parse_bytes_with_opt_multiplier(s: String) -> Result> } else { - Err(Box::new(ParseError::MultiplierStringWouldOverflow(s))) + Err(ParseError::MultiplierStringWouldOverflow(s)) } } else { - let bytes: usize = s.parse()?; - + let bytes: usize = match s.parse() + { + Ok(val) => val, + Err(_) => return Err(ParseError::MultiplierStringContainsNoValue(s)), + }; Ok(bytes) } } -pub fn parse_ibs(matches: &getopts::Matches) -> Result> +pub fn parse_ibs(matches: &getopts::Matches) -> Result { if let Some(mixed_str) = matches.opt_str("bs") { @@ -155,13 +192,13 @@ pub fn parse_ibs(matches: &getopts::Matches) -> Result> } } -pub fn parse_progress_level(matches: &getopts::Matches) -> Result> +pub fn parse_status_level(matches: &getopts::Matches) -> Result { - // TODO: Implement this stub proc - Ok(false) + // TODO: Impl + unimplemented!() } -pub fn parse_obs(matches: &getopts::Matches) -> Result> +pub fn parse_obs(matches: &getopts::Matches) -> Result { if let Some(mixed_str) = matches.opt_str("bs") { @@ -177,80 +214,53 @@ pub fn parse_obs(matches: &getopts::Matches) -> Result> } } -/// Parse the options and flags that control the way -/// the file(s) is(are) copied and converted -pub fn parse_options(matches: &getopts::Matches) -> Result> +fn parse_ctable(fmt: Option, case: Option) -> Option { - panic!() + match (fmt, case) + { + // Both 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, + }, + // Only one of {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, + }, + // Only one of {ucase, lcase} specified + (None, Some(ConvFlag::UCase)) => + Some(ASCII_LCASE_TO_UCASE), + (None, Some(ConvFlag::LCase)) => + Some(ASCII_UCASE_TO_LCASE), + (_, _) => + None, + } } -/// Parse Conversion Options that control how the file is converted -pub fn parse_conv_options(matches: &getopts::Matches) -> Result> -{ - let flags = parse_conv_opts(matches)?; - - let mut table = None; - let mut block = false; - let mut unblock = false; - let mut ucase = false; - let mut lcase = false; - let mut sparse = false; - let mut swab = false; - let mut sync = false; - - for flag in flags - { - match flag - { - ConvFlag::Table(ct) => - { - table = Some(ct); - }, - ConvFlag::Block => - { - block = true; - }, - ConvFlag::Unblock => - { - unblock = true; - }, - ConvFlag::UCase => - { - ucase = true; - }, - ConvFlag::LCase => - { - lcase = true; - }, - ConvFlag::Sparse => - { - sparse = true; - }, - ConvFlag::Swab => - { - swab = true; - }, - ConvFlag::Sync => - { - sync = true; - }, - } - } - - Ok(ConversionOptions - { - table, - block, - unblock, - ucase, - lcase, - sparse, - swab, - sync, - }) -} - -fn parse_conv_opts(matches: &getopts::Matches) -> Result, Box> +fn parse_conv_opts(matches: &getopts::Matches) -> Result, ParseError> { let mut flags = Vec::new(); @@ -267,11 +277,187 @@ fn parse_conv_opts(matches: &getopts::Matches) -> Result, Box Result +{ + let flags = parse_conv_opts(matches)?; + + let mut fmt = None; + let mut case = None; + let mut block = false; + let mut unblock = false; + let mut swab = false; + let mut sync = false; + let mut noerror = false; + + for flag in flags + { + match flag + { + ConvFlag::FmtEtoA => + if let Some(_) = fmt + { + return Err(ParseError::MultipleFmtTable); + } + else + { + fmt = Some(flag); + }, + ConvFlag::FmtAtoE => + if let Some(_) = fmt + { + return Err(ParseError::MultipleFmtTable); + } + else + { + fmt = Some(flag); + }, + ConvFlag::FmtAtoI => + if let Some(_) = fmt + { + return Err(ParseError::MultipleFmtTable); + } + else + { + fmt = Some(flag); + }, + ConvFlag::UCase => + if let Some(_) = case + { + return Err(ParseError::MultipleUCaseLCase); + } + else + { + case = Some(flag) + }, + ConvFlag::LCase => + if let Some(_) = case + { + return Err(ParseError::MultipleUCaseLCase); + } + else + { + case = Some(flag) + }, + ConvFlag::Block => + if !unblock + { + block = true; + } + else + { + return Err(ParseError::MultipleBlockUnblock); + }, + ConvFlag::Unblock => + if !block + { + unblock = true; + } + else + { + return Err(ParseError::MultipleBlockUnblock); + }, + ConvFlag::Swab => + swab = true, + ConvFlag::Sync => + sync = true, + ConvFlag::NoError => + noerror = true, + _ => {}, + } + } + + let ctable = parse_ctable(fmt, case); + + Ok(ConvFlagInput { + ctable, + block, + unblock, + swab, + sync, + noerror, + }) +} + +/// Parse Conversion Options (Output Variety) +/// Construct and validate a ConvFlagOutput +pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result +{ + let flags = parse_conv_opts(matches)?; + + let mut sparse = false; + let mut excl = false; + let mut nocreat = false; + let mut notrunc = false; + let mut fdatasync = false; + let mut fsync = false; + + for flag in flags + { + match flag + { + ConvFlag::Sparse => + sparse = true, + ConvFlag::Excl => + excl = true, + ConvFlag::NoCreat => + nocreat = true, + ConvFlag::NoTrunc => + notrunc = true, + ConvFlag::FDataSync => + fdatasync = true, + ConvFlag::FSync => + fsync = true, + _ => {}, + } + } + + Ok(ConvFlagOutput { + sparse, + excl, + nocreat, + notrunc, + fdatasync, + fsync, + }) +} + #[cfg(test)] mod test { use super::*; + // ----- ConvFlagInput/Output ----- + + #[test] + fn build_cfi() + { + let cfi_expd = ConvFlagInput { + ctable: Some(ASCII_TO_IBM), + block: false, + unblock: false, + swab: false, + sync: false, + noerror: false, + }; + + let args = vec![ + String::from("ketchup"), + String::from("mustard"), + String::from("--conv=ibm"), + String::from("relish"), + ]; + + let matches = build_app!().parse(args); + + let cfi_parsed = parse_conv_flag_input(&matches).unwrap(); + + unimplemented!() + // assert_eq!(cfi_expd, cfi_parsed); + } + + // ----- Multiplier Strings etc. ----- macro_rules! test_byte_parser ( ( $test_name:ident, $bs_str:expr, $bs:expr ) => { @@ -285,161 +471,122 @@ mod test { } ); - #[test] - fn test_input_parser() - { - panic!() - } - - #[test] - fn test_output_parser() - { - panic!() - } - - #[test] - fn test_conv_parser_ibm_conv_table() - { - let args = vec![ - String::from("ketchup"), - String::from("mustard"), - String::from("--conv=ibm"), - String::from("relish"), - ]; - - let matches = build_app!().parse(args); - - assert!(matches.opt_present("conv")); - - if let Some(table) = parse_conv_opts(&matches).unwrap() - { - for (s, t) in ASCII_TO_IBM.iter().zip(table.iter()) - { - assert_eq!(s, t) - } - } - else - { - panic!() - } - } - - test_byte_parser!( + test_byte_parser!( test_bytes_n, "765", 765 ); test_byte_parser!( test_bytes_c, - "13 c", + "13c", 13 ); test_byte_parser!( test_bytes_w, - "1 w", + "1w", 2 ); test_byte_parser!( test_bytes_b, - "1 b", + "1b", 512 ); test_byte_parser!( test_bytes_k, - "1 kB", + "1kB", 1000 ); test_byte_parser!( test_bytes_K, - "1 K", + "1K", 1024 ); test_byte_parser!( test_bytes_Ki, - "1 KiB", + "1KiB", 1024 ); test_byte_parser!( test_bytes_MB, - "1 MB", + "1MB", 1000*1000 ); test_byte_parser!( test_bytes_M, - "1 M", + "1M", 1024*1024 ); test_byte_parser!( test_bytes_Mi, - "1 MiB", + "1MiB", 1024*1024 ); test_byte_parser!( test_bytes_GB, - "1 GB", + "1GB", 1000*1000*1000 ); test_byte_parser!( test_bytes_G, - "1 G", + "1G", 1024*1024*1024 ); test_byte_parser!( test_bytes_Gi, - "1 GiB", + "1GiB", 1024*1024*1024 ); test_byte_parser!( test_bytes_TB, - "1 TB", + "1TB", 1000*1000*1000*1000 ); test_byte_parser!( test_bytes_T, - "1 T", + "1T", 1024*1024*1024*1024 ); test_byte_parser!( test_bytes_Ti, - "1 TiB", + "1TiB", 1024*1024*1024*1024 ); test_byte_parser!( test_bytes_PB, - "1 PB", + "1PB", 1000*1000*1000*1000*1000 ); test_byte_parser!( test_bytes_P, - "1 P", + "1P", 1024*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_Pi, - "1 PiB", + "1PiB", 1024*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_EB, - "1 EB", + "1EB", 1000*1000*1000*1000*1000*1000 ); test_byte_parser!( test_bytes_E, - "1 E", + "1E", 1024*1024*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_Ei, - "1 EiB", + "1EiB", 1024*1024*1024*1024*1024*1024 ); @@ -449,7 +596,7 @@ mod test { fn test_KB_multiplier_error() { // KB is not valid (kB, K, and KiB are) - let bs_str = String::from("2000 KB"); + let bs_str = String::from("2000KB"); parse_bytes_with_opt_multiplier(bs_str).unwrap(); } @@ -458,7 +605,7 @@ mod test { #[should_panic] fn test_overflow_panic() { - let bs_str = format!("{} KiB", usize::MAX); + let bs_str = format!("{}KiB", usize::MAX); parse_bytes_with_opt_multiplier(bs_str).unwrap(); } @@ -467,7 +614,7 @@ mod test { #[should_panic] fn test_neg_panic() { - let bs_str = format!("{} KiB", -1); + let bs_str = format!("{}KiB", -1); parse_bytes_with_opt_multiplier(bs_str).unwrap(); } diff --git a/src/uu/dd/src/test_dd_internal.rs b/src/uu/dd/src/test_dd_internal.rs index bea25b54d..66edd1c88 100644 --- a/src/uu/dd/src/test_dd_internal.rs +++ b/src/uu/dd/src/test_dd_internal.rs @@ -3,88 +3,51 @@ use super::*; use std::io::prelude::*; use std::io::BufReader; use std::fs; -// use md5::{ Md5, Digest, }; -// use hex_literal::hex; +use md5::{ Md5, Digest, }; +use hex_literal::hex; // use tempfile::tempfile; // TODO: (Maybe) Use tempfiles in the tests. -//macro_rules! make_hash_test ( -// ( $test_id:ident, $test_name:expr, $src:expr, $exp:expr ) => -// { -// #[test] -// fn $test_id() -// { -// let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); -// -// let i = Input { -// src: $src, -// ibs: 256, -// output_progress: false, -// }; -// -// let o = Output { -// dst: File::create(&tmp_fname).unwrap(), -// obs: 1024, -// conv_table: None, -// }; -// -// dd(i,o).unwrap(); -// -// let res = { -// let res = File::open(&tmp_fname).unwrap(); -// let res = BufReader::new(res); -// -// let mut h = Md5::new(); -// for b in res.bytes() -// { -// h.update([b.unwrap()]); -// } -// -// h.finalize() -// }; -// -// assert_eq!(hex!($exp), res[..]); -// -// fs::remove_file(&tmp_fname).unwrap(); -// } -// }; -// ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $exp:expr ) => -// { -// #[test] -// fn $test_id() -// { -// let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); -// -// let o = Output { -// dst: File::create(&tmp_fname).unwrap(), -// obs: $o.obs, -// }; -// -// dd($i,o).unwrap(); -// -// let res = { -// let res = File::open(&tmp_fname).unwrap(); -// let res = BufReader::new(res); -// -// let mut h = Md5::new(); -// for b in res.bytes() -// { -// h.update([b.unwrap()]); -// } -// -// h.finalize() -// }; -// -// assert_eq!(hex!($exp), res[..]); -// -// fs::remove_file(&tmp_fname).unwrap(); -// } -// }; -//); +const DEFAULT_CFO: ConvFlagOutput = ConvFlagOutput { + sparse: false, + excl: false, + nocreat: false, + notrunc: false, + fdatasync: false, + fsync: false, +}; + +#[macro_export] +macro_rules! cfi ( + () => + { + cfi!(None) + }; + ( $ctable:expr ) => + { + ConvFlagInput { + ctable: $ctable, + block: false, + unblock: false, + swab: false, + sync: false, + noerror: false, + } + }; +); macro_rules! make_spec_test ( + ( $test_id:ident, $test_name:expr, $src:expr ) => + { + // When spec not given, output should match input + make_spec_test!($test_id, $test_name, $src, None, $src); + }; ( $test_id:ident, $test_name:expr, $src:expr, $spec:expr ) => + { + make_spec_test!($test_id, $test_name, $src, None, $spec); + }; + ( $test_id:ident, $test_name:expr, $src:expr, $ctable:expr, $spec:expr ) => { #[test] fn $test_id() @@ -94,70 +57,17 @@ macro_rules! make_spec_test ( let i = Input { src: $src, ibs: 512, + xfer_stats: StatusLevel::None, + cf: cfi!($ctable), }; let o = Output { dst: File::create(&tmp_fname).unwrap(), obs: 512, + cf: DEFAULT_CFO, }; - let opts = Options { - conv: Some(ConversionOptions { - table: None, - block: false, - unblock: false, - lcase: false, - ucase: false, - sparse: false, - swab: false, - sync: false, - }), - status_level: None, - }; - - dd($i,o,opts).unwrap(); - - let res = File::open(&tmp_fname).unwrap(); - let res = BufReader::new(res); - - let spec = BufReader::new($spec); - - for (b_res, b_spec) in res.bytes().zip(spec.bytes()) - { - assert_eq!(b_res.unwrap(), - b_spec.unwrap()); - } - - fs::remove_file(&tmp_fname).unwrap(); - } - }; - ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $conv:expr, $spec:expr ) => - { - #[test] - fn $test_id() - { - let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); - - let o = Output { - dst: File::create(&tmp_fname).unwrap(), - obs: $o.obs, - }; - - let opts = Options { - conv: Some(ConversionOptions { - table: $conv, - block: false, - unblock: false, - lcase: false, - ucase: false, - sparse: false, - swab: false, - sync: false, - }), - status_level: None, - }; - - dd($i,o,opts).unwrap(); + dd(i,o).unwrap(); let res = File::open(&tmp_fname).unwrap(); let res = BufReader::new(res); @@ -175,45 +85,56 @@ macro_rules! make_spec_test ( }; ); -make_spec_test!( +#[test] +fn test_input_parser() +{ + let args = vec![ + String::from("ketchup"), + String::from("mustard"), + String::from("--conv=ibm"), + String::from("relish"), + ]; + + let matches = build_app!().parse(args); + // ... + + unimplemented!() +} + +#[test] +fn test_output_parser() +{ + unimplemented!() +} + + make_spec_test!( zeros_4k_test, "zeros-4k", - File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(), File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap() ); make_spec_test!( ones_4k_test, "ones-4k", - File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap(), File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap() ); make_spec_test!( deadbeef_32k_test, "deadbeef-32k", - File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); make_spec_test!( random_73k_test, "random-73k", - File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); make_spec_test!( atoe_conv_spec_test, "atoe-conv-spec-test", - Input { - src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - ibs: 512, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - }, + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), Some(ASCII_TO_EBCDIC), File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() ); @@ -221,14 +142,7 @@ make_spec_test!( make_spec_test!( etoa_conv_spec_test, "etoa-conv-spec-test", - Input { - src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - ibs: 512, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - }, + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), Some(EBCDIC_TO_ASCII), File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() ); @@ -236,14 +150,7 @@ make_spec_test!( make_spec_test!( atoibm_conv_spec_test, "atoibm-conv-spec-test", - Input { - src: File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - ibs: 512, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - }, + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), Some(ASCII_TO_IBM), File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() ); @@ -251,33 +158,95 @@ make_spec_test!( make_spec_test!( lcase_ascii_to_ucase_ascii, "lcase_ascii_to_ucase_ascii", - Input { - src: File::open("./test-resources/lcase-ascii.test").unwrap(), - ibs: 512, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - }, - Some(LCASE_TO_UCASE), + File::open("./test-resources/lcase-ascii.test").unwrap(), + Some(ASCII_LCASE_TO_UCASE), File::open("./test-resources/ucase-ascii.test").unwrap() ); make_spec_test!( ucase_ascii_to_lcase_ascii, "ucase_ascii_to_lcase_ascii", - Input { - src: File::open("./test-resources/ucase-ascii.test").unwrap(), - ibs: 512, - }, - Output { - dst: Vec::new(), // unused! - obs: 512, - }, - Some(UCASE_TO_LCASE), + File::open("./test-resources/ucase-ascii.test").unwrap(), + Some(ASCII_UCASE_TO_LCASE), File::open("./test-resources/lcase-ascii.test").unwrap() ); +// // *** +// make_spec_test!( +// lcase_ebcdic_to_ucase_ebcdic, +// "lcase_ebcdic_to_ucase_ebcdic", +// File::open("./test-resources/lcase-ebcdic.test").unwrap(), +// None, +// Some(EBCDIC_LCASE_TO_UCASE), +// File::open("./test-resources/ucase-ebcdic.test").unwrap() +// ); +// +// // *** +// make_spec_test!( +// ucase_ebcdic_to_lcase_ebcdic, +// "ucase_ebcdic_to_lcase_ebcdic", +// File::open("./test-resources/ucase-ebcdic.test").unwrap(), +// None, +// Some(EBCDIC_UCASE_TO_LCASE), +// File::open("./test-resources/lcase-ebcdic.test").unwrap() +// ); +// +// // *** +// make_spec_test!( +// lcase_ibm_to_ucase_ibm, +// "lcase_ibm_to_ucase_ibm", +// File::open("./test-resources/lcase-ibm.test").unwrap(), +// None, +// Some(IBM_LCASE_TO_UCASE), +// File::open("./test-resources/ucase-ibm.test").unwrap() +// ); +// +// // *** +// make_spec_test!( +// ucase_ibm_to_lcase_ibm, +// "ucase_ibm_to_lcase_ibm", +// File::open("./test-resources/ucase-ibm.test").unwrap(), +// None, +// Some(IBM_UCASE_TO_LCASE), +// File::open("./test-resources/lcase-ibm.test").unwrap() +// ); + +make_spec_test!( + // conv=ebcdic,ucase + atoe_and_ucase_conv_spec_test, + "atoe-and-ucase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(ASCII_TO_EBCDIC_LCASE_TO_UCASE), + File::open("./test-resources/ucase-ebcdic.test").unwrap() +); + +make_spec_test!( + // conv=ebcdic,lcase + atoe_and_lcase_conv_spec_test, + "atoe-and-lcase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(ASCII_TO_EBCDIC_UCASE_TO_LCASE), + File::open("./test-resources/lcase-ebcdic.test").unwrap() +); + +make_spec_test!( + // conv=ibm,ucase + atoibm_and_ucase_conv_spec_test, + "atoibm-and-ucase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(ASCII_TO_IBM_UCASE_TO_LCASE), + File::open("./test-resources/lcase-ibm.test").unwrap() +); + +make_spec_test!( + // conv=ibm,lcase + atoibm_and_lcase_conv_spec_test, + "atoibm-and-lcase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(ASCII_TO_IBM_LCASE_TO_UCASE), + File::open("./test-resources/ucase-ibm.test").unwrap() +); + #[test] fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { @@ -288,28 +257,17 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let i = Input { src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), ibs: 256, + xfer_stats: StatusLevel::None, + cf: cfi!(Some(ASCII_TO_EBCDIC)), }; let o = Output { dst: File::create(&tmp_fname_ae).unwrap(), obs: 1024, + cf: DEFAULT_CFO, }; - let opts = Options { - conv: Some(ConversionOptions { - table: Some(ASCII_TO_EBCDIC), - block: false, - unblock: false, - lcase: false, - ucase: false, - sparse: false, - swab: false, - sync: false, - }), - status_level: None, - }; - - dd(i,o,opts).unwrap(); + dd(i,o).unwrap(); // EBCDIC->ASCII let test_name = "all-valid-ebcdic-to-ascii"; @@ -318,28 +276,17 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let i = Input { src: File::open(&tmp_fname_ae).unwrap(), ibs: 256, + xfer_stats: StatusLevel::None, + cf: cfi!(Some(EBCDIC_TO_ASCII)), }; let o = Output { dst: File::create(&tmp_fname_ea).unwrap(), obs: 1024, + cf: DEFAULT_CFO, }; - let opts = Options { - conv: Some(ConversionOptions { - table: Some(ASCII_TO_EBCDIC), - block: false, - unblock: false, - lcase: false, - ucase: false, - sparse: false, - swab: false, - sync: false, - }), - status_level: None, - }; - - dd(i,o,opts).unwrap(); + dd(i,o).unwrap(); let res = { let res = File::open(&tmp_fname_ea).unwrap(); diff --git a/src/uu/dd/test-resources/gnudd-conv-ibm-ltou-seq-byte-values.spec b/src/uu/dd/test-resources/gnudd-conv-ibm-ltou-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..d425e7d012beb1c314cc4e1f72fafed1afe378e2 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=_JxWH%1WXt;%u54+FG1C(p-iH#!{vx9A=gl)>a}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&W=)tgd*0jy^H;B0yJr2mjT<&^ t+PY=?ww*h6@7lX(|Gv|w&Yn4c?)I&_ckbVN{p#(T_wRoH`upeqKL7->hPMC! literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/gnudd-conv-ibm-utol-seq-byte-values.spec b/src/uu/dd/test-resources/gnudd-conv-ibm-utol-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..1dcaccbcdf8adfd826979f9273c87cd9568b0209 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=;wv8K7HwWYnSvt!c4DU+v7oiTmU%q5GLEnTsE?aI9ma^o8- zamgQ8dBLB3p`wDalBkL}o2G`g7N?Fhm!W~Nl&J}anWcrbl?cm2J10jMXE#?5_dw6! zpwN);u*ityq|}u3w9Jgcg5sjmlJc^Kiteu7p8mdB6XwjGH+RAO)vMO7S-)=MhRvI{ sZrQ$V=Z@XG_U_rg@ARp&XU?Cyee3R>`}bbIdi&=6yWhY5{`vn80K=Q!EA*00;RVe_V~ sTeff8xnuXPg?skzJALZxne*pv-@1F}{=L_)-oAPN?)R_1fBydi0I5)U0RR91 literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/lcase-ebcdic.test b/src/uu/dd/test-resources/lcase-ebcdic.test new file mode 100644 index 0000000000000000000000000000000000000000..13c82e5e3c8500dbc52f6820c5fe57b5ab5e9762 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=;wv8K7HwWYnSvt!c4DU+v7oiTmU%q5GLEnTsE?aI9ma%VMG z;*vkG@`68myrP1#lBkL}o2G`g7N?Fhm!W~Nl&J}anWcrbl?cm2J10jMXE#?5_dw6! zpwN);u*ityq|}u3w9Jgcg5sjmlJc^Kiteu7p8me933F!8o4a8C>Q!EA*00;RVe_V~ sTeff8xnuXPg?skzJALZxne*pv-@1F}{=L_)-oAPN?)R_1fBydi0I5)U0RR91 literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/lcase-ibm.spec b/src/uu/dd/test-resources/lcase-ibm.spec new file mode 100644 index 0000000000000000000000000000000000000000..1dcaccbcdf8adfd826979f9273c87cd9568b0209 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=;wv8K7HwWYnSvt!c4DU+v7oiTmU%q5GLEnTsE?aI9ma^o8- zamgQ8dBLB3p`wDalBkL}o2G`g7N?Fhm!W~Nl&J}anWcrbl?cm2J10jMXE#?5_dw6! zpwN);u*ityq|}u3w9Jgcg5sjmlJc^Kiteu7p8mdB6XwjGH+RAO)vMO7S-)=MhRvI{ sZrQ$V=Z@XG_U_rg@ARp&XU?Cyee3R>`}bbIdi&=6yWhY5{`vn80K=`}bbIdi&=6yWhY5{`vn80K=a}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&vL?)#J#X%U`KwoXty#Zr&h5-No literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/ucase-ebcdic.test b/src/uu/dd/test-resources/ucase-ebcdic.test new file mode 100644 index 0000000000000000000000000000000000000000..ed0686639a7ec0d1d4df7e51ef806dd6f76a0e2a GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=a}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&vL?)#J#X%U`KwoXty#Zr&h5-No literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/ucase-ibm.spec b/src/uu/dd/test-resources/ucase-ibm.spec new file mode 100644 index 0000000000000000000000000000000000000000..d425e7d012beb1c314cc4e1f72fafed1afe378e2 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=_JxWH%1WXt;%u54+FG1C(p-iH#!{vx9A=gl)>a}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&W=)tgd*0jy^H;B0yJr2mjT<&^ t+PY=?ww*h6@7lX(|Gv|w&Yn4c?)I&_ckbVN{p#(T_wRoH`upeqKL7->hPMC! literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/ucase-ibm.test b/src/uu/dd/test-resources/ucase-ibm.test new file mode 100644 index 0000000000000000000000000000000000000000..d425e7d012beb1c314cc4e1f72fafed1afe378e2 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=_JxWH%1WXt;%u54+FG1C(p-iH#!{vx9A=gl)>a}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&W=)tgd*0jy^H;B0yJr2mjT<&^ t+PY=?ww*h6@7lX(|Gv|w&Yn4c?)I&_ckbVN{p#(T_wRoH`upeqKL7->hPMC! literal 0 HcmV?d00001 From 7f7dd2ad4ed0dce5aa199f3bcb3aa4b41bb1ddf1 Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 8 Apr 2021 19:14:26 -0700 Subject: [PATCH 08/66] Improves multiplier tests --- src/uu/dd/src/parseargs.rs | 61 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index e2f8b9934..8105efc9a 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -26,7 +26,6 @@ impl Error for ParseError {} /// Some flags specified as part of a conv=CONV[,CONV]... block /// relate to the input file, others to the output file. -/// They are separated here. enum ConvFlag { // Input @@ -512,82 +511,82 @@ mod test { test_byte_parser!( test_bytes_MB, - "1MB", - 1000*1000 + "2MB", + 2*1000*1000 ); test_byte_parser!( test_bytes_M, - "1M", - 1024*1024 + "2M", + 2*1024*1024 ); test_byte_parser!( test_bytes_Mi, - "1MiB", - 1024*1024 + "2MiB", + 2*1024*1024 ); test_byte_parser!( test_bytes_GB, - "1GB", - 1000*1000*1000 + "3GB", + 3*1000*1000*1000 ); test_byte_parser!( test_bytes_G, - "1G", - 1024*1024*1024 + "3G", + 3*1024*1024*1024 ); test_byte_parser!( test_bytes_Gi, - "1GiB", - 1024*1024*1024 + "3GiB", + 3*1024*1024*1024 ); test_byte_parser!( test_bytes_TB, - "1TB", - 1000*1000*1000*1000 + "4TB", + 4*1000*1000*1000*1000 ); test_byte_parser!( test_bytes_T, - "1T", - 1024*1024*1024*1024 + "4T", + 4*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_Ti, - "1TiB", - 1024*1024*1024*1024 + "4TiB", + 4*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_PB, - "1PB", - 1000*1000*1000*1000*1000 + "5PB", + 5*1000*1000*1000*1000*1000 ); test_byte_parser!( test_bytes_P, - "1P", - 1024*1024*1024*1024*1024 + "5P", + 5*1024*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_Pi, - "1PiB", - 1024*1024*1024*1024*1024 + "5PiB", + 5*1024*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_EB, - "1EB", - 1000*1000*1000*1000*1000*1000 + "6EB", + 6*1000*1000*1000*1000*1000*1000 ); test_byte_parser!( test_bytes_E, - "1E", - 1024*1024*1024*1024*1024*1024 + "6E", + 6*1024*1024*1024*1024*1024*1024 ); test_byte_parser!( test_bytes_Ei, - "1EiB", - 1024*1024*1024*1024*1024*1024 + "6EiB", + 6*1024*1024*1024*1024*1024*1024 ); #[test] From b461f102cceb4683e1a291a5593367ed397eca24 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 21 Apr 2021 15:24:14 -0700 Subject: [PATCH 09/66] Testt conv arg parsing. --- src/uu/dd/src/dd.rs | 6 +- .../src/{test_dd_internal.rs => dd_test.rs} | 62 +--- src/uu/dd/src/parseargs.rs | 242 ++----------- src/uu/dd/src/parseargs/test.rs | 317 ++++++++++++++++++ 4 files changed, 358 insertions(+), 269 deletions(-) rename src/uu/dd/src/{test_dd_internal.rs => dd_test.rs} (81%) create mode 100644 src/uu/dd/src/parseargs/test.rs diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index aa5f9e4b4..87949a08e 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -11,11 +11,11 @@ extern crate uucore; #[cfg(test)] -mod test_dd_internal; +mod dd_test; mod parseargs; -mod conversion_tables; +mod conversion_tables; use conversion_tables::*; use std::error::Error; @@ -46,7 +46,7 @@ enum SrcStat /// Captures all Conv Flags that apply to the input pub struct ConvFlagInput { - ctable: Option, + ctable: Option<&'static ConversionTable>, block: bool, unblock: bool, swab: bool, diff --git a/src/uu/dd/src/test_dd_internal.rs b/src/uu/dd/src/dd_test.rs similarity index 81% rename from src/uu/dd/src/test_dd_internal.rs rename to src/uu/dd/src/dd_test.rs index 66edd1c88..c2167a5e6 100644 --- a/src/uu/dd/src/test_dd_internal.rs +++ b/src/uu/dd/src/dd_test.rs @@ -135,7 +135,7 @@ make_spec_test!( atoe_conv_spec_test, "atoe-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(ASCII_TO_EBCDIC), + Some(&ASCII_TO_EBCDIC), File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() ); @@ -143,7 +143,7 @@ make_spec_test!( etoa_conv_spec_test, "etoa-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(EBCDIC_TO_ASCII), + Some(&EBCDIC_TO_ASCII), File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() ); @@ -151,7 +151,7 @@ make_spec_test!( atoibm_conv_spec_test, "atoibm-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(ASCII_TO_IBM), + Some(&ASCII_TO_IBM), File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() ); @@ -159,7 +159,7 @@ make_spec_test!( lcase_ascii_to_ucase_ascii, "lcase_ascii_to_ucase_ascii", File::open("./test-resources/lcase-ascii.test").unwrap(), - Some(ASCII_LCASE_TO_UCASE), + Some(&ASCII_LCASE_TO_UCASE), File::open("./test-resources/ucase-ascii.test").unwrap() ); @@ -167,56 +167,16 @@ make_spec_test!( ucase_ascii_to_lcase_ascii, "ucase_ascii_to_lcase_ascii", File::open("./test-resources/ucase-ascii.test").unwrap(), - Some(ASCII_UCASE_TO_LCASE), + Some(&ASCII_UCASE_TO_LCASE), File::open("./test-resources/lcase-ascii.test").unwrap() ); -// // *** -// make_spec_test!( -// lcase_ebcdic_to_ucase_ebcdic, -// "lcase_ebcdic_to_ucase_ebcdic", -// File::open("./test-resources/lcase-ebcdic.test").unwrap(), -// None, -// Some(EBCDIC_LCASE_TO_UCASE), -// File::open("./test-resources/ucase-ebcdic.test").unwrap() -// ); -// -// // *** -// make_spec_test!( -// ucase_ebcdic_to_lcase_ebcdic, -// "ucase_ebcdic_to_lcase_ebcdic", -// File::open("./test-resources/ucase-ebcdic.test").unwrap(), -// None, -// Some(EBCDIC_UCASE_TO_LCASE), -// File::open("./test-resources/lcase-ebcdic.test").unwrap() -// ); -// -// // *** -// make_spec_test!( -// lcase_ibm_to_ucase_ibm, -// "lcase_ibm_to_ucase_ibm", -// File::open("./test-resources/lcase-ibm.test").unwrap(), -// None, -// Some(IBM_LCASE_TO_UCASE), -// File::open("./test-resources/ucase-ibm.test").unwrap() -// ); -// -// // *** -// make_spec_test!( -// ucase_ibm_to_lcase_ibm, -// "ucase_ibm_to_lcase_ibm", -// File::open("./test-resources/ucase-ibm.test").unwrap(), -// None, -// Some(IBM_UCASE_TO_LCASE), -// File::open("./test-resources/lcase-ibm.test").unwrap() -// ); - make_spec_test!( // conv=ebcdic,ucase atoe_and_ucase_conv_spec_test, "atoe-and-ucase-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(ASCII_TO_EBCDIC_LCASE_TO_UCASE), + Some(&ASCII_TO_EBCDIC_LCASE_TO_UCASE), File::open("./test-resources/ucase-ebcdic.test").unwrap() ); @@ -225,7 +185,7 @@ make_spec_test!( atoe_and_lcase_conv_spec_test, "atoe-and-lcase-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(ASCII_TO_EBCDIC_UCASE_TO_LCASE), + Some(&ASCII_TO_EBCDIC_UCASE_TO_LCASE), File::open("./test-resources/lcase-ebcdic.test").unwrap() ); @@ -234,7 +194,7 @@ make_spec_test!( atoibm_and_ucase_conv_spec_test, "atoibm-and-ucase-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(ASCII_TO_IBM_UCASE_TO_LCASE), + Some(&ASCII_TO_IBM_UCASE_TO_LCASE), File::open("./test-resources/lcase-ibm.test").unwrap() ); @@ -243,7 +203,7 @@ make_spec_test!( atoibm_and_lcase_conv_spec_test, "atoibm-and-lcase-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(ASCII_TO_IBM_LCASE_TO_UCASE), + Some(&ASCII_TO_IBM_LCASE_TO_UCASE), File::open("./test-resources/ucase-ibm.test").unwrap() ); @@ -258,7 +218,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), ibs: 256, xfer_stats: StatusLevel::None, - cf: cfi!(Some(ASCII_TO_EBCDIC)), + cf: cfi!(Some(&ASCII_TO_EBCDIC)), }; let o = Output { @@ -277,7 +237,7 @@ 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)), + cf: cfi!(Some(&EBCDIC_TO_ASCII)), }; let o = Output { diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 8105efc9a..b1f157da6 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -1,8 +1,16 @@ -use super::*; +#[cfg(test)] +mod test; use crate::conversion_tables::*; +use crate::{ + ConvFlagInput, ConvFlagOutput, + StatusLevel, +}; -/// Parser Errors indicate erroneous cl-args +use std::error::Error; + + +/// Parser Errors describe errors with input #[derive(Debug)] pub enum ParseError { @@ -26,6 +34,7 @@ impl Error for ParseError {} /// Some flags specified as part of a conv=CONV[,CONV]... block /// relate to the input file, others to the output file. +#[derive(Debug, PartialEq)] enum ConvFlag { // Input @@ -64,9 +73,9 @@ impl std::str::FromStr for ConvFlag "ibm" => Ok(Self::FmtAtoI), "lcase" => - Ok(Self::UCase), - "ucase" => Ok(Self::LCase), + "ucase" => + Ok(Self::UCase), "block" => Ok(Self::Block), "unblock" => @@ -213,7 +222,7 @@ pub fn parse_obs(matches: &getopts::Matches) -> Result } } -fn parse_ctable(fmt: Option, case: Option) -> Option +fn parse_ctable(fmt: Option, case: Option) -> Option<&'static ConversionTable> { match (fmt, case) { @@ -222,17 +231,17 @@ fn parse_ctable(fmt: Option, case: Option) -> Option - Some(ASCII_TO_EBCDIC_LCASE_TO_UCASE), + Some(&ASCII_TO_EBCDIC_LCASE_TO_UCASE), (ConvFlag::FmtAtoE, ConvFlag::LCase) => - Some(ASCII_TO_EBCDIC_UCASE_TO_LCASE), + Some(&ASCII_TO_EBCDIC_UCASE_TO_LCASE), (ConvFlag::FmtEtoA, ConvFlag::UCase) => - Some(EBCDIC_TO_ASCII_LCASE_TO_UCASE), + Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE), (ConvFlag::FmtEtoA, ConvFlag::LCase) => - Some(EBCDIC_TO_ASCII_UCASE_TO_LCASE), + Some(&EBCDIC_TO_ASCII_UCASE_TO_LCASE), (ConvFlag::FmtAtoI, ConvFlag::UCase) => - Some(ASCII_TO_IBM_UCASE_TO_LCASE), + Some(&ASCII_TO_IBM_UCASE_TO_LCASE), (ConvFlag::FmtAtoI, ConvFlag::LCase) => - Some(ASCII_TO_IBM_LCASE_TO_UCASE), + Some(&ASCII_TO_IBM_LCASE_TO_UCASE), (_, _) => None, }, @@ -241,19 +250,19 @@ fn parse_ctable(fmt: Option, case: Option) -> Option - Some(ASCII_TO_EBCDIC), + Some(&ASCII_TO_EBCDIC), ConvFlag::FmtEtoA => - Some(EBCDIC_TO_ASCII), + Some(&EBCDIC_TO_ASCII), ConvFlag::FmtAtoI => - Some(ASCII_TO_IBM), + Some(&ASCII_TO_IBM), _ => None, }, // Only one of {ucase, lcase} specified (None, Some(ConvFlag::UCase)) => - Some(ASCII_LCASE_TO_UCASE), + Some(&ASCII_LCASE_TO_UCASE), (None, Some(ConvFlag::LCase)) => - Some(ASCII_UCASE_TO_LCASE), + Some(&ASCII_UCASE_TO_LCASE), (_, _) => None, } @@ -265,12 +274,13 @@ fn parse_conv_opts(matches: &getopts::Matches) -> Result, ParseErr if let Some(comma_str) = matches.opt_str("conv") { + println!("Parsing conv: {}", comma_str); for s in comma_str.split(",") { let flag = s.parse()?; + println!("found flag: {:?}", &flag); flags.push(flag); } - } Ok(flags) @@ -421,201 +431,3 @@ pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result - { - #[allow(non_snake_case)] - #[test] - fn $test_name() - { - let bs_str = String::from($bs_str); - assert_eq!($bs, parse_bytes_with_opt_multiplier(bs_str).unwrap()) - } - } - ); - - test_byte_parser!( - test_bytes_n, - "765", - 765 - ); - test_byte_parser!( - test_bytes_c, - "13c", - 13 - ); - - test_byte_parser!( - test_bytes_w, - "1w", - 2 - ); - - test_byte_parser!( - test_bytes_b, - "1b", - 512 - ); - - test_byte_parser!( - test_bytes_k, - "1kB", - 1000 - ); - test_byte_parser!( - test_bytes_K, - "1K", - 1024 - ); - test_byte_parser!( - test_bytes_Ki, - "1KiB", - 1024 - ); - - test_byte_parser!( - test_bytes_MB, - "2MB", - 2*1000*1000 - ); - test_byte_parser!( - test_bytes_M, - "2M", - 2*1024*1024 - ); - test_byte_parser!( - test_bytes_Mi, - "2MiB", - 2*1024*1024 - ); - - test_byte_parser!( - test_bytes_GB, - "3GB", - 3*1000*1000*1000 - ); - test_byte_parser!( - test_bytes_G, - "3G", - 3*1024*1024*1024 - ); - test_byte_parser!( - test_bytes_Gi, - "3GiB", - 3*1024*1024*1024 - ); - - test_byte_parser!( - test_bytes_TB, - "4TB", - 4*1000*1000*1000*1000 - ); - test_byte_parser!( - test_bytes_T, - "4T", - 4*1024*1024*1024*1024 - ); - test_byte_parser!( - test_bytes_Ti, - "4TiB", - 4*1024*1024*1024*1024 - ); - - test_byte_parser!( - test_bytes_PB, - "5PB", - 5*1000*1000*1000*1000*1000 - ); - test_byte_parser!( - test_bytes_P, - "5P", - 5*1024*1024*1024*1024*1024 - ); - test_byte_parser!( - test_bytes_Pi, - "5PiB", - 5*1024*1024*1024*1024*1024 - ); - - test_byte_parser!( - test_bytes_EB, - "6EB", - 6*1000*1000*1000*1000*1000*1000 - ); - test_byte_parser!( - test_bytes_E, - "6E", - 6*1024*1024*1024*1024*1024*1024 - ); - test_byte_parser!( - test_bytes_Ei, - "6EiB", - 6*1024*1024*1024*1024*1024*1024 - ); - - #[test] - #[should_panic] - #[allow(non_snake_case)] - fn test_KB_multiplier_error() - { - // KB is not valid (kB, K, and KiB are) - let bs_str = String::from("2000KB"); - - parse_bytes_with_opt_multiplier(bs_str).unwrap(); - } - - #[test] - #[should_panic] - fn test_overflow_panic() - { - let bs_str = format!("{}KiB", usize::MAX); - - parse_bytes_with_opt_multiplier(bs_str).unwrap(); - } - - #[test] - #[should_panic] - fn test_neg_panic() - { - let bs_str = format!("{}KiB", -1); - - parse_bytes_with_opt_multiplier(bs_str).unwrap(); - } - -} diff --git a/src/uu/dd/src/parseargs/test.rs b/src/uu/dd/src/parseargs/test.rs new file mode 100644 index 000000000..0a1db2868 --- /dev/null +++ b/src/uu/dd/src/parseargs/test.rs @@ -0,0 +1,317 @@ +use super::*; + +use crate::{ + build_app, + SYNTAX, SUMMARY, LONG_HELP, + ConvFlagInput, ConvFlagOutput, + StatusLevel, +}; + +// ----- ConvFlagInput/Output ----- + +#[test] +fn build_cfi() +{ + let cfi_expd = ConvFlagInput { + ctable: Some(&ASCII_TO_IBM), + block: false, + unblock: false, + swab: false, + sync: false, + noerror: false, + }; + + let args = vec![ + String::from("dd"), + String::from("--conv=ibm"), + ]; + + let matches = build_app!().parse(args); + + let cfi_parsed = parse_conv_flag_input(&matches).unwrap(); + + unimplemented!() + // assert_eq!(cfi_expd, cfi_parsed); +} + +#[test] +#[should_panic] +fn cfi_ctable_error() +{ + let args = vec![ + String::from("dd"), + String::from("--conv=ascii,ebcdic,ibm"), + ]; + + let matches = build_app!().parse(args); + + let cfi_parsed = parse_conv_flag_input(&matches).unwrap(); +} + +#[test] +#[should_panic] +fn cfi_case_error() +{ + let args = vec![ + String::from("dd"), + String::from("--conv=ucase,lcase"), + ]; + + let matches = build_app!().parse(args); + + let cfi_parsed = parse_conv_flag_input(&matches).unwrap(); +} + +#[test] +#[should_panic] +fn cfi_block_error() +{ + let args = vec![ + String::from("dd"), + String::from("--conv=block,unblock"), + ]; + + let matches = build_app!().parse(args); + + let cfi_parsed = parse_conv_flag_input(&matches).unwrap(); +} + +#[test] +fn parse_cfi_token_ibm() +{ + let exp = vec![ + ConvFlag::FmtAtoI, + ]; + + let args = vec![ + String::from("dd"), + String::from("--conv=ibm"), + ]; + let matches = build_app!().parse(args); + + let act = parse_conv_opts(&matches).unwrap(); + + assert_eq!(exp.len(), act.len()); + for cf in &exp + { + assert!(exp.contains(&cf)); + } +} + +#[test] +fn parse_cfi_tokens_elu() +{ + let exp = vec![ + ConvFlag::FmtEtoA, + ConvFlag::LCase, + ConvFlag::Unblock, + ]; + + let args = vec![ + String::from("dd"), + String::from("--conv=ebcdic,lcase,unblock"), + ]; + let matches = build_app!().parse(args); + let act = parse_conv_opts(&matches).unwrap(); + + assert_eq!(exp.len(), act.len()); + for cf in &exp + { + assert!(exp.contains(&cf)); + } +} + +#[test] +fn parse_cfi_tokens_remaining() +{ + let exp = vec![ + ConvFlag::FmtAtoE, + ConvFlag::UCase, + ConvFlag::Block, + ConvFlag::Sparse, + ConvFlag::Swab, + ConvFlag::Sync, + ConvFlag::NoError, + ConvFlag::Excl, + ConvFlag::NoCreat, + ConvFlag::NoTrunc, + ConvFlag::NoError, + ConvFlag::FDataSync, + ConvFlag::FSync, + ]; + + let args = vec![ + String::from("dd"), + String::from("--conv=ascii,ucase,block,sparse,swab,sync,noerror,excl,nocreat,notrunc,noerror,fdatasync,fsync"), + ]; + let matches = build_app!().parse(args); + let act = parse_conv_opts(&matches).unwrap(); + + assert_eq!(exp.len(), act.len()); + for cf in &exp + { + assert!(exp.contains(&cf)); + } +} + +// ----- Multiplier Strings etc. ----- +macro_rules! test_byte_parser ( + ( $test_name:ident, $bs_str:expr, $bs:expr ) => + { + #[allow(non_snake_case)] + #[test] + fn $test_name() + { + let bs_str = String::from($bs_str); + assert_eq!($bs, parse_bytes_with_opt_multiplier(bs_str).unwrap()) + } + } +); + +test_byte_parser!( + test_bytes_n, + "765", + 765 +); +test_byte_parser!( + test_bytes_c, + "13c", + 13 +); + +test_byte_parser!( + test_bytes_w, + "1w", + 2 +); + +test_byte_parser!( + test_bytes_b, + "1b", + 512 +); + +test_byte_parser!( + test_bytes_k, + "1kB", + 1000 +); +test_byte_parser!( + test_bytes_K, + "1K", + 1024 +); +test_byte_parser!( + test_bytes_Ki, + "1KiB", + 1024 +); + +test_byte_parser!( + test_bytes_MB, + "2MB", + 2*1000*1000 +); +test_byte_parser!( + test_bytes_M, + "2M", + 2*1024*1024 +); +test_byte_parser!( + test_bytes_Mi, + "2MiB", + 2*1024*1024 +); + +test_byte_parser!( + test_bytes_GB, + "3GB", + 3*1000*1000*1000 +); +test_byte_parser!( + test_bytes_G, + "3G", + 3*1024*1024*1024 +); +test_byte_parser!( + test_bytes_Gi, + "3GiB", + 3*1024*1024*1024 +); + +test_byte_parser!( + test_bytes_TB, + "4TB", + 4*1000*1000*1000*1000 +); +test_byte_parser!( + test_bytes_T, + "4T", + 4*1024*1024*1024*1024 +); +test_byte_parser!( + test_bytes_Ti, + "4TiB", + 4*1024*1024*1024*1024 +); + +test_byte_parser!( + test_bytes_PB, + "5PB", + 5*1000*1000*1000*1000*1000 +); +test_byte_parser!( + test_bytes_P, + "5P", + 5*1024*1024*1024*1024*1024 +); +test_byte_parser!( + test_bytes_Pi, + "5PiB", + 5*1024*1024*1024*1024*1024 +); + +test_byte_parser!( + test_bytes_EB, + "6EB", + 6*1000*1000*1000*1000*1000*1000 +); +test_byte_parser!( + test_bytes_E, + "6E", + 6*1024*1024*1024*1024*1024*1024 +); +test_byte_parser!( + test_bytes_Ei, + "6EiB", + 6*1024*1024*1024*1024*1024*1024 +); + +#[test] +#[should_panic] +#[allow(non_snake_case)] +fn test_KB_multiplier_error() +{ + // KB is not valid (kB, K, and KiB are) + let bs_str = String::from("2000KB"); + + parse_bytes_with_opt_multiplier(bs_str).unwrap(); +} + +#[test] +#[should_panic] +fn test_overflow_panic() +{ + let bs_str = format!("{}KiB", usize::MAX); + + parse_bytes_with_opt_multiplier(bs_str).unwrap(); +} + +#[test] +#[should_panic] +fn test_neg_panic() +{ + let bs_str = format!("{}KiB", -1); + + parse_bytes_with_opt_multiplier(bs_str).unwrap(); +} From 5aabdf854d46c6b5027854f248dd76242da28142 Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 22 Apr 2021 10:56:33 -0700 Subject: [PATCH 10/66] Adds plumbing for conv=sparse. Not impl. - conv=sparse option requires knowledge of File/Stdout to change behaviour. - Unclear how best to impl this. - Possible option: develop 4 versions of dd for each valid pair of { File, Stdin, Stdout }. --- src/uu/dd/src/dd.rs | 91 +++++++++++++++++++++++++++------ src/uu/dd/src/parseargs.rs | 21 ++++++-- src/uu/dd/src/parseargs/test.rs | 14 +++++ 3 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 87949a08e..e6698244b 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -18,14 +18,18 @@ mod parseargs; mod conversion_tables; use conversion_tables::*; +use std::convert::TryInto; use std::error::Error; -use std::fs::File; +use std::fs::{ + File, OpenOptions, +}; +use getopts; use std::io::{ self, Read, Write, + Seek, }; use std::sync::mpsc; use std::thread; -use getopts; const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; const SUMMARY: &str = "convert, and optionally copy, a file"; @@ -218,10 +222,16 @@ impl Output { let obs = parseargs::parse_obs(matches)?; let cf = parseargs::parse_conv_flag_output(matches)?; - if let Some(fname) = matches.opt_str("if") + if let Some(fname) = matches.opt_str("of") { + let dst = OpenOptions::new() + .write(true) + .create(!cf.nocreat) + .truncate(!cf.notrunc) + .open(fname)?; + Ok(Output { - dst: File::open(fname)?, + dst, obs, cf, }) @@ -233,6 +243,32 @@ impl Output { } } +impl Seek for Output +{ + fn seek(&mut self, pos: io::SeekFrom) -> io::Result + { + unimplemented!() + } +} + +// 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 @@ -246,6 +282,11 @@ impl Write for Output } } +fn is_sparse(buf: &[u8]) -> bool +{ + buf.iter().all(| &e | e == 0) +} + fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { move || { @@ -290,22 +331,31 @@ fn dd(mut i: Input, mut o: Output) -> Result<(usize, us loop { let mut buf = vec![DEFAULT_FILL_BYTE; o.obs]; - let r_len = - match i.fill_n(&mut buf, o.obs)? { - SrcStat::Read(len) => - { - bytes_in += len; - len - }, - SrcStat::EOF => - break, + + // Read + let r_len = match i.fill_n(&mut buf, o.obs)? { + SrcStat::Read(len) => + { + bytes_in += len; + len + }, + SrcStat::EOF => + break, }; - let w_len = o.write(&buf[..r_len])?; - - // TODO: Some flag (sync?) controls this behaviour - // o.flush()?; + // Write + let w_len = if o.cf.sparse && is_sparse(&buf) + { + let seek_amt: i64 = r_len.try_into()?; + o.seek(io::SeekFrom::Current(seek_amt))?; + r_len + } + else + { + o.write(&buf[..r_len])? + }; + // Prog bytes_out += w_len; if let Some(prog_tx) = &prog_tx @@ -314,6 +364,13 @@ 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 + { + o.flush()?; + } + Ok((bytes_in, bytes_out)) } diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index b1f157da6..29f643e90 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -17,6 +17,7 @@ pub enum ParseError MultipleFmtTable, MultipleUCaseLCase, MultipleBlockUnblock, + MultipleExclNoCreat, ConvFlagNoMatch(String), NoMatchingMultiplier(String), MultiplierStringContainsNoValue(String), @@ -274,11 +275,9 @@ fn parse_conv_opts(matches: &getopts::Matches) -> Result, ParseErr if let Some(comma_str) = matches.opt_str("conv") { - println!("Parsing conv: {}", comma_str); for s in comma_str.split(",") { let flag = s.parse()?; - println!("found flag: {:?}", &flag); flags.push(flag); } } @@ -409,9 +408,23 @@ pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result sparse = true, ConvFlag::Excl => - excl = true, + if !nocreat + { + excl = true; + } + else + { + return Err(ParseError::MultipleExclNoCreat); + }, ConvFlag::NoCreat => - nocreat = true, + if !excl + { + nocreat = true; + } + else + { + return Err(ParseError::MultipleExclNoCreat); + }, ConvFlag::NoTrunc => notrunc = true, ConvFlag::FDataSync => diff --git a/src/uu/dd/src/parseargs/test.rs b/src/uu/dd/src/parseargs/test.rs index 0a1db2868..491d3f335 100644 --- a/src/uu/dd/src/parseargs/test.rs +++ b/src/uu/dd/src/parseargs/test.rs @@ -76,6 +76,20 @@ fn cfi_block_error() let cfi_parsed = parse_conv_flag_input(&matches).unwrap(); } +#[test] +#[should_panic] +fn cfi_creat_error() +{ + let args = vec![ + String::from("dd"), + String::from("--conv=excl,nocreat"), + ]; + + let matches = build_app!().parse(args); + + let cfi_parsed = parse_conv_flag_output(&matches).unwrap(); +} + #[test] fn parse_cfi_token_ibm() { From beb7abcff01544ee055ec430aac4f8a9ba2a7d32 Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 22 Apr 2021 13:19:51 -0700 Subject: [PATCH 11/66] Implements swab. Adds odd & even tests. --- src/uu/dd/src/dd.rs | 12 ++ src/uu/dd/src/dd_test.rs | 156 ++++++++++++------ .../seq-byte-values-swapped.test | Bin 256 -> 256 bytes 3 files changed, 116 insertions(+), 52 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index e6698244b..51f82c0f6 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -118,6 +118,18 @@ impl Read for Input } } + if self.cf.swab + { + let mut tmp = DEFAULT_FILL_BYTE; + + for base in (1..len).step_by(2) + { + tmp = buf[base]; + buf[base] = buf[base-1]; + buf[base-1] = tmp; + } + } + Ok(len) } } diff --git a/src/uu/dd/src/dd_test.rs b/src/uu/dd/src/dd_test.rs index c2167a5e6..d73a9ede2 100644 --- a/src/uu/dd/src/dd_test.rs +++ b/src/uu/dd/src/dd_test.rs @@ -41,35 +41,35 @@ macro_rules! make_spec_test ( ( $test_id:ident, $test_name:expr, $src:expr ) => { // When spec not given, output should match input - make_spec_test!($test_id, $test_name, $src, None, $src); + make_spec_test!($test_id, $test_name, $src, $src); }; ( $test_id:ident, $test_name:expr, $src:expr, $spec:expr ) => { - make_spec_test!($test_id, $test_name, $src, None, $spec); + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + ibs: 512, + xfer_stats: StatusLevel::None, + cf: cfi!(), + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cf: DEFAULT_CFO, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); }; - ( $test_id:ident, $test_name:expr, $src:expr, $ctable:expr, $spec:expr ) => + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr, $tmp_fname:expr ) => { #[test] fn $test_id() { - let tmp_fname = format!("./test-resources/FAILED-{}.test", $test_name); + dd($i,$o).unwrap(); - let i = Input { - src: $src, - ibs: 512, - xfer_stats: StatusLevel::None, - cf: cfi!($ctable), - }; - - let o = Output { - dst: File::create(&tmp_fname).unwrap(), - obs: 512, - cf: DEFAULT_CFO, - }; - - dd(i,o).unwrap(); - - let res = File::open(&tmp_fname).unwrap(); + let res = File::open($tmp_fname).unwrap(); let res = BufReader::new(res); let spec = BufReader::new($spec); @@ -80,34 +80,56 @@ macro_rules! make_spec_test ( b_spec.unwrap()); } - fs::remove_file(&tmp_fname).unwrap(); + fs::remove_file($tmp_fname).unwrap(); } }; ); -#[test] -fn test_input_parser() -{ - let args = vec![ - String::from("ketchup"), - String::from("mustard"), - String::from("--conv=ibm"), - String::from("relish"), - ]; +macro_rules! make_conv_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $ctable:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + ibs: 512, + xfer_stats: StatusLevel::None, + cf: cfi!($ctable), + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cf: DEFAULT_CFO, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); - let matches = build_app!().parse(args); - // ... +macro_rules! make_cfi_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $cfi:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + ibs: 512, + xfer_stats: StatusLevel::None, + cf: $cfi, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cf: DEFAULT_CFO, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); - unimplemented!() -} - -#[test] -fn test_output_parser() -{ - unimplemented!() -} - - make_spec_test!( +make_spec_test!( zeros_4k_test, "zeros-4k", File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap() @@ -131,7 +153,7 @@ make_spec_test!( File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); -make_spec_test!( +make_conv_test!( atoe_conv_spec_test, "atoe-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), @@ -139,7 +161,7 @@ make_spec_test!( File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() ); -make_spec_test!( +make_conv_test!( etoa_conv_spec_test, "etoa-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), @@ -147,7 +169,7 @@ make_spec_test!( File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() ); -make_spec_test!( +make_conv_test!( atoibm_conv_spec_test, "atoibm-conv-spec-test", File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), @@ -155,7 +177,7 @@ make_spec_test!( File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() ); -make_spec_test!( +make_conv_test!( lcase_ascii_to_ucase_ascii, "lcase_ascii_to_ucase_ascii", File::open("./test-resources/lcase-ascii.test").unwrap(), @@ -163,7 +185,7 @@ make_spec_test!( File::open("./test-resources/ucase-ascii.test").unwrap() ); -make_spec_test!( +make_conv_test!( ucase_ascii_to_lcase_ascii, "ucase_ascii_to_lcase_ascii", File::open("./test-resources/ucase-ascii.test").unwrap(), @@ -171,7 +193,7 @@ make_spec_test!( File::open("./test-resources/lcase-ascii.test").unwrap() ); -make_spec_test!( +make_conv_test!( // conv=ebcdic,ucase atoe_and_ucase_conv_spec_test, "atoe-and-ucase-conv-spec-test", @@ -180,7 +202,7 @@ make_spec_test!( File::open("./test-resources/ucase-ebcdic.test").unwrap() ); -make_spec_test!( +make_conv_test!( // conv=ebcdic,lcase atoe_and_lcase_conv_spec_test, "atoe-and-lcase-conv-spec-test", @@ -189,7 +211,7 @@ make_spec_test!( File::open("./test-resources/lcase-ebcdic.test").unwrap() ); -make_spec_test!( +make_conv_test!( // conv=ibm,ucase atoibm_and_ucase_conv_spec_test, "atoibm-and-ucase-conv-spec-test", @@ -198,7 +220,7 @@ make_spec_test!( File::open("./test-resources/lcase-ibm.test").unwrap() ); -make_spec_test!( +make_conv_test!( // conv=ibm,lcase atoibm_and_lcase_conv_spec_test, "atoibm-and-lcase-conv-spec-test", @@ -216,7 +238,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let i = Input { src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), - ibs: 256, + ibs: 128, xfer_stats: StatusLevel::None, cf: cfi!(Some(&ASCII_TO_EBCDIC)), }; @@ -266,3 +288,33 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() fs::remove_file(&tmp_fname_ae).unwrap(); fs::remove_file(&tmp_fname_ea).unwrap(); } + +make_cfi_test!( + swab_256_test, + "swab-256", + File::open("./test-resources/seq-byte-values.test").unwrap(), + ConvFlagInput { + ctable: None, + block: false, + unblock: false, + swab: true, + sync: false, + noerror: false, + }, + File::open("./test-resources/seq-byte-values-swapped.test").unwrap() +); + +make_cfi_test!( + swab_257_test, + "swab-257", + File::open("./test-resources/seq-byte-values-odd.test").unwrap(), + ConvFlagInput { + ctable: None, + block: false, + unblock: false, + swab: true, + sync: false, + noerror: false, + }, + File::open("./test-resources/seq-byte-values-odd.spec").unwrap() +); diff --git a/src/uu/dd/test-resources/seq-byte-values-swapped.test b/src/uu/dd/test-resources/seq-byte-values-swapped.test index c86626638e0bc8cf47ca49bb1525b40e9737ee64..be75933c8be3ad3965a405ea2d0ccb625110f055 100644 GIT binary patch literal 256 zcmZQ%U}j=vVQ1sy;O64x;pY<+5Ec>@5f_t`kd~5_k(X0cP*ze^QCHK{(ALt`(bqFH zFg7wZF*mccu(qcc@%IZ12o4Gj2@i{mh>nVliH}Q6NKQ&k zNl(kn$j-{m$aT7=F=93Ve+J@Q>IUw zIb-&$xpU^vTex8HqNPigFI%}{^{TaN*00;RVe_V~Teff8xnuXPy?gfWJ9yymp`%BR zA3J&C^r^FF&Y!z@;qs-cSFT^XdE@r2yLay2d-&k-qo+@vKYRJ&^{cmU-oN|!;q#}j RU%r3)`Q!JmzkmM!0|2gee}Mo1 literal 256 zcmZQzWMXDvWn<^yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v` Date: Thu, 22 Apr 2021 14:14:32 -0700 Subject: [PATCH 12/66] Implements noerror (ignores read errors) --- src/uu/dd/src/dd.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 51f82c0f6..50c66a639 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -345,14 +345,20 @@ 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)? { - SrcStat::Read(len) => + let r_len = match i.fill_n(&mut buf, o.obs) { + Ok(SrcStat::Read(len)) => { bytes_in += len; len }, - SrcStat::EOF => + Ok(SrcStat::EOF) => break, + Err(e) => + if !i.cf.noerror { + return Err(e); + } else { + continue + }, }; // Write From 4d7be2f098a24c9e39ea524798199a8738348fe8 Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 26 Apr 2021 15:17:48 -0700 Subject: [PATCH 13/66] 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, From d97672dfd3197de9148c6aa259c582147b6ee21f Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 27 Apr 2021 12:21:35 -0700 Subject: [PATCH 14/66] Continues impl - Completes impl of skip=N, and seek=N - Parses cbs=N --- src/uu/dd/src/dd.rs | 48 +++++++++--- src/uu/dd/src/dd_test.rs | 8 ++ src/uu/dd/src/parseargs.rs | 70 +++++++++++++----- src/uu/dd/src/parseargs/test.rs | 8 +- .../test-resources/seq-byte-values-odd.spec | Bin 0 -> 257 bytes .../test-resources/seq-byte-values-odd.test | Bin 0 -> 257 bytes src/uu/dd/test-resources/seq-byte-values.test | Bin 0 -> 256 bytes 7 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 src/uu/dd/test-resources/seq-byte-values-odd.spec create mode 100644 src/uu/dd/test-resources/seq-byte-values-odd.test create mode 100644 src/uu/dd/test-resources/seq-byte-values.test diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 4efa156ab..f36ac487b 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -36,6 +36,7 @@ const SUMMARY: &str = "convert, and optionally copy, a file"; const LONG_HELP: &str = ""; const DEFAULT_FILL_BYTE: u8 = 0xDD; +const DEFAULT_SKIP_TRIES: u8 = 3; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; @@ -51,6 +52,7 @@ enum SrcStat pub struct IConvFlags { ctable: Option<&'static ConversionTable>, + cbs: Option, block: bool, unblock: bool, swab: bool, @@ -131,7 +133,12 @@ enum InternalError impl std::fmt::Display for InternalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Internal dd error") + match self + { + Self::WrongInputType | + Self::WrongOutputType => + write!(f, "Internal dd error"), + } } } @@ -184,7 +191,7 @@ impl Input let xfer_stats = parseargs::parse_status_level(matches)?; let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; - let skip = parseargs::parse_skip_amt(matches)?; + let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; let mut i = Input { src: io::stdin(), @@ -194,10 +201,11 @@ impl Input iflags, }; - if let Some(skip_amt) = skip + if let Some(amt) = skip { - let mut buf = vec![DEFAULT_FILL_BYTE; skip_amt]; - i.read(&mut buf)?; + let mut buf = vec![DEFAULT_FILL_BYTE; amt]; + + i.force_fill(&mut buf, amt)?; } Ok(i) @@ -212,16 +220,16 @@ impl Input let xfer_stats = parseargs::parse_status_level(matches)?; let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; - let skip = parseargs::parse_skip_amt(matches)?; + let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; if let Some(fname) = matches.opt_str("if") { let mut src = File::open(fname)?; - if let Some(skip_amt) = skip + if let Some(amt) = skip { - let skip_amt: u64 = skip_amt.try_into()?; - src.seek(io::SeekFrom::Start(skip_amt))?; + let amt: u64 = amt.try_into()?; + src.seek(io::SeekFrom::Start(amt))?; } let i = Input { @@ -266,6 +274,21 @@ impl Input Ok(SrcStat::EOF) } } + + fn force_fill(&mut self, mut buf: &mut [u8], len: usize) -> Result<(), Box> + { + let mut total_len = 0; + + loop + { + total_len += self.read(&mut buf)?; + + if total_len == len + { + return Ok(()); + } + } + } } struct Output @@ -297,8 +320,8 @@ impl Output { { let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; - let seek = parseargs::parse_seek_amt(matches)?; let oflags = parseargs::parse_oflags(matches)?; + let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; if let Some(fname) = matches.opt_str("of") { @@ -308,9 +331,10 @@ impl Output { .truncate(!cflags.notrunc) .open(fname)?; - if let Some(seek_amt) = seek + if let Some(amt) = seek { - dst.seek(io::SeekFrom::Start(seek_amt))?; + let amt: u64 = amt.try_into()?; + dst.seek(io::SeekFrom::Start(amt))?; } Ok(Output { diff --git a/src/uu/dd/src/dd_test.rs b/src/uu/dd/src/dd_test.rs index 9dd1d1333..ee30b8907 100644 --- a/src/uu/dd/src/dd_test.rs +++ b/src/uu/dd/src/dd_test.rs @@ -65,6 +65,7 @@ macro_rules! icf ( { IConvFlags { ctable: $ctable, + cbs: None, block: false, unblock: false, swab: false, @@ -95,6 +96,7 @@ macro_rules! make_spec_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -139,6 +141,7 @@ macro_rules! make_conv_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -162,6 +165,7 @@ macro_rules! make_icf_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -288,6 +292,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() dst: File::create(&tmp_fname_ae).unwrap(), obs: 1024, cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, }; dd_fileout(i,o).unwrap(); @@ -308,6 +313,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() dst: File::create(&tmp_fname_ea).unwrap(), obs: 1024, cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, }; dd_fileout(i,o).unwrap(); @@ -337,6 +343,7 @@ make_icf_test!( File::open("./test-resources/seq-byte-values.test").unwrap(), IConvFlags { ctable: None, + cbs: None, block: false, unblock: false, swab: true, @@ -352,6 +359,7 @@ make_icf_test!( File::open("./test-resources/seq-byte-values-odd.test").unwrap(), IConvFlags { ctable: None, + cbs: None, block: false, unblock: false, swab: true, diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 4605bf6cf..b18054f14 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -23,7 +23,7 @@ pub enum ParseError FlagNoMatch(String), ConvFlagNoMatch(String), NoMatchingMultiplier(String), - MultiplierStringContainsNoValue(String), + ByteStringContainsNoValue(String), MultiplierStringWouldOverflow(String), } @@ -235,15 +235,21 @@ fn parse_multiplier<'a>(s: &'a str) -> Result } } +fn parse_bytes_only(s: &str) -> Result +{ + let bytes: usize = match s.parse() + { + Ok(val) => val, + Err(_) => return Err(ParseError::ByteStringContainsNoValue(String::from(s))), + }; + Ok(bytes) +} + fn parse_bytes_with_opt_multiplier(s: String) -> Result { if let Some(idx) = s.find(char::is_alphabetic) { - let base: usize = match s[0..idx].parse() - { - Ok(val) => val, - Err(_) => return Err(ParseError::MultiplierStringContainsNoValue(s)), - }; + let base = parse_bytes_only(&s[0..idx])?; let mult = parse_multiplier(&s[idx..])?; if let Some(bytes) = base.checked_mul(mult) @@ -257,12 +263,7 @@ fn parse_bytes_with_opt_multiplier(s: String) -> Result } else { - let bytes: usize = match s.parse() - { - Ok(val) => val, - Err(_) => return Err(ParseError::MultiplierStringContainsNoValue(s)), - }; - Ok(bytes) + parse_bytes_only(&s) } } @@ -282,6 +283,19 @@ pub fn parse_ibs(matches: &getopts::Matches) -> Result } } +fn parse_cbs(matches: &getopts::Matches) -> Result, ParseError> +{ + if let Some(s) = matches.opt_str("cbs") + { + let bytes = parse_bytes_with_opt_multiplier(s)?; + Ok(Some(bytes)) + } + else + { + Ok(None) + } +} + pub fn parse_status_level(matches: &getopts::Matches) -> Result { // TODO: Impl @@ -371,6 +385,7 @@ fn parse_flag_list>(tag: &str, matches: & pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result { let flags = parse_flag_list("conv", matches)?; + let cbs = parse_cbs(matches)?; let mut fmt = None; let mut case = None; @@ -461,6 +476,7 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result Result } /// Parse the amount of the input file to skip. -pub fn parse_skip_amt(matches: &getopts::Matches) -> Result, ParseError> +pub fn parse_skip_amt(ibs: &usize, iflags: &IFlags, matches: &getopts::Matches) -> Result, ParseError> { - if let Some(skip_amt) = matches.opt_str("skip") + if let Some(amt) = matches.opt_str("skip") { - unimplemented!() + if iflags.skip_bytes + { + let n = parse_bytes_with_opt_multiplier(amt)?; + Ok(Some(n)) + } + else + { + let n = parse_bytes_only(&amt)?; + Ok(Some(ibs*n)) + } } else { @@ -704,11 +729,20 @@ pub fn parse_skip_amt(matches: &getopts::Matches) -> Result, Parse } /// Parse the amount of the output file to seek. -pub fn parse_seek_amt(matches: &getopts::Matches) -> Result, ParseError> +pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &getopts::Matches) -> Result, ParseError> { - if let Some(seek_amt) = matches.opt_str("seek") + if let Some(amt) = matches.opt_str("seek") { - unimplemented!() + if oflags.seek_bytes + { + let n = parse_bytes_with_opt_multiplier(amt)?; + Ok(Some(n)) + } + else + { + let n = parse_bytes_only(&amt)?; + Ok(Some(obs*n)) + } } else { diff --git a/src/uu/dd/src/parseargs/test.rs b/src/uu/dd/src/parseargs/test.rs index 390622a12..5587c1c4b 100644 --- a/src/uu/dd/src/parseargs/test.rs +++ b/src/uu/dd/src/parseargs/test.rs @@ -14,6 +14,7 @@ fn build_icf() { let icf_expd = IConvFlags { ctable: Some(&ASCII_TO_IBM), + cbs: None, block: false, unblock: false, swab: false, @@ -103,7 +104,7 @@ fn parse_icf_token_ibm() ]; let matches = build_app!().parse(args); - let act = parse_conv_opts(&matches).unwrap(); + let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); for cf in &exp @@ -126,7 +127,7 @@ fn parse_icf_tokens_elu() String::from("--conv=ebcdic,lcase,unblock"), ]; let matches = build_app!().parse(args); - let act = parse_conv_opts(&matches).unwrap(); + let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); for cf in &exp @@ -159,7 +160,8 @@ fn parse_icf_tokens_remaining() String::from("--conv=ascii,ucase,block,sparse,swab,sync,noerror,excl,nocreat,notrunc,noerror,fdatasync,fsync"), ]; let matches = build_app!().parse(args); - let act = parse_conv_opts(&matches).unwrap(); + + let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); for cf in &exp diff --git a/src/uu/dd/test-resources/seq-byte-values-odd.spec b/src/uu/dd/test-resources/seq-byte-values-odd.spec new file mode 100644 index 0000000000000000000000000000000000000000..2952a28a1779ad29836d61704372bd9feea7e4a5 GIT binary patch literal 257 zcmZQ%U}j=vVQ1sy;O64x;pY<+5Ec>@5f_t`kd~5_k(X0cP*ze^QCHK{(ALt`(bqFH zFg7wZF*mccu(qcc@%IZ12o4Gj2@i{mh>nVliH}Q6NKQ&k zNl(kn$j-{m$aT7=F=93Ve+J@Q>IUw zIb-&$xpU^vTex8HqNPigFI%}{^{TaN*00;RVe_V~Teff8xnuXPy?gfWJ9yymp`%BR zA3J&C^r^FF&Y!z@;qs-cSFT^XdE@r2yLay2d-&k-qo+@vKYRJ&^{cmU-oN|!;q#}j SU%r3)`Q!JmzkmM!y9)p=MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v`MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v` Date: Thu, 27 May 2021 15:55:29 -0700 Subject: [PATCH 15/66] Refactor. Returns code to buildable - Pushes file/stdout specific logic to trait objects. - Extracts read/write helper - Extracts conv/block/unblock helper - Impl block - WIP: Many failing tests! --- src/uu/dd/src/dd.rs | 485 ++++++++++++++++++++++++-------- src/uu/dd/src/dd_test.rs | 28 +- src/uu/dd/src/parseargs.rs | 60 ++-- src/uu/dd/src/parseargs/test.rs | 5 +- 4 files changed, 424 insertions(+), 154 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index f36ac487b..304236915 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -52,9 +52,8 @@ enum SrcStat pub struct IConvFlags { ctable: Option<&'static ConversionTable>, - cbs: Option, - block: bool, - unblock: bool, + block: Option, + unblock: Option, swab: bool, sync: bool, noerror: bool, @@ -128,6 +127,7 @@ enum InternalError { WrongInputType, WrongOutputType, + InvalidConvBlockUnblockCase, } impl std::fmt::Display for InternalError @@ -137,7 +137,9 @@ impl std::fmt::Display for InternalError { Self::WrongInputType | Self::WrongOutputType => - write!(f, "Internal dd error"), + write!(f, "Internal dd error: Wrong Input/Output data type"), + Self::InvalidConvBlockUnblockCase => + write!(f, "Internal dd error: Invalid Conversion, Block, or Unblock data"), } } } @@ -147,47 +149,19 @@ impl Error for InternalError {} struct Input { src: R, + non_ascii: bool, ibs: usize, xfer_stats: StatusLevel, cflags: IConvFlags, iflags: IFlags, } -impl Read for Input -{ - fn read(&mut self, mut buf: &mut [u8]) -> io::Result - { - let len = self.src.read(&mut buf)?; - - if let Some(ct) = self.cflags.ctable - { - for idx in 0..len - { - buf[idx] = ct[buf[idx] as usize]; - } - } - - if self.cflags.swab - { - let mut tmp = DEFAULT_FILL_BYTE; - - for base in (1..len).step_by(2) - { - tmp = buf[base]; - buf[base] = buf[base-1]; - buf[base-1] = tmp; - } - } - - Ok(len) - } -} - impl Input { fn new(matches: &getopts::Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; + let non_ascii = parseargs::parse_input_non_ascii(matches)?; let xfer_stats = parseargs::parse_status_level(matches)?; let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; @@ -195,6 +169,7 @@ impl Input let mut i = Input { src: io::stdin(), + non_ascii, ibs, xfer_stats, cflags, @@ -217,6 +192,7 @@ impl Input fn new(matches: &getopts::Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; + let non_ascii = parseargs::parse_input_non_ascii(matches)?; let xfer_stats = parseargs::parse_status_level(matches)?; let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; @@ -234,6 +210,7 @@ impl Input let i = Input { src, + non_ascii, ibs, xfer_stats, cflags, @@ -250,13 +227,42 @@ impl Input } } +impl Read for Input +{ + fn read(&mut self, mut buf: &mut [u8]) -> io::Result + { + // Read from source, ignore read errors if conv=noerror + let len = match self.src.read(&mut buf) + { + Ok(len) => + len, + Err(e) => + if !self.cflags.noerror + { + return Err(e); + } + else + { + return Ok(0); + }, + }; + + Ok(len) + } +} + impl Input { - fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result> + /// Fills to a given size n, which is expected to be 'obs'. + /// Reads in increments of 'self.ibs'. + fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result> { let ibs = self.ibs; let mut bytes_read = 0; + // TODO: Fix this! + assert!(obs > ibs); + for n in 0..(obs/ibs) { // fill an ibs-len slice from src let this_read = self.read(&mut buf[n*ibs..(n+1)*ibs])?; @@ -268,14 +274,13 @@ impl Input } } - if bytes_read != 0 { - Ok(SrcStat::Read(bytes_read)) - } else { - Ok(SrcStat::EOF) - } - } + Ok(bytes_read) + } - fn force_fill(&mut self, mut buf: &mut [u8], len: usize) -> Result<(), Box> + /// Force-fills a buffer, ignoring zero-length reads which would otherwise be + /// interpreted as EOF. Does not continue after errors. + /// Note: This may never return. + fn force_fill(&mut self, mut buf: &mut [u8], target_len: usize) -> Result<(), Box> { let mut total_len = 0; @@ -283,12 +288,13 @@ impl Input { total_len += self.read(&mut buf)?; - if total_len == len + if total_len == target_len { return Ok(()); } } } + } struct Output @@ -313,6 +319,16 @@ impl Output { oflags, }) } + + fn fsync(&mut self) -> io::Result<()> + { + self.dst.flush() + } + + fn fdatasync(&mut self) -> io::Result<()> + { + self.dst.flush() + } } impl Output { @@ -346,9 +362,25 @@ impl Output { } else { + // The following error should only occur if someone + // mistakenly calls Output::::new() without checking + // if 'of' has been provided. In this case, + // Output::::new() is probably intended. Err(Box::new(InternalError::WrongOutputType)) } } + + fn fsync(&mut self) -> io::Result<()> + { + self.dst.flush()?; + self.dst.sync_all() + } + + fn fdatasync(&mut self) -> io::Result<()> + { + self.dst.flush()?; + self.dst.sync_data() + } } impl Seek for Output @@ -359,11 +391,29 @@ impl Seek for Output } } -impl Write for Output +impl Write for Output { fn write(&mut self, buf: &[u8]) -> io::Result { + #[inline] + fn is_sparse(buf: &[u8]) -> bool + { + buf.iter() + .all(|&e| e == 0u8) + } + // ----------------------------- + if self.cflags.sparse && is_sparse(buf) + { + let seek_amt: i64 = buf.len() + .try_into() + .expect("Internal dd Error: Seek amount greater than signed 64-bit integer"); + self.dst.seek(io::SeekFrom::Current(seek_amt))?; + Ok(buf.len()) + } + else + { self.dst.write(buf) + } } fn flush(&mut self) -> io::Result<()> @@ -372,9 +422,17 @@ impl Write for Output } } -fn is_sparse(buf: &[u8]) -> bool +impl Write for Output { - buf.iter().all(| &e | e == 0) + fn write(&mut self, buf: &[u8]) -> io::Result + { + self.dst.write(buf) + } + + fn flush(&mut self) -> io::Result<()> + { + self.dst.flush() + } } fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () @@ -400,22 +458,243 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -#[inline] -fn dd_read_helper(mut buf: &mut [u8], i: &mut Input, o: &Output) -> Result> +fn pad(buf: &mut Vec, padding: u8) { - match i.fill_n(&mut buf, o.obs) + unimplemented!() +} + +/// Splits the content of buf into cbs-length blocks +/// Appends padding as specified by conv=block and cbs=N +/// +/// Example cases: +/// +/// [a...b] -> [a...b] +/// [a...b'\n'c...d] -> [a...b' '...], [c...d] +/// [a...b'\n'c...d'\n'e...] -> [a...b' '...], [c...d' '...], [e...] +/// +/// [a...b'\n'] -> [a...b' '] +/// ['\n'a...b] -> [' '...], [a...b] +/// +/// ['\n'a...b] -> [' '...], [a...b] +/// ['\n''\n'a...b] -> [' '...], [' '...], [a...b] +// +fn block(buf: &[u8], cbs: usize) -> Vec> +{ + buf.split(| &c | c == '\n' as u8) + .fold(Vec::new(), | mut blocks, split | { + let mut block = Vec::with_capacity(cbs); + block.extend(split); + pad(&mut block, ' ' as u8); + blocks.push(block); + + blocks + }) +} + +// Trims padding from each cbs-length partition of buf +// as specified by conv=unblock and cbs=N +fn unblock(buf: &[u8], cbs: usize) +{ + unimplemented!() +} + +#[inline] +fn apply_ct(buf: &mut [u8], ct: &ConversionTable) +{ + for idx in 0..buf.len() { - Ok(ss) => - Ok(ss), - Err(e) => - if !i.cflags.noerror + buf[idx] = ct[buf[idx] as usize]; + } +} + +#[inline] +fn perform_swab(buf: &mut [u8]) +{ + let mut tmp; + + for base in (1..buf.len()).step_by(2) + { + tmp = buf[base]; + buf[base] = buf[base-1]; + buf[base-1] = tmp; + } +} + +fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output) -> Result, Box> +{ + #[inline] + fn should_block_then_conv(i: &Input) -> bool + { + !i.non_ascii + && i.cflags.block.is_some() + } + #[inline] + fn should_conv_then_block(i: &Input) -> bool + { + i.non_ascii + && i.cflags.block.is_some() + } + #[inline] + fn should_unblock_then_conv(i: &Input) -> bool + { + !i.non_ascii + && i.cflags.unblock.is_some() + } + #[inline] + fn should_conv_then_unblock(i: &Input) -> bool + { + i.non_ascii + && i.cflags.unblock.is_some() + } + fn conv_only(i: &Input) -> bool + { + i.cflags.ctable.is_some() + && i.cflags.block.is_none() + && i.cflags.unblock.is_none() + } + // -------------------------------------------------------------------- + if conv_only(&i) + { // no block/unblock + let ct = i.cflags.ctable.unwrap(); + apply_ct(&mut buf, &ct); + + Ok(buf) + } + else if should_block_then_conv(&i) + { // ascii input so perform the block first + let cbs = i.cflags.block.unwrap(); + + let mut blocks = block(&mut buf, cbs); + + if let Some(ct) = i.cflags.ctable + { + for buf in blocks.iter_mut() { - return Err(e); + apply_ct(buf, &ct); } - else - { - Ok(SrcStat::Read(0)) - }, + } + + let blocks = blocks.into_iter() + .flatten() + .collect(); + + Ok(blocks) + } + else if should_conv_then_block(&i) + { // Non-ascii so perform the conversion first + let cbs = i.cflags.block.unwrap(); + + if let Some(ct) = i.cflags.ctable + { + apply_ct(&mut buf, &ct); + } + + let blocks = block(&mut buf, cbs); + + let blocks = blocks.into_iter() + .flatten() + .collect(); + + Ok(blocks) + } + else if should_unblock_then_conv(&i) + { // ascii input so perform the unblock first + let cbs = i.cflags.unblock.unwrap(); + + unblock(&mut buf, cbs); + + if let Some(ct) = i.cflags.ctable + { + apply_ct(&mut buf, &ct); + } + + Ok(buf) + } + else if should_conv_then_unblock(&i) + { // Non-ascii input so perform the conversion first + let cbs = i.cflags.unblock.unwrap(); + + if let Some(ct) = i.cflags.ctable + { + apply_ct(&mut buf, &ct); + } + + unblock(&buf, cbs); + + Ok(buf) + } + else + { + // The following error should not happen, as it results from + // insufficient command line data. This case should be caught + // by the parser before making it this far. + // Producing this error is an alternative to risking an unwrap call + // on 'cbs' if the required data is not provided. + Err(Box::new(InternalError::InvalidConvBlockUnblockCase)) + } +} + +fn read_write_helper(i: &mut Input, o: &mut Output) -> Result<(usize, Vec), Box> +{ + #[inline] + fn is_fast_read(i: &Input, o: &Output) -> bool + { + i.ibs == o.obs + && !is_conv(i) + && !is_block(i) + && !is_unblock(i) + && !i.cflags.swab + } + #[inline] + fn is_conv(i: &Input) -> bool + { + i.cflags.ctable.is_some() + } + #[inline] + fn is_block(i: &Input) -> bool + { + i.cflags.block.is_some() + } + #[inline] + fn is_unblock(i: &Input) -> bool + { + i.cflags.unblock.is_some() + } + // -------------------------------------------------------------------- + if is_fast_read(&i, &o) + { + // TODO: fast reads are copies performed + // directly to output (without creating any buffers) + // as mentioned in the dd spec. + unimplemented!() + } + else + { + // Read + let mut buf = Vec::with_capacity(o.obs); + let rlen = i.fill_n(&mut buf, o.obs)?; + + if rlen == 0 + { + return Ok((0,Vec::new())); + } + + + // Conv etc... + if i.cflags.swab + { + perform_swab(&mut buf[..rlen]); + } + + if is_conv(&i) || is_block(&i) || is_unblock(&i) + { + let buf = conv_block_unblock_helper(buf, i, o)?; + Ok((rlen, buf)) + } + else + { + Ok((rlen, buf)) + } } } @@ -424,12 +703,13 @@ fn dd_read_helper(mut buf: &mut [u8], i: &mut Input, o: &O // and should be fixed in the future. fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> { + let mut bytes_in = 0; + let mut bytes_out = 0; + 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 @@ -437,42 +717,35 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usi 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)? + match read_write_helper(&mut i, &mut o)? { - SrcStat::Read(0) => - continue, - SrcStat::Read(len) => - { - bytes_in += len; - len - }, - SrcStat::EOF => + (0, _) => break, + (rlen, buf) => + { + let wlen = o.write(&buf)?; + + bytes_in += rlen; + bytes_out += wlen; + }, }; - // Write - 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 + if o.cflags.fsync { - o.flush()?; + o.fsync()?; + } + else if o.cflags.fdatasync + { + o.fdatasync()?; } Ok((bytes_in, bytes_out)) @@ -483,12 +756,13 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usi // and should be fixed in the future. fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, usize), Box> { + let mut bytes_in = 0; + let mut bytes_out = 0; + 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 @@ -496,43 +770,22 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, u 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)? + match read_write_helper(&mut i, &mut o)? { - SrcStat::Read(0) => - continue, - SrcStat::Read(len) => - { - bytes_in += len; - len - }, - SrcStat::EOF => + (0, _) => break, - }; + (rlen, buf) => + { + let wlen = o.write(&buf)?; - - // 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))?; - r_len - } - else - { - o.write(&buf[..r_len])? + bytes_in += rlen; + bytes_out += wlen; + }, }; // Prog - bytes_out += w_len; - if let Some(prog_tx) = &prog_tx { prog_tx.send(bytes_out)?; @@ -541,13 +794,11 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, u if o.cflags.fsync { - o.flush()?; - o.dst.sync_all()?; + o.fsync()?; } else if o.cflags.fdatasync { - o.flush()?; - o.dst.sync_data()?; + o.fdatasync()?; } Ok((bytes_in, bytes_out)) diff --git a/src/uu/dd/src/dd_test.rs b/src/uu/dd/src/dd_test.rs index ee30b8907..6d697b5ec 100644 --- a/src/uu/dd/src/dd_test.rs +++ b/src/uu/dd/src/dd_test.rs @@ -65,9 +65,8 @@ macro_rules! icf ( { IConvFlags { ctable: $ctable, - cbs: None, - block: false, - unblock: false, + block: None, + unblock: None, swab: false, sync: false, noerror: false, @@ -87,6 +86,7 @@ macro_rules! make_spec_test ( $test_name, Input { src: $src, + non_ascii: false, ibs: 512, xfer_stats: StatusLevel::None, cflags: icf!(), @@ -132,6 +132,7 @@ macro_rules! make_conv_test ( $test_name, Input { src: $src, + non_ascii: false, ibs: 512, xfer_stats: StatusLevel::None, cflags: icf!($ctable), @@ -156,6 +157,7 @@ macro_rules! make_icf_test ( $test_name, Input { src: $src, + non_ascii: false, ibs: 512, xfer_stats: StatusLevel::None, cflags: $icf, @@ -282,6 +284,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let i = Input { src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), + non_ascii: false, ibs: 128, xfer_stats: StatusLevel::None, cflags: icf!(Some(&ASCII_TO_EBCDIC)), @@ -303,6 +306,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let i = Input { src: File::open(&tmp_fname_ae).unwrap(), + non_ascii: false, ibs: 256, xfer_stats: StatusLevel::None, cflags: icf!(Some(&EBCDIC_TO_ASCII)), @@ -343,9 +347,8 @@ make_icf_test!( File::open("./test-resources/seq-byte-values.test").unwrap(), IConvFlags { ctable: None, - cbs: None, - block: false, - unblock: false, + block: None, + unblock: None, swab: true, sync: false, noerror: false, @@ -359,12 +362,19 @@ make_icf_test!( File::open("./test-resources/seq-byte-values-odd.test").unwrap(), IConvFlags { ctable: None, - cbs: None, - block: false, - unblock: false, + block: None, + unblock: None, swab: true, sync: false, noerror: false, }, File::open("./test-resources/seq-byte-values-odd.spec").unwrap() ); + +fn block_test_basic() +{ + let mut buf = vec![0u8, 1u8, 2u8, 3u8]; + let res = block(&buf, 4); + + assert_eq!(res, vec![buf]); +} diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index b18054f14..12bb1aa89 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -25,6 +25,7 @@ pub enum ParseError NoMatchingMultiplier(String), ByteStringContainsNoValue(String), MultiplierStringWouldOverflow(String), + BlockUnblockWithoutCBS, } impl std::fmt::Display for ParseError @@ -298,7 +299,6 @@ fn parse_cbs(matches: &getopts::Matches) -> Result, ParseError> pub fn parse_status_level(matches: &getopts::Matches) -> Result { - // TODO: Impl unimplemented!() } @@ -322,7 +322,7 @@ fn parse_ctable(fmt: Option, case: Option) -> Option<&'stati { match (fmt, case) { - // Both specified + // Both [ascii | ebcdic | ibm] and [lcase | ucase] specified (Some(fmt), Some(case)) => match (fmt, case) { @@ -341,7 +341,7 @@ fn parse_ctable(fmt: Option, case: Option) -> Option<&'stati (_, _) => None, }, - // Only one of {ascii, ebcdic, ibm} specified + // Only [ascii | ebcdic | ibm] specified (Some(fmt), None) => match fmt { @@ -354,7 +354,7 @@ fn parse_ctable(fmt: Option, case: Option) -> Option<&'stati _ => None, }, - // Only one of {ucase, lcase} specified + // Only [lcase | ucase] specified (None, Some(ConvFlag::UCase)) => Some(&ASCII_LCASE_TO_UCASE), (None, Some(ConvFlag::LCase)) => @@ -389,8 +389,8 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result Result - if !unblock + match (cbs, unblock) { - block = true; - } - else - { - return Err(ParseError::MultipleBlockUnblock); + (Some(cbs), None) => + block = Some(cbs), + (None, _) => + return Err(ParseError::BlockUnblockWithoutCBS), + (_, Some(_)) => + return Err(ParseError::MultipleBlockUnblock), }, ConvFlag::Unblock => - if !block + match (cbs, block) { - unblock = true; - } - else - { - return Err(ParseError::MultipleBlockUnblock); + (Some(cbs), None) => + unblock = Some(cbs), + (None, _) => + return Err(ParseError::BlockUnblockWithoutCBS), + (_, Some(_)) => + return Err(ParseError::MultipleBlockUnblock), }, ConvFlag::Swab => swab = true, @@ -476,7 +478,6 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result Result sync = true, Flag::NoCache => nocache = true, - Flag::NoCache => - nocache = true, Flag::NonBlock => nonblock = true, Flag::NoATime => @@ -665,8 +664,6 @@ pub fn parse_oflags(matches: &getopts::Matches) -> Result sync = true, Flag::NoCache => nocache = true, - Flag::NoCache => - nocache = true, Flag::NonBlock => nonblock = true, Flag::NoATime => @@ -718,7 +715,7 @@ pub fn parse_skip_amt(ibs: &usize, iflags: &IFlags, matches: &getopts::Matches) } else { - let n = parse_bytes_only(&amt)?; + let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(ibs*n)) } } @@ -740,7 +737,7 @@ pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &getopts::Matches) } else { - let n = parse_bytes_only(&amt)?; + let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(obs*n)) } } @@ -749,3 +746,16 @@ pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &getopts::Matches) Ok(None) } } + +/// Parse whether the args indicate the input is not ascii +pub fn parse_input_non_ascii(matches: &getopts::Matches) -> Result +{ + if let Some(conv_opts) = matches.opt_str("conv") + { + Ok(conv_opts.contains("ascii")) + } + else + { + Ok(false) + } +} diff --git a/src/uu/dd/src/parseargs/test.rs b/src/uu/dd/src/parseargs/test.rs index 5587c1c4b..a902a9f5e 100644 --- a/src/uu/dd/src/parseargs/test.rs +++ b/src/uu/dd/src/parseargs/test.rs @@ -14,9 +14,8 @@ fn build_icf() { let icf_expd = IConvFlags { ctable: Some(&ASCII_TO_IBM), - cbs: None, - block: false, - unblock: false, + block: None, + unblock: None, swab: false, sync: false, noerror: false, From f2fa1e1be14d7a9a59d6eae4181ac5497ce4f5b7 Mon Sep 17 00:00:00 2001 From: Tyler Date: Sat, 29 May 2021 15:22:42 -0700 Subject: [PATCH 16/66] Adds additional test resources & unit tests for block. --- src/uu/dd/src/dd_test.rs | 203 +++++++++++++++++- src/uu/dd/test-resources/dd-block-cbs16.spec | 1 + src/uu/dd/test-resources/dd-block-cbs16.test | 16 ++ src/uu/dd/test-resources/dd-block-cbs8.spec | 1 + .../dd-block-consecutive-nl-cbs16.spec | 1 + .../dd-block-consecutive-nl.test | 4 + .../dd/test-resources/dd-unblock-cbs16.spec | 16 ++ .../dd/test-resources/dd-unblock-cbs16.test | 1 + src/uu/dd/test-resources/dd-unblock-cbs8.spec | 32 +++ 9 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 src/uu/dd/test-resources/dd-block-cbs16.spec create mode 100644 src/uu/dd/test-resources/dd-block-cbs16.test create mode 100644 src/uu/dd/test-resources/dd-block-cbs8.spec create mode 100644 src/uu/dd/test-resources/dd-block-consecutive-nl-cbs16.spec create mode 100644 src/uu/dd/test-resources/dd-block-consecutive-nl.test create mode 100644 src/uu/dd/test-resources/dd-unblock-cbs16.spec create mode 100644 src/uu/dd/test-resources/dd-unblock-cbs16.test create mode 100644 src/uu/dd/test-resources/dd-unblock-cbs8.spec diff --git a/src/uu/dd/src/dd_test.rs b/src/uu/dd/src/dd_test.rs index 6d697b5ec..b3187fc10 100644 --- a/src/uu/dd/src/dd_test.rs +++ b/src/uu/dd/src/dd_test.rs @@ -175,6 +175,38 @@ macro_rules! make_icf_test ( }; ); +macro_rules! make_block_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: IConvFlags { + ctable: None, + block: $block, + unblock: None, + swab: false, + sync: false, + noerror: false, + }, + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + make_spec_test!( zeros_4k_test, "zeros-4k", @@ -371,10 +403,175 @@ make_icf_test!( File::open("./test-resources/seq-byte-values-odd.spec").unwrap() ); -fn block_test_basic() +static NL: u8 = '\n' as u8; +static SPACE: u8 = ' ' as u8; + +#[test] +fn block_test_no_nl() { - let mut buf = vec![0u8, 1u8, 2u8, 3u8]; + let buf = vec![0u8, 1u8, 2u8, 3u8]; let res = block(&buf, 4); - assert_eq!(res, vec![buf]); + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + ]); } + +#[test] +fn block_test_no_nl_short_rec() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8]; + let res = block(&buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_no_nl_trunc() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; + let res = block(&buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8/*, 4u8*/], + ]); +} + +#[test] +fn block_test_nl_gt_cbs_trunc() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8]; + let res = block(&buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + // gnu-dd truncates this record + // vec![4u8, SPACE, SPACE, SPACE], + vec![5u8, 6u8], + vec![7u8], + ]); +} + +#[test] +fn block_test_surrounded_nl() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; + let res = block(&buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, 8u8, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_multiple_nl_same_block() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8, 10u8, 11u8 ]; + let res = block(&buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![5u8, 6u8, 7u8, 8u8, 9u8, 10u8, 11u8, SPACE], + ]); +} + +#[test] +fn block_test_multiple_nl_diff_block() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8, 10u8, 11u8 ]; + let res = block(&buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], + vec![8u8, 9u8, 10u8, 11u8, SPACE, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_lone_nl_end() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; + let res = block(&buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + ]); +} + +#[test] +fn block_test_end_nl() +{ + let buf = vec![0u8, 1u8, 2u8, NL]; + let res = block(&buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, SPACE] + ]); +} + +#[test] +fn block_test_start_nl() +{ + let buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; + let res = block(&buf, 4); + + assert_eq!(res, vec![ + vec![SPACE, SPACE, SPACE, SPACE], + vec![0u8, 1u8, 2u8, 3u8], + ]); +} + +#[test] +fn block_test_double_nl() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; + let res = block(&buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8], + ]); +} + +#[test] +fn block_test_double_nl_aligned() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8]; + let res = block(&buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + vec![SPACE, SPACE, SPACE, SPACE], + vec![3u8, 4u8, 5u8, 6u8, 7u8/*, 8u8*/], + ]); +} + +make_block_test!( + block_cbs16, + "block-cbs-16", + File::open("./test-resources/dd-block-cbs16.test").unwrap(), + Some(16), + File::open("./test-resources/dd-block-cbs16.spec").unwrap() +); + +make_block_test!( + block_cbs16_as_cbs8, + "block-cbs-16-as-cbs8", + File::open("./test-resources/dd-block-cbs16.test").unwrap(), + Some(8), + File::open("./test-resources/dd-block-cbs8.spec").unwrap() +); + +make_block_test!( + block_consecutive_nl, + "block-consecutive-nl", + File::open("./test-resources/dd-block-consecutive-nl.test").unwrap(), + Some(16), + File::open("./test-resources/dd-block-consecutive-nl-cbs16.spec").unwrap() +); diff --git a/src/uu/dd/test-resources/dd-block-cbs16.spec b/src/uu/dd/test-resources/dd-block-cbs16.spec new file mode 100644 index 000000000..d06bc43ca --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-cbs16.spec @@ -0,0 +1 @@ +0 01 012 0123 01234 012345 0123456 01234567 012345678 0123456789 0123456789a 0123456789ab 0123456789abc 0123456789abcd 0123456789abcde 0123456789abcdef \ No newline at end of file diff --git a/src/uu/dd/test-resources/dd-block-cbs16.test b/src/uu/dd/test-resources/dd-block-cbs16.test new file mode 100644 index 000000000..b59df5561 --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-cbs16.test @@ -0,0 +1,16 @@ +0 +01 +012 +0123 +01234 +012345 +0123456 +01234567 +012345678 +0123456789 +0123456789a +0123456789ab +0123456789abc +0123456789abcd +0123456789abcde +0123456789abcdef diff --git a/src/uu/dd/test-resources/dd-block-cbs8.spec b/src/uu/dd/test-resources/dd-block-cbs8.spec new file mode 100644 index 000000000..ea86bdd74 --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-cbs8.spec @@ -0,0 +1 @@ +0 01 012 0123 01234 012345 0123456 012345670123456701234567012345670123456701234567012345670123456701234567 \ No newline at end of file diff --git a/src/uu/dd/test-resources/dd-block-consecutive-nl-cbs16.spec b/src/uu/dd/test-resources/dd-block-consecutive-nl-cbs16.spec new file mode 100644 index 000000000..f365007e0 --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-consecutive-nl-cbs16.spec @@ -0,0 +1 @@ +pre post \ No newline at end of file diff --git a/src/uu/dd/test-resources/dd-block-consecutive-nl.test b/src/uu/dd/test-resources/dd-block-consecutive-nl.test new file mode 100644 index 000000000..df0029a74 --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-consecutive-nl.test @@ -0,0 +1,4 @@ +pre + + +post diff --git a/src/uu/dd/test-resources/dd-unblock-cbs16.spec b/src/uu/dd/test-resources/dd-unblock-cbs16.spec new file mode 100644 index 000000000..b59df5561 --- /dev/null +++ b/src/uu/dd/test-resources/dd-unblock-cbs16.spec @@ -0,0 +1,16 @@ +0 +01 +012 +0123 +01234 +012345 +0123456 +01234567 +012345678 +0123456789 +0123456789a +0123456789ab +0123456789abc +0123456789abcd +0123456789abcde +0123456789abcdef diff --git a/src/uu/dd/test-resources/dd-unblock-cbs16.test b/src/uu/dd/test-resources/dd-unblock-cbs16.test new file mode 100644 index 000000000..d06bc43ca --- /dev/null +++ b/src/uu/dd/test-resources/dd-unblock-cbs16.test @@ -0,0 +1 @@ +0 01 012 0123 01234 012345 0123456 01234567 012345678 0123456789 0123456789a 0123456789ab 0123456789abc 0123456789abcd 0123456789abcde 0123456789abcdef \ No newline at end of file diff --git a/src/uu/dd/test-resources/dd-unblock-cbs8.spec b/src/uu/dd/test-resources/dd-unblock-cbs8.spec new file mode 100644 index 000000000..02a88fe2a --- /dev/null +++ b/src/uu/dd/test-resources/dd-unblock-cbs8.spec @@ -0,0 +1,32 @@ +0 + +01 + +012 + +0123 + +01234 + +012345 + +0123456 + +01234567 + +01234567 +8 +01234567 +89 +01234567 +89a +01234567 +89ab +01234567 +89abc +01234567 +89abcd +01234567 +89abcde +01234567 +89abcdef From 48374ab0a257d011da6e2dbb7193746ef83193ea Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 1 Jun 2021 11:39:18 -0700 Subject: [PATCH 17/66] Imlements block. All tests passing. --- src/uu/dd/src/dd.rs | 65 +++++++++++++++--------------- src/uu/dd/src/dd_test.rs | 85 +++++++++++++++++++++++++--------------- 2 files changed, 87 insertions(+), 63 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 304236915..e315d79df 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -18,6 +18,7 @@ mod parseargs; mod conversion_tables; use conversion_tables::*; +use std::cmp; use std::convert::TryInto; use std::error::Error; use std::fs::{ @@ -261,7 +262,7 @@ impl Input let mut bytes_read = 0; // TODO: Fix this! - assert!(obs > ibs); + // assert!(obs > ibs); for n in 0..(obs/ibs) { // fill an ibs-len slice from src @@ -458,37 +459,35 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -fn pad(buf: &mut Vec, padding: u8) +#[inline] +fn pad(buf: &mut Vec, cbs: usize, pad: u8) { - unimplemented!() + buf.resize(cbs, pad) } /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N -/// -/// Example cases: -/// -/// [a...b] -> [a...b] -/// [a...b'\n'c...d] -> [a...b' '...], [c...d] -/// [a...b'\n'c...d'\n'e...] -> [a...b' '...], [c...d' '...], [e...] -/// -/// [a...b'\n'] -> [a...b' '] -/// ['\n'a...b] -> [' '...], [a...b] -/// -/// ['\n'a...b] -> [' '...], [a...b] -/// ['\n''\n'a...b] -> [' '...], [' '...], [a...b] -// -fn block(buf: &[u8], cbs: usize) -> Vec> +fn block(mut buf: Vec, cbs: usize) -> Vec> { - buf.split(| &c | c == '\n' as u8) - .fold(Vec::new(), | mut blocks, split | { - let mut block = Vec::with_capacity(cbs); - block.extend(split); - pad(&mut block, ' ' as u8); - blocks.push(block); + let mut blocks = buf.split(| &e | e == '\n' as u8) + .fold(Vec::new(), | mut blocks, split | + { + let mut split = split.to_vec(); + split.resize(cbs, ' ' as u8); + blocks.push(split); - blocks - }) + blocks + }); + + if let Some(last) = blocks.last() + { + if last.iter().all(| &e | e == ' ' as u8) + { + blocks.pop(); + } + } + + blocks } // Trims padding from each cbs-length partition of buf @@ -564,7 +563,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< { // ascii input so perform the block first let cbs = i.cflags.block.unwrap(); - let mut blocks = block(&mut buf, cbs); + let mut blocks = block(buf, cbs); if let Some(ct) = i.cflags.ctable { @@ -589,11 +588,10 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< apply_ct(&mut buf, &ct); } - let blocks = block(&mut buf, cbs); - - let blocks = blocks.into_iter() - .flatten() - .collect(); + let blocks = block(buf, cbs) + .into_iter() + .flatten() + .collect(); Ok(blocks) } @@ -639,6 +637,8 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> #[inline] fn is_fast_read(i: &Input, o: &Output) -> bool { + // TODO: Enable this once fast_reads are implemented + false && i.ibs == o.obs && !is_conv(i) && !is_block(i) @@ -671,7 +671,8 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> else { // Read - let mut buf = Vec::with_capacity(o.obs); + // let mut buf = Vec::with_capacity(o.obs); + let mut buf = vec![DEFAULT_FILL_BYTE; o.obs]; let rlen = i.fill_n(&mut buf, o.obs)?; if rlen == 0 diff --git a/src/uu/dd/src/dd_test.rs b/src/uu/dd/src/dd_test.rs index b3187fc10..8f9de6d76 100644 --- a/src/uu/dd/src/dd_test.rs +++ b/src/uu/dd/src/dd_test.rs @@ -409,8 +409,8 @@ static SPACE: u8 = ' ' as u8; #[test] fn block_test_no_nl() { - let buf = vec![0u8, 1u8, 2u8, 3u8]; - let res = block(&buf, 4); + let mut buf = vec![0u8, 1u8, 2u8, 3u8]; + let res = block(buf, 4); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], @@ -420,8 +420,8 @@ fn block_test_no_nl() #[test] fn block_test_no_nl_short_rec() { - let buf = vec![0u8, 1u8, 2u8, 3u8]; - let res = block(&buf, 8); + let mut buf = vec![0u8, 1u8, 2u8, 3u8]; + let res = block(buf, 8); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], @@ -431,8 +431,8 @@ fn block_test_no_nl_short_rec() #[test] fn block_test_no_nl_trunc() { - let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; - let res = block(&buf, 4); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; + let res = block(buf, 4); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8/*, 4u8*/], @@ -442,23 +442,23 @@ fn block_test_no_nl_trunc() #[test] fn block_test_nl_gt_cbs_trunc() { - let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8]; - let res = block(&buf, 4); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8]; + let res = block(buf, 4); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], // gnu-dd truncates this record // vec![4u8, SPACE, SPACE, SPACE], - vec![5u8, 6u8], - vec![7u8], + vec![0u8, 1u8, 2u8, 3u8], + vec![5u8, 6u8, 7u8, 8u8], ]); } #[test] fn block_test_surrounded_nl() { - let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; - let res = block(&buf, 8); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; + let res = block(buf, 8); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], @@ -469,34 +469,34 @@ fn block_test_surrounded_nl() #[test] fn block_test_multiple_nl_same_block() { - let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8, 10u8, 11u8 ]; - let res = block(&buf, 8); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8]; + let res = block(buf, 8); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], vec![4u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - vec![5u8, 6u8, 7u8, 8u8, 9u8, 10u8, 11u8, SPACE], + vec![5u8, 6u8, 7u8, 8u8, 9u8, SPACE, SPACE, SPACE], ]); } #[test] fn block_test_multiple_nl_diff_block() { - let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8, 10u8, 11u8 ]; - let res = block(&buf, 8); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8]; + let res = block(buf, 8); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], - vec![8u8, 9u8, 10u8, 11u8, SPACE, SPACE, SPACE, SPACE], + vec![8u8, 9u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], ]); } #[test] fn block_test_lone_nl_end() { - let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; - let res = block(&buf, 4); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL]; + let res = block(buf, 4); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], @@ -506,19 +506,42 @@ fn block_test_lone_nl_end() #[test] fn block_test_end_nl() { - let buf = vec![0u8, 1u8, 2u8, NL]; - let res = block(&buf, 4); + let mut buf = vec![0u8, 1u8, 2u8, NL]; + let res = block(buf, 4); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, SPACE] ]); } +#[test] +fn block_test_end_nl_new_block() +{ + let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + ]); +} + +#[test] +fn block_test_double_end_nl() +{ + let mut buf = vec![0u8, 1u8, 2u8, NL, NL]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, SPACE], + vec![SPACE, SPACE, SPACE, SPACE], + ]); +} + #[test] fn block_test_start_nl() { - let buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; - let res = block(&buf, 4); + let mut buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; + let res = block(buf, 4); assert_eq!(res, vec![ vec![SPACE, SPACE, SPACE, SPACE], @@ -529,26 +552,26 @@ fn block_test_start_nl() #[test] fn block_test_double_nl() { - let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; - let res = block(&buf, 8); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; + let res = block(buf, 8); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8], + vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], ]); } #[test] -fn block_test_double_nl_aligned() +fn block_test_double_nl_double_trunc() { - let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8]; - let res = block(&buf, 4); + let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; + let res = block(buf, 4); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], vec![SPACE, SPACE, SPACE, SPACE], - vec![3u8, 4u8, 5u8, 6u8, 7u8/*, 8u8*/], + vec![4u8, 5u8, 6u8, 7u8/*, 8u8*/], ]); } From 391a175dbb9a57c6c9aed1965750fa914c9fc751 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 1 Jun 2021 12:34:15 -0700 Subject: [PATCH 18/66] Fix regression in converison logic. --- src/uu/dd/src/dd.rs | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index e315d79df..c6c6a8f26 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -262,7 +262,7 @@ impl Input let mut bytes_read = 0; // TODO: Fix this! - // assert!(obs > ibs); + // assert!(obs < ibs); for n in 0..(obs/ibs) { // fill an ibs-len slice from src @@ -459,12 +459,6 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -#[inline] -fn pad(buf: &mut Vec, cbs: usize, pad: u8) -{ - buf.resize(cbs, pad) -} - /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N fn block(mut buf: Vec, cbs: usize) -> Vec> @@ -506,19 +500,6 @@ fn apply_ct(buf: &mut [u8], ct: &ConversionTable) } } -#[inline] -fn perform_swab(buf: &mut [u8]) -{ - let mut tmp; - - for base in (1..buf.len()).step_by(2) - { - tmp = buf[base]; - buf[base] = buf[base-1]; - buf[base-1] = tmp; - } -} - fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output) -> Result, Box> { #[inline] @@ -634,6 +615,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< fn read_write_helper(i: &mut Input, o: &mut Output) -> Result<(usize, Vec), Box> { + // -------------------------------------------------------------------- #[inline] fn is_fast_read(i: &Input, o: &Output) -> bool { @@ -661,6 +643,19 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> i.cflags.unblock.is_some() } // -------------------------------------------------------------------- + #[inline] + fn perform_swab(buf: &mut [u8]) + { + let mut tmp; + + for base in (1..buf.len()).step_by(2) + { + tmp = buf[base]; + buf[base] = buf[base-1]; + buf[base-1] = tmp; + } + } + // -------------------------------------------------------------------- if is_fast_read(&i, &o) { // TODO: fast reads are copies performed @@ -671,20 +666,19 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> else { // Read - // let mut buf = Vec::with_capacity(o.obs); let mut buf = vec![DEFAULT_FILL_BYTE; o.obs]; let rlen = i.fill_n(&mut buf, o.obs)?; + buf.resize(rlen, DEFAULT_FILL_BYTE); if rlen == 0 { return Ok((0,Vec::new())); } - // Conv etc... if i.cflags.swab { - perform_swab(&mut buf[..rlen]); + perform_swab(&mut buf); } if is_conv(&i) || is_block(&i) || is_unblock(&i) From ead3c8c8fc666cf49766e53775b59c7e46cafdb8 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 1 Jun 2021 12:53:23 -0700 Subject: [PATCH 19/66] Project structure refactor. - Unit tests split into individual folders - Parseargs test module renamed unit_tests to match new structure --- src/uu/dd/src/dd.rs | 2 +- src/uu/dd/src/dd_test.rs | 600 ------------------ .../src/dd_unit_tests/block_unblock_tests.rs | 197 ++++++ .../dd/src/dd_unit_tests/conversion_tests.rs | 173 +++++ src/uu/dd/src/dd_unit_tests/mod.rs | 216 +++++++ src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 25 + src/uu/dd/src/parseargs.rs | 4 +- .../src/parseargs/{test.rs => unit_tests.rs} | 0 8 files changed, 614 insertions(+), 603 deletions(-) delete mode 100644 src/uu/dd/src/dd_test.rs create mode 100644 src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs create mode 100644 src/uu/dd/src/dd_unit_tests/conversion_tests.rs create mode 100644 src/uu/dd/src/dd_unit_tests/mod.rs create mode 100644 src/uu/dd/src/dd_unit_tests/sanity_tests.rs rename src/uu/dd/src/parseargs/{test.rs => unit_tests.rs} (100%) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index c6c6a8f26..d00cbd1c0 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -11,7 +11,7 @@ extern crate uucore; #[cfg(test)] -mod dd_test; +mod dd_unit_tests; mod parseargs; diff --git a/src/uu/dd/src/dd_test.rs b/src/uu/dd/src/dd_test.rs deleted file mode 100644 index 8f9de6d76..000000000 --- a/src/uu/dd/src/dd_test.rs +++ /dev/null @@ -1,600 +0,0 @@ -use super::*; - -use std::io::prelude::*; -use std::io::BufReader; -use std::fs; -use md5::{ Md5, Digest, }; -use hex_literal::hex; - -// use tempfile::tempfile; -// TODO: (Maybe) Use tempfiles in the tests. - -const DEFAULT_CFO: OConvFlags = OConvFlags { - sparse: false, - excl: false, - nocreat: false, - notrunc: false, - fdatasync: false, - 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! icf ( - () => - { - icf!(None) - }; - ( $ctable:expr ) => - { - IConvFlags { - ctable: $ctable, - block: None, - unblock: None, - swab: false, - sync: false, - noerror: false, - } - }; -); - -macro_rules! make_spec_test ( - ( $test_id:ident, $test_name:expr, $src:expr ) => - { - // When spec not given, output should match input - make_spec_test!($test_id, $test_name, $src, $src); - }; - ( $test_id:ident, $test_name:expr, $src:expr, $spec:expr ) => - { - make_spec_test!($test_id, - $test_name, - Input { - src: $src, - non_ascii: false, - ibs: 512, - xfer_stats: StatusLevel::None, - cflags: icf!(), - iflags: DEFAULT_IFLAGS, - }, - Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), - obs: 512, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - $spec, - format!("./test-resources/FAILED-{}.test", $test_name) - ); - }; - ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr, $tmp_fname:expr ) => - { - #[test] - fn $test_id() - { - dd_fileout($i,$o).unwrap(); - - let res = File::open($tmp_fname).unwrap(); - let res = BufReader::new(res); - - let spec = BufReader::new($spec); - - for (b_res, b_spec) in res.bytes().zip(spec.bytes()) - { - assert_eq!(b_res.unwrap(), - b_spec.unwrap()); - } - - fs::remove_file($tmp_fname).unwrap(); - } - }; -); - -macro_rules! make_conv_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $ctable:expr, $spec:expr ) => - { - make_spec_test!($test_id, - $test_name, - Input { - src: $src, - non_ascii: false, - ibs: 512, - xfer_stats: StatusLevel::None, - cflags: icf!($ctable), - iflags: DEFAULT_IFLAGS, - }, - Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), - obs: 512, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - $spec, - format!("./test-resources/FAILED-{}.test", $test_name) - ); - }; -); - -macro_rules! make_icf_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $icf:expr, $spec:expr ) => - { - make_spec_test!($test_id, - $test_name, - Input { - src: $src, - non_ascii: false, - ibs: 512, - xfer_stats: StatusLevel::None, - cflags: $icf, - iflags: DEFAULT_IFLAGS, - }, - Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), - obs: 512, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - $spec, - format!("./test-resources/FAILED-{}.test", $test_name) - ); - }; -); - -macro_rules! make_block_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) => - { - make_spec_test!($test_id, - $test_name, - Input { - src: $src, - non_ascii: false, - ibs: 512, - xfer_stats: StatusLevel::None, - cflags: IConvFlags { - ctable: None, - block: $block, - unblock: None, - swab: false, - sync: false, - noerror: false, - }, - iflags: DEFAULT_IFLAGS, - }, - Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), - obs: 512, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - $spec, - format!("./test-resources/FAILED-{}.test", $test_name) - ); - }; -); - -make_spec_test!( - zeros_4k_test, - "zeros-4k", - File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap() -); - -make_spec_test!( - ones_4k_test, - "ones-4k", - File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap() -); - -make_spec_test!( - deadbeef_32k_test, - "deadbeef-32k", - File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() -); - -make_spec_test!( - random_73k_test, - "random-73k", - File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() -); - -make_conv_test!( - atoe_conv_spec_test, - "atoe-conv-spec-test", - File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(&ASCII_TO_EBCDIC), - File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() -); - -make_conv_test!( - etoa_conv_spec_test, - "etoa-conv-spec-test", - File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(&EBCDIC_TO_ASCII), - File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() -); - -make_conv_test!( - atoibm_conv_spec_test, - "atoibm-conv-spec-test", - File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(&ASCII_TO_IBM), - File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() -); - -make_conv_test!( - lcase_ascii_to_ucase_ascii, - "lcase_ascii_to_ucase_ascii", - File::open("./test-resources/lcase-ascii.test").unwrap(), - Some(&ASCII_LCASE_TO_UCASE), - File::open("./test-resources/ucase-ascii.test").unwrap() -); - -make_conv_test!( - ucase_ascii_to_lcase_ascii, - "ucase_ascii_to_lcase_ascii", - File::open("./test-resources/ucase-ascii.test").unwrap(), - Some(&ASCII_UCASE_TO_LCASE), - File::open("./test-resources/lcase-ascii.test").unwrap() -); - -make_conv_test!( - // conv=ebcdic,ucase - atoe_and_ucase_conv_spec_test, - "atoe-and-ucase-conv-spec-test", - File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(&ASCII_TO_EBCDIC_LCASE_TO_UCASE), - File::open("./test-resources/ucase-ebcdic.test").unwrap() -); - -make_conv_test!( - // conv=ebcdic,lcase - atoe_and_lcase_conv_spec_test, - "atoe-and-lcase-conv-spec-test", - File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(&ASCII_TO_EBCDIC_UCASE_TO_LCASE), - File::open("./test-resources/lcase-ebcdic.test").unwrap() -); - -make_conv_test!( - // conv=ibm,ucase - atoibm_and_ucase_conv_spec_test, - "atoibm-and-ucase-conv-spec-test", - File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(&ASCII_TO_IBM_UCASE_TO_LCASE), - File::open("./test-resources/lcase-ibm.test").unwrap() -); - -make_conv_test!( - // conv=ibm,lcase - atoibm_and_lcase_conv_spec_test, - "atoibm-and-lcase-conv-spec-test", - File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), - Some(&ASCII_TO_IBM_LCASE_TO_UCASE), - File::open("./test-resources/ucase-ibm.test").unwrap() -); - -#[test] -fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() -{ - // ASCII->EBCDIC - let test_name = "all-valid-ascii-to-ebcdic"; - let tmp_fname_ae = format!("./test-resources/FAILED-{}.test", test_name); - - let i = Input { - src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), - non_ascii: false, - ibs: 128, - xfer_stats: StatusLevel::None, - cflags: icf!(Some(&ASCII_TO_EBCDIC)), - iflags: DEFAULT_IFLAGS, - }; - - let o = Output { - dst: File::create(&tmp_fname_ae).unwrap(), - obs: 1024, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }; - - dd_fileout(i,o).unwrap(); - - // EBCDIC->ASCII - let test_name = "all-valid-ebcdic-to-ascii"; - let tmp_fname_ea = format!("./test-resources/FAILED-{}.test", test_name); - - let i = Input { - src: File::open(&tmp_fname_ae).unwrap(), - non_ascii: false, - ibs: 256, - xfer_stats: StatusLevel::None, - cflags: icf!(Some(&EBCDIC_TO_ASCII)), - iflags: DEFAULT_IFLAGS, - }; - - let o = Output { - dst: File::create(&tmp_fname_ea).unwrap(), - obs: 1024, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }; - - dd_fileout(i,o).unwrap(); - - let res = { - let res = File::open(&tmp_fname_ea).unwrap(); - let res = BufReader::new(res); - - let mut h = Md5::new(); - for b in res.bytes() - { - h.update([b.unwrap()]); - } - - h.finalize() - }; - - assert_eq!(hex!("37eff01866ba3f538421b30b7cbefcac"), res[..]); - - fs::remove_file(&tmp_fname_ae).unwrap(); - fs::remove_file(&tmp_fname_ea).unwrap(); -} - -make_icf_test!( - swab_256_test, - "swab-256", - File::open("./test-resources/seq-byte-values.test").unwrap(), - IConvFlags { - ctable: None, - block: None, - unblock: None, - swab: true, - sync: false, - noerror: false, - }, - File::open("./test-resources/seq-byte-values-swapped.test").unwrap() -); - -make_icf_test!( - swab_257_test, - "swab-257", - File::open("./test-resources/seq-byte-values-odd.test").unwrap(), - IConvFlags { - ctable: None, - block: None, - unblock: None, - swab: true, - sync: false, - noerror: false, - }, - File::open("./test-resources/seq-byte-values-odd.spec").unwrap() -); - -static NL: u8 = '\n' as u8; -static SPACE: u8 = ' ' as u8; - -#[test] -fn block_test_no_nl() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - ]); -} - -#[test] -fn block_test_no_nl_short_rec() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8]; - let res = block(buf, 8); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - ]); -} - -#[test] -fn block_test_no_nl_trunc() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8/*, 4u8*/], - ]); -} - -#[test] -fn block_test_nl_gt_cbs_trunc() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - // gnu-dd truncates this record - // vec![4u8, SPACE, SPACE, SPACE], - vec![0u8, 1u8, 2u8, 3u8], - vec![5u8, 6u8, 7u8, 8u8], - ]); -} - -#[test] -fn block_test_surrounded_nl() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; - let res = block(buf, 8); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8, 8u8, SPACE, SPACE, SPACE], - ]); -} - -#[test] -fn block_test_multiple_nl_same_block() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8]; - let res = block(buf, 8); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![4u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - vec![5u8, 6u8, 7u8, 8u8, 9u8, SPACE, SPACE, SPACE], - ]); -} - -#[test] -fn block_test_multiple_nl_diff_block() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8]; - let res = block(buf, 8); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], - vec![8u8, 9u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - ]); -} - -#[test] -fn block_test_lone_nl_end() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - ]); -} - -#[test] -fn block_test_end_nl() -{ - let mut buf = vec![0u8, 1u8, 2u8, NL]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, SPACE] - ]); -} - -#[test] -fn block_test_end_nl_new_block() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - ]); -} - -#[test] -fn block_test_double_end_nl() -{ - let mut buf = vec![0u8, 1u8, 2u8, NL, NL]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, SPACE], - vec![SPACE, SPACE, SPACE, SPACE], - ]); -} - -#[test] -fn block_test_start_nl() -{ - let mut buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![SPACE, SPACE, SPACE, SPACE], - vec![0u8, 1u8, 2u8, 3u8], - ]); -} - -#[test] -fn block_test_double_nl() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; - let res = block(buf, 8); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], - ]); -} - -#[test] -fn block_test_double_nl_double_trunc() -{ - let mut buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - vec![SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8/*, 8u8*/], - ]); -} - -make_block_test!( - block_cbs16, - "block-cbs-16", - File::open("./test-resources/dd-block-cbs16.test").unwrap(), - Some(16), - File::open("./test-resources/dd-block-cbs16.spec").unwrap() -); - -make_block_test!( - block_cbs16_as_cbs8, - "block-cbs-16-as-cbs8", - File::open("./test-resources/dd-block-cbs16.test").unwrap(), - Some(8), - File::open("./test-resources/dd-block-cbs8.spec").unwrap() -); - -make_block_test!( - block_consecutive_nl, - "block-consecutive-nl", - File::open("./test-resources/dd-block-consecutive-nl.test").unwrap(), - Some(16), - File::open("./test-resources/dd-block-consecutive-nl-cbs16.spec").unwrap() -); diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs new file mode 100644 index 000000000..7dffe8392 --- /dev/null +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -0,0 +1,197 @@ +use super::*; + +static NL: u8 = '\n' as u8; +static SPACE: u8 = ' ' as u8; + +#[test] +fn block_test_no_nl() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + ]); +} + +#[test] +fn block_test_no_nl_short_rec() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8]; + let res = block(buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_no_nl_trunc() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8/*, 4u8*/], + ]); +} + +#[test] +fn block_test_nl_gt_cbs_trunc() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + // gnu-dd truncates this record + // vec![4u8, SPACE, SPACE, SPACE], + vec![0u8, 1u8, 2u8, 3u8], + vec![5u8, 6u8, 7u8, 8u8], + ]); +} + +#[test] +fn block_test_surrounded_nl() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; + let res = block(buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, 8u8, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_multiple_nl_same_block() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8]; + let res = block(buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![5u8, 6u8, 7u8, 8u8, 9u8, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_multiple_nl_diff_block() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8]; + let res = block(buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], + vec![8u8, 9u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_lone_nl_end() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + ]); +} + +#[test] +fn block_test_end_nl() +{ + let buf = vec![0u8, 1u8, 2u8, NL]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, SPACE] + ]); +} + +#[test] +fn block_test_end_nl_new_block() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + ]); +} + +#[test] +fn block_test_double_end_nl() +{ + let buf = vec![0u8, 1u8, 2u8, NL, NL]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, SPACE], + vec![SPACE, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_start_nl() +{ + let buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![SPACE, SPACE, SPACE, SPACE], + vec![0u8, 1u8, 2u8, 3u8], + ]); +} + +#[test] +fn block_test_double_nl() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; + let res = block(buf, 8); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], + ]); +} + +#[test] +fn block_test_double_nl_double_trunc() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; + let res = block(buf, 4); + + assert_eq!(res, vec![ + vec![0u8, 1u8, 2u8, 3u8], + vec![SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8/*, 8u8*/], + ]); +} + +make_block_test!( + block_cbs16, + "block-cbs-16", + File::open("./test-resources/dd-block-cbs16.test").unwrap(), + Some(16), + File::open("./test-resources/dd-block-cbs16.spec").unwrap() +); + +make_block_test!( + block_cbs16_as_cbs8, + "block-cbs-16-as-cbs8", + File::open("./test-resources/dd-block-cbs16.test").unwrap(), + Some(8), + File::open("./test-resources/dd-block-cbs8.spec").unwrap() +); + +make_block_test!( + block_consecutive_nl, + "block-consecutive-nl", + File::open("./test-resources/dd-block-consecutive-nl.test").unwrap(), + Some(16), + File::open("./test-resources/dd-block-consecutive-nl-cbs16.spec").unwrap() +); diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs new file mode 100644 index 000000000..584b92df1 --- /dev/null +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -0,0 +1,173 @@ +use super::*; + +make_conv_test!( + atoe_conv_spec_test, + "atoe-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(&ASCII_TO_EBCDIC), + File::open("./test-resources/gnudd-conv-atoe-seq-byte-values.spec").unwrap() +); + +make_conv_test!( + etoa_conv_spec_test, + "etoa-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(&EBCDIC_TO_ASCII), + File::open("./test-resources/gnudd-conv-etoa-seq-byte-values.spec").unwrap() +); + +make_conv_test!( + atoibm_conv_spec_test, + "atoibm-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(&ASCII_TO_IBM), + File::open("./test-resources/gnudd-conv-atoibm-seq-byte-values.spec").unwrap() +); + +make_conv_test!( + lcase_ascii_to_ucase_ascii, + "lcase_ascii_to_ucase_ascii", + File::open("./test-resources/lcase-ascii.test").unwrap(), + Some(&ASCII_LCASE_TO_UCASE), + File::open("./test-resources/ucase-ascii.test").unwrap() +); + +make_conv_test!( + ucase_ascii_to_lcase_ascii, + "ucase_ascii_to_lcase_ascii", + File::open("./test-resources/ucase-ascii.test").unwrap(), + Some(&ASCII_UCASE_TO_LCASE), + File::open("./test-resources/lcase-ascii.test").unwrap() +); + +make_conv_test!( + // conv=ebcdic,ucase + atoe_and_ucase_conv_spec_test, + "atoe-and-ucase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(&ASCII_TO_EBCDIC_LCASE_TO_UCASE), + File::open("./test-resources/ucase-ebcdic.test").unwrap() +); + +make_conv_test!( + // conv=ebcdic,lcase + atoe_and_lcase_conv_spec_test, + "atoe-and-lcase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(&ASCII_TO_EBCDIC_UCASE_TO_LCASE), + File::open("./test-resources/lcase-ebcdic.test").unwrap() +); + +make_conv_test!( + // conv=ibm,ucase + atoibm_and_ucase_conv_spec_test, + "atoibm-and-ucase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(&ASCII_TO_IBM_UCASE_TO_LCASE), + File::open("./test-resources/lcase-ibm.test").unwrap() +); + +make_conv_test!( + // conv=ibm,lcase + atoibm_and_lcase_conv_spec_test, + "atoibm-and-lcase-conv-spec-test", + File::open("./test-resources/seq-byte-values-b632a992d3aed5d8d1a59cc5a5a455ba.test").unwrap(), + Some(&ASCII_TO_IBM_LCASE_TO_UCASE), + File::open("./test-resources/ucase-ibm.test").unwrap() +); + +#[test] +fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() +{ + // ASCII->EBCDIC + let test_name = "all-valid-ascii-to-ebcdic"; + let tmp_fname_ae = format!("./test-resources/FAILED-{}.test", test_name); + + let i = Input { + src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), + non_ascii: false, + ibs: 128, + xfer_stats: StatusLevel::None, + cflags: icf!(Some(&ASCII_TO_EBCDIC)), + iflags: DEFAULT_IFLAGS, + }; + + let o = Output { + dst: File::create(&tmp_fname_ae).unwrap(), + obs: 1024, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }; + + dd_fileout(i,o).unwrap(); + + // EBCDIC->ASCII + let test_name = "all-valid-ebcdic-to-ascii"; + let tmp_fname_ea = format!("./test-resources/FAILED-{}.test", test_name); + + let i = Input { + src: File::open(&tmp_fname_ae).unwrap(), + non_ascii: false, + ibs: 256, + xfer_stats: StatusLevel::None, + cflags: icf!(Some(&EBCDIC_TO_ASCII)), + iflags: DEFAULT_IFLAGS, + }; + + let o = Output { + dst: File::create(&tmp_fname_ea).unwrap(), + obs: 1024, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }; + + dd_fileout(i,o).unwrap(); + + let res = { + let res = File::open(&tmp_fname_ea).unwrap(); + let res = BufReader::new(res); + + let mut h = Md5::new(); + for b in res.bytes() + { + h.update([b.unwrap()]); + } + + h.finalize() + }; + + assert_eq!(hex!("37eff01866ba3f538421b30b7cbefcac"), res[..]); + + fs::remove_file(&tmp_fname_ae).unwrap(); + fs::remove_file(&tmp_fname_ea).unwrap(); +} + +make_icf_test!( + swab_256_test, + "swab-256", + File::open("./test-resources/seq-byte-values.test").unwrap(), + IConvFlags { + ctable: None, + block: None, + unblock: None, + swab: true, + sync: false, + noerror: false, + }, + File::open("./test-resources/seq-byte-values-swapped.test").unwrap() +); + +make_icf_test!( + swab_257_test, + "swab-257", + File::open("./test-resources/seq-byte-values-odd.test").unwrap(), + IConvFlags { + ctable: None, + block: None, + unblock: None, + swab: true, + sync: false, + noerror: false, + }, + File::open("./test-resources/seq-byte-values-odd.spec").unwrap() +); diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs new file mode 100644 index 000000000..5fd1fef19 --- /dev/null +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -0,0 +1,216 @@ +use super::*; + +mod sanity_tests; +mod conversion_tests; +mod block_unblock_tests; + +use std::io::prelude::*; +use std::io::BufReader; +use std::fs; +use md5::{ Md5, Digest, }; +use hex_literal::hex; + +// use tempfile::tempfile; +// TODO: (Maybe) Use tempfiles in the tests. + +const DEFAULT_CFO: OConvFlags = OConvFlags { + sparse: false, + excl: false, + nocreat: false, + notrunc: false, + fdatasync: false, + 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! icf ( + () => + { + icf!(None) + }; + ( $ctable:expr ) => + { + IConvFlags { + ctable: $ctable, + block: None, + unblock: None, + swab: false, + sync: false, + noerror: false, + } + }; +); + +#[macro_export] +macro_rules! make_spec_test ( + ( $test_id:ident, $test_name:expr, $src:expr ) => + { + // When spec not given, output should match input + make_spec_test!($test_id, $test_name, $src, $src); + }; + ( $test_id:ident, $test_name:expr, $src:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr, $tmp_fname:expr ) => + { + #[test] + fn $test_id() + { + dd_fileout($i,$o).unwrap(); + + let res = File::open($tmp_fname).unwrap(); + let res = BufReader::new(res); + + let spec = BufReader::new($spec); + + for (b_res, b_spec) in res.bytes().zip(spec.bytes()) + { + assert_eq!(b_res.unwrap(), + b_spec.unwrap()); + } + + fs::remove_file($tmp_fname).unwrap(); + } + }; +); + +#[macro_export] +macro_rules! make_conv_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $ctable:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: icf!($ctable), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + +#[macro_export] +macro_rules! make_icf_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $icf:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: $icf, + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + +#[macro_export] +macro_rules! make_block_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: IConvFlags { + ctable: None, + block: $block, + unblock: None, + swab: false, + sync: false, + noerror: false, + }, + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs new file mode 100644 index 000000000..b772d6a27 --- /dev/null +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -0,0 +1,25 @@ +use super::*; + +make_spec_test!( + zeros_4k_test, + "zeros-4k", + File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap() +); + +make_spec_test!( + ones_4k_test, + "ones-4k", + File::open("./test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap() +); + +make_spec_test!( + deadbeef_32k_test, + "deadbeef-32k", + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() +); + +make_spec_test!( + random_73k_test, + "random-73k", + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() +); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 12bb1aa89..5af5d0cce 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -1,5 +1,5 @@ #[cfg(test)] -mod test; +mod unit_tests; use crate::conversion_tables::*; use crate::{ @@ -12,7 +12,7 @@ use crate::{ use std::error::Error; -/// Parser Errors describe errors with input +/// Parser Errors describe errors with parser input #[derive(Debug)] pub enum ParseError { diff --git a/src/uu/dd/src/parseargs/test.rs b/src/uu/dd/src/parseargs/unit_tests.rs similarity index 100% rename from src/uu/dd/src/parseargs/test.rs rename to src/uu/dd/src/parseargs/unit_tests.rs From 7bc151d561bfcea5ad6a1095dab36abe06b0e3e5 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 1 Jun 2021 13:02:02 -0700 Subject: [PATCH 20/66] Project structure refactor (cont'd) - Move test macros to specific modules. --- .../src/dd_unit_tests/block_unblock_tests.rs | 32 +++++++ .../dd/src/dd_unit_tests/conversion_tests.rs | 50 +++++++++++ src/uu/dd/src/dd_unit_tests/mod.rs | 84 ------------------- 3 files changed, 82 insertions(+), 84 deletions(-) diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index 7dffe8392..e635358cd 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -3,6 +3,38 @@ use super::*; static NL: u8 = '\n' as u8; static SPACE: u8 = ' ' as u8; +macro_rules! make_block_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: IConvFlags { + ctable: None, + block: $block, + unblock: None, + swab: false, + sync: false, + noerror: false, + }, + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + #[test] fn block_test_no_nl() { diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 584b92df1..9652a6b76 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -1,5 +1,55 @@ use super::*; +macro_rules! make_conv_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $ctable:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: icf!($ctable), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + +macro_rules! make_icf_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $icf:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: $icf, + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + make_conv_test!( atoe_conv_spec_test, "atoe-conv-spec-test", diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index 5fd1fef19..faeb13e20 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -130,87 +130,3 @@ macro_rules! make_spec_test ( }; ); -#[macro_export] -macro_rules! make_conv_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $ctable:expr, $spec:expr ) => - { - make_spec_test!($test_id, - $test_name, - Input { - src: $src, - non_ascii: false, - ibs: 512, - xfer_stats: StatusLevel::None, - cflags: icf!($ctable), - iflags: DEFAULT_IFLAGS, - }, - Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), - obs: 512, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - $spec, - format!("./test-resources/FAILED-{}.test", $test_name) - ); - }; -); - -#[macro_export] -macro_rules! make_icf_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $icf:expr, $spec:expr ) => - { - make_spec_test!($test_id, - $test_name, - Input { - src: $src, - non_ascii: false, - ibs: 512, - xfer_stats: StatusLevel::None, - cflags: $icf, - iflags: DEFAULT_IFLAGS, - }, - Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), - obs: 512, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - $spec, - format!("./test-resources/FAILED-{}.test", $test_name) - ); - }; -); - -#[macro_export] -macro_rules! make_block_test ( - ( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) => - { - make_spec_test!($test_id, - $test_name, - Input { - src: $src, - non_ascii: false, - ibs: 512, - xfer_stats: StatusLevel::None, - cflags: IConvFlags { - ctable: None, - block: $block, - unblock: None, - swab: false, - sync: false, - noerror: false, - }, - iflags: DEFAULT_IFLAGS, - }, - Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), - obs: 512, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - $spec, - format!("./test-resources/FAILED-{}.test", $test_name) - ); - }; -); From 787b9696cbb16cadb2a44052d7e8e9957c1173cb Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 2 Jun 2021 15:11:14 -0700 Subject: [PATCH 21/66] Implements Unblock - Adds unit tests - Implements unblock - All tests passing --- src/uu/dd/src/dd.rs | 129 +++++++++++----- .../src/dd_unit_tests/block_unblock_tests.rs | 141 +++++++++++++++--- 2 files changed, 211 insertions(+), 59 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index d00cbd1c0..b9c7078e3 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -436,32 +436,9 @@ impl Write for Output } } -fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () -{ - move || { - - // TODO: Replace ?? with accurate info - print!("\rProgress ({}/??)", 0); - - loop - { - match rx.recv() - { - Ok(wr_total) => { - print!("\rProgress ({}/??)", wr_total); - }, - Err(_) => { - println!(""); - break - }, - } - } - } -} - /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N -fn block(mut buf: Vec, cbs: usize) -> Vec> +fn block(buf: Vec, cbs: usize) -> Vec> { let mut blocks = buf.split(| &e | e == '\n' as u8) .fold(Vec::new(), | mut blocks, split | @@ -484,24 +461,63 @@ fn block(mut buf: Vec, cbs: usize) -> Vec> blocks } -// Trims padding from each cbs-length partition of buf -// as specified by conv=unblock and cbs=N -fn unblock(buf: &[u8], cbs: usize) +/// Trims padding from each cbs-length partition of buf +/// as specified by conv=unblock and cbs=N +fn unblock(buf: Vec, cbs: usize) -> Vec { - unimplemented!() -} - -#[inline] -fn apply_ct(buf: &mut [u8], ct: &ConversionTable) -{ - for idx in 0..buf.len() + // Local Helper Fns ---------------------------------------------------- + #[inline] + fn build_blocks(buf: Vec, cbs: usize) -> Vec> { - buf[idx] = ct[buf[idx] as usize]; + let mut blocks = Vec::new(); + let mut curr = buf; + let mut next; + let mut width; + + while !curr.is_empty() + { + width = cmp::min(cbs, curr.len()); + next = curr.split_off(width); + + blocks.push(curr); + + curr = next; + } + + blocks } + // --------------------------------------------------------------------- + build_blocks(buf, cbs) + .into_iter() + .fold(Vec::new(), | mut unblocks, mut block | { + let block = if let Some(last_char_idx) = block.iter().rposition(| &e | e != ' ' as u8) + { + block.truncate(last_char_idx+1); + block.push('\n' as u8); + + block + } + else if let Some(32u8) = block.get(0) + { + vec!['\n' as u8] + } + else + { + block + }; + + unblocks.push(block); + + unblocks + }) + .into_iter() + .flatten() + .collect() } fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output) -> Result, Box> { + // Local Predicate Fns ------------------------------------------------- #[inline] fn should_block_then_conv(i: &Input) -> bool { @@ -532,6 +548,15 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< && i.cflags.block.is_none() && i.cflags.unblock.is_none() } + // Local Helper Fns ---------------------------------------------------- + #[inline] + fn apply_ct(buf: &mut [u8], ct: &ConversionTable) + { + for idx in 0..buf.len() + { + buf[idx] = ct[buf[idx] as usize]; + } + } // -------------------------------------------------------------------- if conv_only(&i) { // no block/unblock @@ -580,7 +605,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< { // ascii input so perform the unblock first let cbs = i.cflags.unblock.unwrap(); - unblock(&mut buf, cbs); + let mut buf = unblock(buf, cbs); if let Some(ct) = i.cflags.ctable { @@ -598,7 +623,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< apply_ct(&mut buf, &ct); } - unblock(&buf, cbs); + let buf = unblock(buf, cbs); Ok(buf) } @@ -615,7 +640,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< fn read_write_helper(i: &mut Input, o: &mut Output) -> Result<(usize, Vec), Box> { - // -------------------------------------------------------------------- + // Local Predicate Fns ----------------------------------------------- #[inline] fn is_fast_read(i: &Input, o: &Output) -> bool { @@ -642,7 +667,7 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> { i.cflags.unblock.is_some() } - // -------------------------------------------------------------------- + // Local Helper Fns ------------------------------------------------- #[inline] fn perform_swab(buf: &mut [u8]) { @@ -655,7 +680,7 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> buf[base-1] = tmp; } } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------ if is_fast_read(&i, &o) { // TODO: fast reads are copies performed @@ -693,6 +718,30 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> } } +/// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals. +fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () +{ + move || { + + // TODO: Replace ?? with accurate info + print!("\rProgress ({}/??)", 0); + + loop + { + match rx.recv() + { + Ok(wr_total) => { + print!("\rProgress ({}/??)", wr_total); + }, + Err(_) => { + println!(""); + break + }, + } + } + } +} + /// 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. diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index e635358cd..238765888 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -35,6 +35,38 @@ macro_rules! make_block_test ( }; ); +macro_rules! make_unblock_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $unblock:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: 512, + xfer_stats: StatusLevel::None, + cflags: IConvFlags { + ctable: None, + block: None, + unblock: $unblock, + swab: false, + sync: false, + noerror: false, + }, + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: 512, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + #[test] fn block_test_no_nl() { @@ -47,7 +79,7 @@ fn block_test_no_nl() } #[test] -fn block_test_no_nl_short_rec() +fn block_test_no_nl_short_record() { let buf = vec![0u8, 1u8, 2u8, 3u8]; let res = block(buf, 8); @@ -76,9 +108,9 @@ fn block_test_nl_gt_cbs_trunc() assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], - // gnu-dd truncates this record // vec![4u8, SPACE, SPACE, SPACE], vec![0u8, 1u8, 2u8, 3u8], + // vec![4u8, SPACE, SPACE, SPACE], vec![5u8, 6u8, 7u8, 8u8], ]); } @@ -96,7 +128,7 @@ fn block_test_surrounded_nl() } #[test] -fn block_test_multiple_nl_same_block() +fn block_test_multiple_nl_same_cbs_block() { let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8]; let res = block(buf, 8); @@ -109,7 +141,7 @@ fn block_test_multiple_nl_same_block() } #[test] -fn block_test_multiple_nl_diff_block() +fn block_test_multiple_nl_diff_cbs_block() { let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8]; let res = block(buf, 8); @@ -122,7 +154,7 @@ fn block_test_multiple_nl_diff_block() } #[test] -fn block_test_lone_nl_end() +fn block_test_end_nl_diff_cbs_block() { let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; let res = block(buf, 4); @@ -133,7 +165,7 @@ fn block_test_lone_nl_end() } #[test] -fn block_test_end_nl() +fn block_test_end_nl_same_cbs_block() { let buf = vec![0u8, 1u8, 2u8, NL]; let res = block(buf, 4); @@ -143,17 +175,6 @@ fn block_test_end_nl() ]); } -#[test] -fn block_test_end_nl_new_block() -{ - let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; - let res = block(buf, 4); - - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - ]); -} - #[test] fn block_test_double_end_nl() { @@ -179,7 +200,7 @@ fn block_test_start_nl() } #[test] -fn block_test_double_nl() +fn block_test_double_surrounded_nl_no_trunc() { let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; let res = block(buf, 8); @@ -192,7 +213,7 @@ fn block_test_double_nl() } #[test] -fn block_test_double_nl_double_trunc() +fn block_test_double_surrounded_nl_double_trunc() { let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; let res = block(buf, 4); @@ -227,3 +248,85 @@ make_block_test!( Some(16), File::open("./test-resources/dd-block-consecutive-nl-cbs16.spec").unwrap() ); + +#[test] +fn unblock_test_full_cbs() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8]; + let res = unblock(buf, 8); + + assert_eq!(res, + vec![0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, NL], + ); +} + +#[test] +fn unblock_test_all_space() +{ + let buf = vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE]; + let res = unblock(buf, 8); + + assert_eq!(res, + vec![NL], + ); +} + +#[test] +fn unblock_test_decoy_spaces() +{ + let buf = vec![0u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 7u8]; + let res = unblock(buf, 8); + + assert_eq!(res, + vec![0u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 7u8, NL], + ); +} + +#[test] +fn unblock_test_strip_single_cbs() +{ + let buf = vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE]; + let res = unblock(buf, 8); + + assert_eq!(res, + vec![0u8, 1u8, 2u8, 3u8, NL], + ); +} + +#[test] +fn unblock_test_strip_multi_cbs() +{ + let buf = vec![ + vec![0u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![0u8, 1u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![0u8, 1u8, 2u8, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + ].into_iter().flatten().collect::>(); + + let res = unblock(buf, 8); + + let exp = vec![ + vec![0u8, NL], + vec![0u8, 1u8, NL], + vec![0u8, 1u8, 2u8, NL], + vec![0u8, 1u8, 2u8, 3u8, NL], + ].into_iter().flatten().collect::>(); + + assert_eq!(res, exp); +} + +make_unblock_test!( + unblock_multi_16, + "unblock-multi-16", + File::open("./test-resources/dd-unblock-cbs16.test").unwrap(), + Some(16), + File::open("./test-resources/dd-unblock-cbs16.spec").unwrap() +); + +make_unblock_test!( + unblock_multi_16_as_8, + "unblock-multi-16-as-8", + File::open("./test-resources/dd-unblock-cbs16.test").unwrap(), + Some(8), + File::open("./test-resources/dd-unblock-cbs8.spec").unwrap() +); From f7eaf96eda8530eb032ad5d8f11502edb0fd31fe Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 4 Jun 2021 11:29:41 -0700 Subject: [PATCH 22/66] Fixes bugs. Prepares for conv=sync - Splits read fn into conv=sync and standard (consecutive) versions. - Fixes bug in previous read/fill where short reads would copy to wrong position in output buffer. - Fixes bug in unit tests. Empty source would pass (since no bytes failed to match). --- src/uu/dd/src/dd.rs | 86 ++++++++++++------- .../src/dd_unit_tests/block_unblock_tests.rs | 4 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 4 +- src/uu/dd/src/dd_unit_tests/mod.rs | 6 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 42 +++++++++ src/uu/dd/src/parseargs.rs | 12 +++ src/uu/dd/src/parseargs/unit_tests.rs | 25 ------ 7 files changed, 117 insertions(+), 62 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index b9c7078e3..b02c21acb 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -36,27 +36,23 @@ const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; const SUMMARY: &str = "convert, and optionally copy, a file"; const LONG_HELP: &str = ""; -const DEFAULT_FILL_BYTE: u8 = 0xDD; -const DEFAULT_SKIP_TRIES: u8 = 3; +const DEFAULT_INIT_BYTE: u8 = 0xDD; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; // ----- Datatypes ----- -enum SrcStat -{ - Read(usize), - EOF, -} + +type Cbs = usize; /// Stores all Conv Flags that apply to the input pub struct IConvFlags { ctable: Option<&'static ConversionTable>, - block: Option, - unblock: Option, + block: Option, + unblock: Option, swab: bool, - sync: bool, + sync: Option, noerror: bool, } @@ -179,7 +175,7 @@ impl Input if let Some(amt) = skip { - let mut buf = vec![DEFAULT_FILL_BYTE; amt]; + let mut buf = vec![DEFAULT_INIT_BYTE; amt]; i.force_fill(&mut buf, amt)?; } @@ -233,43 +229,64 @@ impl Read for Input fn read(&mut self, mut buf: &mut [u8]) -> io::Result { // Read from source, ignore read errors if conv=noerror - let len = match self.src.read(&mut buf) + match self.src.read(&mut buf) { Ok(len) => - len, + Ok(len), Err(e) => if !self.cflags.noerror { - return Err(e); + Err(e) } else { - return Ok(0); + Ok(0) }, - }; - - Ok(len) + } } } impl Input { - /// Fills to a given size n, which is expected to be 'obs'. + /// Fills a given obs-sized buffer. /// Reads in increments of 'self.ibs'. - fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result> + fn fill_consecutive(&mut self, buf: &mut [u8]) -> Result> + { + let mut base_idx = 0; + + while base_idx < buf.len() + { + let low_idx = base_idx; + let up_idx = cmp::min(low_idx+self.ibs, buf.len()-1); + + let rlen = self.read(&mut buf[low_idx..=up_idx])?; + if rlen > 0 + { + base_idx += rlen; + } + else + { + break; + } + } + + Ok(base_idx) + } + + /// Fills a given obs-sized buffer. + /// Reads in increments of 'self.ibs'. + fn fill_blocks(&mut self, buf: &mut [u8]) -> Result> { let ibs = self.ibs; + let obs = buf.len(); let mut bytes_read = 0; - // TODO: Fix this! - // assert!(obs < ibs); - - for n in 0..(obs/ibs) { + for n in 0..cmp::max(obs/ibs, 1) { // fill an ibs-len slice from src - let this_read = self.read(&mut buf[n*ibs..(n+1)*ibs])?; + let rlen = self.read(&mut buf[n*ibs..(n+1)*ibs])?; - if this_read != 0 { - bytes_read += this_read; + if rlen != 0 { + bytes_read += rlen; } else { break; } @@ -691,13 +708,20 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> else { // Read - let mut buf = vec![DEFAULT_FILL_BYTE; o.obs]; - let rlen = i.fill_n(&mut buf, o.obs)?; - buf.resize(rlen, DEFAULT_FILL_BYTE); + let mut buf = vec![DEFAULT_INIT_BYTE; o.obs]; + let rlen = if let Some(ch) = i.cflags.sync + { + i.fill_blocks(&mut buf)? + } + else + { + i.fill_consecutive(&mut buf)? + }; + buf.truncate(rlen); if rlen == 0 { - return Ok((0,Vec::new())); + return Ok((0,buf)); } // Conv etc... diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index 238765888..241088e63 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -18,7 +18,7 @@ macro_rules! make_block_test ( block: $block, unblock: None, swab: false, - sync: false, + sync: None, noerror: false, }, iflags: DEFAULT_IFLAGS, @@ -50,7 +50,7 @@ macro_rules! make_unblock_test ( block: None, unblock: $unblock, swab: false, - sync: false, + sync: None, noerror: false, }, iflags: DEFAULT_IFLAGS, diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 9652a6b76..521dd11f4 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -201,7 +201,7 @@ make_icf_test!( block: None, unblock: None, swab: true, - sync: false, + sync: None, noerror: false, }, File::open("./test-resources/seq-byte-values-swapped.test").unwrap() @@ -216,7 +216,7 @@ make_icf_test!( block: None, unblock: None, swab: true, - sync: false, + sync: None, noerror: false, }, File::open("./test-resources/seq-byte-values-odd.spec").unwrap() diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index faeb13e20..7e654ec14 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -3,6 +3,7 @@ use super::*; mod sanity_tests; mod conversion_tests; mod block_unblock_tests; +mod conv_sync_tests; use std::io::prelude::*; use std::io::BufReader; @@ -72,7 +73,7 @@ macro_rules! icf ( block: None, unblock: None, swab: false, - sync: false, + sync: None, noerror: false, } }; @@ -115,9 +116,10 @@ macro_rules! make_spec_test ( dd_fileout($i,$o).unwrap(); let res = File::open($tmp_fname).unwrap(); - let res = BufReader::new(res); + assert_eq!(res.metadata().unwrap().len(), $spec.metadata().unwrap().len()); let spec = BufReader::new($spec); + let res = BufReader::new(res); for (b_res, b_spec) in res.bytes().zip(spec.bytes()) { diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index b772d6a27..f7a7ec448 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -23,3 +23,45 @@ make_spec_test!( "random-73k", File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); + +make_spec_test!( + random_73k_test_not_a_multiple_obs_gt_ibs, + "random-73k-not-a-multiple-obs-gt-ibs", + Input { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + non_ascii: false, + ibs: 521, + xfer_stats: StatusLevel::None, + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", "random-73k-not-a-multiple-obs-gt-ibs")).unwrap(), + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + format!("./test-resources/FAILED-{}.test", "random-73k-not-a-multiple-obs-gt-ibs") +); + +make_spec_test!( + random_73k_test_obs_lt_not_a_multiple_ibs, + "random-73k-obs-lt-not-a-multiple-ibs", + Input { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + non_ascii: false, + ibs: 1031, + xfer_stats: StatusLevel::None, + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", "random-73k-obs-lt-not-a-multiple-ibs")).unwrap(), + obs: 521, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + format!("./test-resources/FAILED-{}.test", "random-73k-obs-lt-not-a-multiple-ibs") +); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 5af5d0cce..f115a6a2c 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -475,6 +475,18 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result Date: Mon, 7 Jun 2021 16:13:46 -0700 Subject: [PATCH 23/66] Impl conv=sync - Adds tests where ibs causes extention - Impl conv=sync. All tests passing. --- src/uu/dd/src/dd.rs | 135 +++++++++++++----- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 97 +++++++++++++ ...d-conv-sync-ibs-1031-obs-521-deadbeef.spec | 1 + ...udd-conv-sync-ibs-1031-obs-521-random.spec | Bin 0 -> 75263 bytes ...nudd-conv-sync-ibs-1031-obs-521-zeros.spec | Bin 0 -> 4124 bytes ...d-conv-sync-ibs-521-obs-1031-deadbeef.spec | 1 + ...udd-conv-sync-ibs-521-obs-1031-random.spec | Bin 0 -> 74503 bytes ...nudd-conv-sync-ibs-521-obs-1031-zeros.spec | Bin 0 -> 4168 bytes 8 files changed, 200 insertions(+), 34 deletions(-) create mode 100644 src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs create mode 100644 src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-deadbeef.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-random.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-zeros.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-sync-ibs-521-obs-1031-deadbeef.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-sync-ibs-521-obs-1031-random.spec create mode 100644 src/uu/dd/test-resources/gnudd-conv-sync-ibs-521-obs-1031-zeros.spec diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index b02c21acb..5263a4052 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -36,7 +36,7 @@ const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; const SUMMARY: &str = "convert, and optionally copy, a file"; const LONG_HELP: &str = ""; -const DEFAULT_INIT_BYTE: u8 = 0xDD; +const BUF_INIT_BYTE: u8 = 0xDD; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; @@ -175,7 +175,7 @@ impl Input if let Some(amt) = skip { - let mut buf = vec![DEFAULT_INIT_BYTE; amt]; + let mut buf = vec![BUF_INIT_BYTE; amt]; i.force_fill(&mut buf, amt)?; } @@ -250,16 +250,16 @@ impl Input { /// Fills a given obs-sized buffer. /// Reads in increments of 'self.ibs'. - fn fill_consecutive(&mut self, buf: &mut [u8]) -> Result> + /// The start of each ibs-sized read follows the previous one. + fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> { let mut base_idx = 0; while base_idx < buf.len() { - let low_idx = base_idx; - let up_idx = cmp::min(low_idx+self.ibs, buf.len()-1); + let next_blk = cmp::min(base_idx+self.ibs, buf.len()); - let rlen = self.read(&mut buf[low_idx..=up_idx])?; + let rlen = self.read(&mut buf[base_idx..next_blk])?; if rlen > 0 { base_idx += rlen; @@ -270,29 +270,41 @@ impl Input } } + buf.truncate(base_idx); Ok(base_idx) } /// Fills a given obs-sized buffer. /// Reads in increments of 'self.ibs'. - fn fill_blocks(&mut self, buf: &mut [u8]) -> Result> + /// The start of each ibs-sized read is aligned to multiples of ibs; remaing space is filled with the 'pad' byte. + fn fill_blocks(&mut self, buf: &mut Vec, obs: usize, pad: u8) -> Result> { - let ibs = self.ibs; - let obs = buf.len(); - let mut bytes_read = 0; + let mut base_idx = 0; + let mut rbytes = 0; - for n in 0..cmp::max(obs/ibs, 1) { - // fill an ibs-len slice from src - let rlen = self.read(&mut buf[n*ibs..(n+1)*ibs])?; + while base_idx < buf.len() + { + let next_blk = cmp::min(base_idx+self.ibs, buf.len()); + let plen = next_blk - base_idx; - if rlen != 0 { - bytes_read += rlen; - } else { + let rlen = self.read(&mut buf[base_idx..next_blk])?; + + if rlen < plen + { + let padding = vec![pad; plen-rlen]; + buf.splice(base_idx+rlen..next_blk, padding.into_iter()); + } + if rlen == 0 + { break; } + + rbytes += rlen; + base_idx += self.ibs; } - Ok(bytes_read) + buf.truncate(base_idx); + Ok(rbytes) } /// Force-fills a buffer, ignoring zero-length reads which would otherwise be @@ -453,6 +465,40 @@ impl Write for Output } } +impl Output +{ + fn write_blocks(&mut self, buf: Vec) -> io::Result + { + let mut base_idx = 0; + + while base_idx < buf.len() + { + let next_blk = cmp::min(base_idx+self.obs, buf.len()); + let wlen = self.write(&buf[base_idx..next_blk])?; + base_idx += wlen; + } + + Ok(base_idx) + } +} + +impl Output +{ + fn write_blocks(&mut self, buf: Vec) -> io::Result + { + let mut base_idx = 0; + + while base_idx < buf.len() + { + let next_blk = cmp::min(base_idx+self.obs, buf.len()); + let wlen = self.write(&buf[base_idx..next_blk])?; + base_idx += wlen; + } + + Ok(base_idx) + } +} + /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N fn block(buf: Vec, cbs: usize) -> Vec> @@ -655,7 +701,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< } } -fn read_write_helper(i: &mut Input, o: &mut Output) -> Result<(usize, Vec), Box> +fn read_helper(i: &mut Input, o: &mut Output, bsize: usize) -> Result<(usize, Vec), Box> { // Local Predicate Fns ----------------------------------------------- #[inline] @@ -697,7 +743,7 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> buf[base-1] = tmp; } } - // ------------------------------------------------------------------ + // ------------------------------------------------------------------ if is_fast_read(&i, &o) { // TODO: fast reads are copies performed @@ -708,17 +754,13 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> else { // Read - let mut buf = vec![DEFAULT_INIT_BYTE; o.obs]; - let rlen = if let Some(ch) = i.cflags.sync - { - i.fill_blocks(&mut buf)? - } - else - { - i.fill_consecutive(&mut buf)? + let mut buf = vec![BUF_INIT_BYTE; bsize]; + let rlen = match i.cflags.sync { + Some(ch) => + i.fill_blocks(&mut buf, o.obs, ch)?, + _ => + i.fill_consecutive(&mut buf)?, }; - buf.truncate(rlen); - if rlen == 0 { return Ok((0,buf)); @@ -729,7 +771,6 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> { perform_swab(&mut buf); } - if is_conv(&i) || is_block(&i) || is_unblock(&i) { let buf = conv_block_unblock_helper(buf, i, o)?; @@ -742,6 +783,21 @@ fn read_write_helper(i: &mut Input, o: &mut Output) -> } } +/// Write obs-size blocks +// fn write_helper(o: &mut Output, buf: Vec) -> Result> +// { +// let mut base_idx = 0; +// +// while base_idx < buf.len() +// { +// let width = cmp::min(base_idx+o.obs, buf.len()); +// let wlen = o.write(&mut buf[base_idx..width])?; +// base_idx += wlen; +// } +// +// Ok(base_idx) +// } + /// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals. fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { @@ -766,13 +822,22 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -/// Perform the copy/convert opertaions. Non file backed output version +/// Find the greatest common factor for the pair of integers. +fn gcf(u: usize, v: usize) -> usize +{ + // TODO: 1 is not the gcf of all pairs of integers... + 1 +} + +/// Perform the copy/convert opertaions. Stdout 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 mut bytes_in = 0; let mut bytes_out = 0; + let gcf = gcf(i.ibs, o.obs); + let buf_size = (i.ibs/gcf)*(o.obs/gcf); let prog_tx = if i.xfer_stats == StatusLevel::Progress { @@ -787,13 +852,13 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usi loop { - match read_write_helper(&mut i, &mut o)? + match read_helper(&mut i, &mut o, buf_size)? { (0, _) => break, (rlen, buf) => { - let wlen = o.write(&buf)?; + let wlen = o.write_blocks(buf)?; bytes_in += rlen; bytes_out += wlen; @@ -826,6 +891,8 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, u { let mut bytes_in = 0; let mut bytes_out = 0; + let gcf = gcf(i.ibs, o.obs); + let buf_size = (i.ibs/gcf)*(o.obs/gcf); let prog_tx = if i.xfer_stats == StatusLevel::Progress { @@ -840,7 +907,7 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, u loop { - match read_write_helper(&mut i, &mut o)? + match read_helper(&mut i, &mut o, buf_size)? { (0, _) => break, diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs new file mode 100644 index 000000000..0cb939fd9 --- /dev/null +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -0,0 +1,97 @@ +use super::*; + +macro_rules! make_sync_test ( + ( $test_id:ident, $test_name:expr, $src:expr, $sync:expr, $ibs:expr, $obs:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + Input { + src: $src, + non_ascii: false, + ibs: $ibs, + xfer_stats: StatusLevel::None, + cflags: IConvFlags { + ctable: None, + block: None, + unblock: None, + swab: false, + sync: $sync, + noerror: false, + }, + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: $obs, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + +// Zeros +make_sync_test!( + zeros_4k_conv_sync_obs_gt_ibs, + "zeros_4k_conv_sync_obs_gt_ibs", + File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(), + Some(0u8), + 521, + 1031, + File::open("./test-resources/gnudd-conv-sync-ibs-521-obs-1031-zeros.spec").unwrap() +); + +make_sync_test!( + zeros_4k_conv_sync_ibs_gt_obs, + "zeros_4k_conv_sync_ibs_gt_obs", + File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(), + Some(0u8), + 1031, + 521, + File::open("./test-resources/gnudd-conv-sync-ibs-1031-obs-521-zeros.spec").unwrap() +); + +// Deadbeef +make_sync_test!( + deadbeef_32k_conv_sync_obs_gt_ibs, + "deadbeef_32k_conv_sync_obs_gt_ibs", + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), + Some(0u8), + 521, + 1031, + File::open("./test-resources/gnudd-conv-sync-ibs-521-obs-1031-deadbeef.spec").unwrap() +); + +make_sync_test!( + deadbeef_32k_conv_sync_ibs_gt_obs, + "deadbeef_32k_conv_sync_ibs_gt_obs", + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), + Some(0u8), + 1031, + 521, + File::open("./test-resources/gnudd-conv-sync-ibs-1031-obs-521-deadbeef.spec").unwrap() +); + +// Random +make_sync_test!( + random_73k_test_bs_prime_obs_gt_ibs_sync, + "random-73k-test-bs-prime-obs-gt-ibs-sync", + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + Some(0u8), + 521, + 1031, + File::open("./test-resources/gnudd-conv-sync-ibs-521-obs-1031-random.spec").unwrap() +); + +make_sync_test!( + random_73k_test_bs_prime_ibs_gt_obs_sync, + "random-73k-test-bs-prime-ibs-gt-obs-sync", + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + Some(0u8), + 1031, + 521, + File::open("./test-resources/gnudd-conv-sync-ibs-1031-obs-521-random.spec").unwrap() +); + diff --git a/src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-deadbeef.spec b/src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-deadbeef.spec new file mode 100644 index 000000000..e7bbc1c3b --- /dev/null +++ b/src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-deadbeef.spec @@ -0,0 +1 @@ +­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾ \ No newline at end of file diff --git a/src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-random.spec b/src/uu/dd/test-resources/gnudd-conv-sync-ibs-1031-obs-521-random.spec new file mode 100644 index 0000000000000000000000000000000000000000..fce1481862d1b06dbeeb9de59fcdac98ade9539e GIT binary patch literal 75263 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 Date: Tue, 8 Jun 2021 12:37:03 -0700 Subject: [PATCH 24/66] Adds additional conv=sync test for short reads. --- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 23 ++++++++++++++++++ src/uu/dd/test-resources/deadbeef-16.spec | Bin 0 -> 128 bytes src/uu/dd/test-resources/deadbeef-16.test | 1 + 3 files changed, 24 insertions(+) create mode 100644 src/uu/dd/test-resources/deadbeef-16.spec create mode 100644 src/uu/dd/test-resources/deadbeef-16.test diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 0cb939fd9..ffb095c4a 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -1,5 +1,19 @@ use super::*; +struct LazyReader +{ + src: R, +} + +impl Read for LazyReader +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result + { + let half = buf.len() / 2; + self.src.read(&mut buf[..half]) + } +} + macro_rules! make_sync_test ( ( $test_id:ident, $test_name:expr, $src:expr, $sync:expr, $ibs:expr, $obs:expr, $spec:expr ) => { @@ -95,3 +109,12 @@ make_sync_test!( File::open("./test-resources/gnudd-conv-sync-ibs-1031-obs-521-random.spec").unwrap() ); +make_sync_test!( + deadbeef_16_delayed, + "deadbeef-16-delayed", + LazyReader { src: File::open("./test-resources/deadbeef-16.test").unwrap() }, + Some(0u8), + 16, + 32, + File::open("./test-resources/deadbeef-16.spec").unwrap() +); diff --git a/src/uu/dd/test-resources/deadbeef-16.spec b/src/uu/dd/test-resources/deadbeef-16.spec new file mode 100644 index 0000000000000000000000000000000000000000..4eb7c10f190e85dbf88978936a95194d29658839 GIT binary patch literal 128 Ucmewl1q@IC<^G3q8EDN101#h4!2kdN literal 0 HcmV?d00001 diff --git a/src/uu/dd/test-resources/deadbeef-16.test b/src/uu/dd/test-resources/deadbeef-16.test new file mode 100644 index 000000000..85f2b7569 --- /dev/null +++ b/src/uu/dd/test-resources/deadbeef-16.test @@ -0,0 +1 @@ +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file From 96fd665ce15f8a26279058a791ba9106ee66c2be Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 8 Jun 2021 14:14:19 -0700 Subject: [PATCH 25/66] Implements internal read/write buffer optimization - Spoiler Alert: Turns out it was just the lcm. --- Cargo.lock | 2057 +++++++++---------- src/uu/dd/Cargo.toml | 2 +- src/uu/dd/src/dd.rs | 38 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 78 + 4 files changed, 1093 insertions(+), 1082 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3cb823561..45764d0a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,2782 +4,2729 @@ name = "advapi32-sys" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "aho-corasick" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.4", ] [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9", ] [[package]] name = "arrayvec" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", + "winapi 0.3.9", ] [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bit-set" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" dependencies = [ - "bit-vec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec", + "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" dependencies = [ - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", + "generic-array 0.8.4", ] [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.14.4", ] [[package]] name = "bstr" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "memchr 2.3.4", + "regex-automata", + "serde", ] [[package]] name = "bumpalo" version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byte-tools" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cast" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version", ] [[package]] name = "cc" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" dependencies = [ - "num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer", + "num-traits", + "time", ] [[package]] name = "clap" version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "atty", + "bitflags 1.2.1", + "strsim", + "textwrap", + "unicode-width", + "vec_map", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", ] [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "conv" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" dependencies = [ - "custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "custom_derive", ] [[package]] name = "coreutils" version = "0.0.4" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", - "conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "unindent 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "users 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uu_arch 0.0.4", - "uu_base32 0.0.4", - "uu_base64 0.0.4", - "uu_basename 0.0.4", - "uu_cat 0.0.4", - "uu_chgrp 0.0.4", - "uu_chmod 0.0.4", - "uu_chown 0.0.4", - "uu_chroot 0.0.4", - "uu_cksum 0.0.4", - "uu_comm 0.0.4", - "uu_cp 0.0.4", - "uu_csplit 0.0.4", - "uu_cut 0.0.4", - "uu_date 0.0.4", - "uu_dd 0.0.4", - "uu_df 0.0.4", - "uu_dircolors 0.0.4", - "uu_dirname 0.0.4", - "uu_du 0.0.4", - "uu_echo 0.0.4", - "uu_env 0.0.4", - "uu_expand 0.0.4", - "uu_expr 0.0.4", - "uu_factor 0.0.4", - "uu_false 0.0.4", - "uu_fmt 0.0.4", - "uu_fold 0.0.4", - "uu_groups 0.0.4", - "uu_hashsum 0.0.4", - "uu_head 0.0.4", - "uu_hostid 0.0.4", - "uu_hostname 0.0.4", - "uu_id 0.0.4", - "uu_install 0.0.4", - "uu_join 0.0.4", - "uu_kill 0.0.4", - "uu_link 0.0.4", - "uu_ln 0.0.4", - "uu_logname 0.0.4", - "uu_ls 0.0.4", - "uu_mkdir 0.0.4", - "uu_mkfifo 0.0.4", - "uu_mknod 0.0.4", - "uu_mktemp 0.0.4", - "uu_more 0.0.4", - "uu_mv 0.0.4", - "uu_nice 0.0.4", - "uu_nl 0.0.4", - "uu_nohup 0.0.4", - "uu_nproc 0.0.4", - "uu_numfmt 0.0.4", - "uu_od 0.0.4", - "uu_paste 0.0.4", - "uu_pathchk 0.0.4", - "uu_pinky 0.0.4", - "uu_printenv 0.0.4", - "uu_printf 0.0.4", - "uu_ptx 0.0.4", - "uu_pwd 0.0.4", - "uu_readlink 0.0.4", - "uu_realpath 0.0.4", - "uu_relpath 0.0.4", - "uu_rm 0.0.4", - "uu_rmdir 0.0.4", - "uu_seq 0.0.4", - "uu_shred 0.0.4", - "uu_shuf 0.0.4", - "uu_sleep 0.0.4", - "uu_sort 0.0.4", - "uu_split 0.0.4", - "uu_stat 0.0.4", - "uu_stdbuf 0.0.4", - "uu_sum 0.0.4", - "uu_sync 0.0.4", - "uu_tac 0.0.4", - "uu_tail 0.0.4", - "uu_tee 0.0.4", - "uu_test 0.0.4", - "uu_timeout 0.0.4", - "uu_touch 0.0.4", - "uu_tr 0.0.4", - "uu_true 0.0.4", - "uu_truncate 0.0.4", - "uu_tsort 0.0.4", - "uu_tty 0.0.4", - "uu_uname 0.0.4", - "uu_unexpand 0.0.4", - "uu_uniq 0.0.4", - "uu_unlink 0.0.4", - "uu_uptime 0.0.4", - "uu_users 0.0.4", - "uu_wc 0.0.4", - "uu_who 0.0.4", - "uu_whoami 0.0.4", - "uu_yes 0.0.4", - "uucore 0.0.7", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "cc", + "conv", + "filetime", + "glob 0.3.0", + "lazy_static", + "libc", + "rand 0.7.3", + "regex", + "rustc-demangle", + "same-file", + "sha1", + "tempfile", + "textwrap", + "thread_local", + "time", + "unindent", + "unix_socket", + "users", + "uu_arch", + "uu_base32", + "uu_base64", + "uu_basename", + "uu_cat", + "uu_chgrp", + "uu_chmod", + "uu_chown", + "uu_chroot", + "uu_cksum", + "uu_comm", + "uu_cp", + "uu_csplit", + "uu_cut", + "uu_date", + "uu_dd", + "uu_df", + "uu_dircolors", + "uu_dirname", + "uu_du", + "uu_echo", + "uu_env", + "uu_expand", + "uu_expr", + "uu_factor", + "uu_false", + "uu_fmt", + "uu_fold", + "uu_groups", + "uu_hashsum", + "uu_head", + "uu_hostid", + "uu_hostname", + "uu_id", + "uu_install", + "uu_join", + "uu_kill", + "uu_link", + "uu_ln", + "uu_logname", + "uu_ls", + "uu_mkdir", + "uu_mkfifo", + "uu_mknod", + "uu_mktemp", + "uu_more", + "uu_mv", + "uu_nice", + "uu_nl", + "uu_nohup", + "uu_nproc", + "uu_numfmt", + "uu_od", + "uu_paste", + "uu_pathchk", + "uu_pinky", + "uu_printenv", + "uu_printf", + "uu_ptx", + "uu_pwd", + "uu_readlink", + "uu_realpath", + "uu_relpath", + "uu_rm", + "uu_rmdir", + "uu_seq", + "uu_shred", + "uu_shuf", + "uu_sleep", + "uu_sort", + "uu_split", + "uu_stat", + "uu_stdbuf", + "uu_sum", + "uu_sync", + "uu_tac", + "uu_tail", + "uu_tee", + "uu_test", + "uu_timeout", + "uu_touch", + "uu_tr", + "uu_true", + "uu_truncate", + "uu_tsort", + "uu_tty", + "uu_uname", + "uu_unexpand", + "uu_uniq", + "uu_unlink", + "uu_uptime", + "uu_users", + "uu_wc", + "uu_who", + "uu_whoami", + "uu_yes", + "uucore", + "winapi-util", ] [[package]] name = "cpp" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4875a08600be48dcc9cb6ee07f104a3e0752e95184dede6a30044d6480bf50e8" dependencies = [ - "cpp_macros 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cpp_macros", ] [[package]] name = "cpp_build" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c47531e7e09532ad4827098729794f5e1a5b1c2ccbb5e295498d2e7ab451c445" dependencies = [ - "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_common 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_syn 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_synmap 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_synom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "cpp_common 0.4.0", + "cpp_syn", + "cpp_synmap", + "cpp_synom", + "lazy_static", ] [[package]] name = "cpp_common" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e39149a7943affa02f5b6e347ca2840a129cc78d5883ee229f0f1c4027d628" dependencies = [ - "cpp_syn 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_synom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cpp_syn", + "cpp_synom", + "lazy_static", + "quote 0.3.15", ] [[package]] name = "cpp_common" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df78ad28e5fe814285016779fb3d3b874520c799a847e6190bf2b834cc4ff283" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "proc-macro2", + "syn", ] [[package]] name = "cpp_macros" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f93a21e618c10abc84ebb63ffa5952e1f7a4568b8141d542d5ef860e4a8fc25" dependencies = [ - "aho-corasick 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_common 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "if_rust_version 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "byteorder", + "cpp_common 0.5.6", + "if_rust_version", + "lazy_static", + "proc-macro2", + "quote 1.0.9", + "syn", ] [[package]] name = "cpp_syn" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8cd649bf5b3804d92fe12a60c7698f5a538a6033ed8a668bf5241d4d4f1644e" dependencies = [ - "cpp_synom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cpp_synom", + "quote 0.3.15", + "unicode-xid 0.0.4", ] [[package]] name = "cpp_synmap" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897e4f9cdbe2874edd3ffe53718ee5d8b89e2a970057b2c93d3214104f2e90b6" dependencies = [ - "cpp_syn 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_synom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cpp_syn", + "cpp_synom", + "memchr 1.0.2", ] [[package]] name = "cpp_synom" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc8da5694233b646150c785118f77835ad0a49680c7f312a10ef30957c67b6d" dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4", ] [[package]] name = "criterion" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion-plot 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "oorandom 11.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "plotters 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_cbor 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "tinytemplate 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools 0.10.0", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", ] [[package]] name = "criterion-plot" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" dependencies = [ - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cast", + "itertools 0.9.0", ] [[package]] name = "crossbeam-channel" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "cfg-if 1.0.0", + "lazy_static", ] [[package]] name = "csv" version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" dependencies = [ - "bstr 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", - "csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] name = "csv-core" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" dependencies = [ - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.4", ] [[package]] name = "custom_derive" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" [[package]] name = "data-encoding" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" [[package]] name = "digest" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" dependencies = [ - "generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.8.4", ] [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.14.4", ] [[package]] name = "dunce" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2641c4a7c0c4101df53ea572bffdc561c146f6c2eb09e4df02bc4811e3feeb4" [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ - "log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "regex", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "filetime" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.5", + "winapi 0.3.9", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fs_extra" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcd" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7cd301bf2ab11ae4e5bdfd79c221d97a25e46c089144a62ee9d09cb32d2b92" [[package]] name = "generic-array" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2297fb0e3ea512e380da24b52dca3924028f59df5e3a17a18f81d8349ca7ebe" dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop", + "typenum", ] [[package]] name = "generic-array" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ - "typenum 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", + "version_check", ] [[package]] name = "getopts" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "getrandom" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "getrandom" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.10.2+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "wasi", ] [[package]] name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "half" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "hermit-abi" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "hex" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" [[package]] name = "hex-literal" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" [[package]] name = "hostname" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "match_cfg", + "winapi 0.3.9", ] [[package]] name = "if_rust_version" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46dbcb333e86939721589d25a3557e180b52778cb33c7fdfe9e0158ff790d5ec" [[package]] name = "ioctl-sys" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a" [[package]] name = "itertools" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" dependencies = [ - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itertools" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" dependencies = [ - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itertools" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" dependencies = [ - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" dependencies = [ - "wasm-bindgen 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" [[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "md-5" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" dependencies = [ - "block-buffer 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", ] [[package]] name = "md5" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c56d6a0b07f9e19282511c83fc5b086364cbae4ba8c7d5f190c3d9b0425a48" [[package]] name = "memchr" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memoffset" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "nix" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487" dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0", + "cfg-if 0.1.10", + "libc", + "void", ] [[package]] name = "nix" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dbdc256eaac2e3bd236d93ad999d3479ef775c863dbda3068c4006a92eec51b" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "cc", + "cfg-if 0.1.10", + "libc", + "void", ] [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num-integer" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-traits" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ - "hermit-abi 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", ] [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "numtoa" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" [[package]] name = "onig" version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8518fcb2b1b8c2f45f0ad499df4fda6087fc3475ca69a185c173b8315d2fb383" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "lazy_static", + "libc", + "onig_sys", ] [[package]] name = "onig_sys" version = "69.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388410bf5fa341f10e58e6db3975f4bea1ac30247dd79d37a9e5ced3cb4cc3b0" dependencies = [ - "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "pkg-config", ] [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "paste" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" dependencies = [ - "paste-impl 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.19 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl", + "proc-macro-hack", ] [[package]] name = "paste-impl" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" dependencies = [ - "proc-macro-hack 0.5.19 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", ] [[package]] name = "pkg-config" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "platform-info" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ea9cd21d89bffb387b6c7363d23bead0807be9de676c671b474dd29e7436d3" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.9", ] [[package]] name = "plotters" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" dependencies = [ - "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "plotters-backend 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "plotters-svg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" [[package]] name = "plotters-svg" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" dependencies = [ - "plotters-backend 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plotters-backend", ] [[package]] name = "ppv-lite86" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro-hack" version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.1", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quickcheck" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger", + "log", + "rand 0.7.3", + "rand_core 0.5.1", ] [[package]] name = "quote" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi 0.3.9", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "ppv-lite86 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ppv-lite86 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rand_pcg" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rayon" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", ] [[package]] name = "rayon-core" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ - "crossbeam-channel 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", ] [[package]] name = "redox_termios" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" dependencies = [ - "redox_syscall 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.2.5", ] [[package]] name = "regex" version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54fd1046a3107eb58f42de31d656fee6853e5d276c455fd943742dce89fc3dd3" dependencies = [ - "aho-corasick 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.23 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr 2.3.4", + "regex-syntax", ] [[package]] name = "regex-automata" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "regex-syntax" version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9", ] [[package]] name = "rust-ini" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "same-file" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" dependencies = [ - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" [[package]] name = "serde_cbor" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" dependencies = [ - "half 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", + "half", + "serde", ] [[package]] name = "serde_derive" version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote 1.0.9", + "syn", ] [[package]] name = "serde_json" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ - "itoa 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "sha1" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a" dependencies = [ - "block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.2.0", + "byte-tools", + "digest 0.6.2", + "fake-simd", + "generic-array 0.8.4", ] [[package]] name = "sha3" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26405905b6a56a94c60109cfda62610507ac14a65be531f5767dec5c5a8dd6a0" dependencies = [ - "block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.2.0", + "byte-tools", + "digest 0.6.2", + "generic-array 0.8.4", ] [[package]] name = "smallvec" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" dependencies = [ - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit", ] [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote 1.0.9", + "unicode-xid 0.2.1", ] [[package]] name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "rand 0.7.3", + "redox_syscall 0.1.57", + "remove_dir_all", + "winapi 0.3.9", ] [[package]] name = "term_grid" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf" dependencies = [ - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "term_size" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.9", ] [[package]] name = "termion" version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "numtoa", + "redox_syscall 0.2.5", + "redox_termios", ] [[package]] name = "termsize" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e86d824a8e90f342ad3ef4bd51ef7119a9b681b0cc9f8ee7b2852f02ccd2517" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "kernel32-sys", + "libc", + "termion", + "winapi 0.2.8", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "term_size 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "term_size", + "unicode-width", ] [[package]] name = "thiserror" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" dependencies = [ - "thiserror-impl 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote 1.0.9", + "syn", ] [[package]] name = "thread_local" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_syscall 0.1.57", + "winapi 0.3.9", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_json", ] [[package]] name = "typenum" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "unindent" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" [[package]] name = "unix_socket" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", ] [[package]] name = "users" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "log", ] [[package]] name = "uu_arch" version = "0.0.4" dependencies = [ - "platform-info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "platform-info", + "uucore", + "uucore_procs", ] [[package]] name = "uu_base32" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_base64" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_basename" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_cat" version = "0.0.4" dependencies = [ - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "quick-error", + "unix_socket", + "uucore", + "uucore_procs", ] [[package]] name = "uu_chgrp" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uucore", + "uucore_procs", + "walkdir", ] [[package]] name = "uu_chmod" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "uucore", + "uucore_procs", + "walkdir", ] [[package]] name = "uu_chown" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "glob 0.3.0", + "uucore", + "uucore_procs", + "walkdir", ] [[package]] name = "uu_chroot" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "uucore", + "uucore_procs", ] [[package]] name = "uu_cksum" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_comm" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_cp" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "filetime", + "ioctl-sys", + "libc", + "quick-error", + "uucore", + "uucore_procs", + "walkdir", + "winapi 0.3.9", + "xattr", ] [[package]] name = "uu_csplit" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "glob 0.2.11", + "regex", + "thiserror", + "uucore", + "uucore_procs", ] [[package]] name = "uu_cut" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_date" version = "0.0.4" dependencies = [ - "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "chrono", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_dd" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "md-5 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "gcd", + "getopts", + "hex-literal", + "md-5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_df" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "number_prefix 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "libc", + "number_prefix", + "uucore", + "uucore_procs", + "winapi 0.3.9", ] [[package]] name = "uu_dircolors" version = "0.0.4" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "glob 0.3.0", + "uucore", + "uucore_procs", ] [[package]] name = "uu_dirname" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_du" version = "0.0.4" dependencies = [ - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "time", + "uucore", + "uucore_procs", ] [[package]] name = "uu_echo" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_env" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "libc", + "rust-ini", + "uucore", + "uucore_procs", ] [[package]] name = "uu_expand" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "unicode-width", + "uucore", + "uucore_procs", ] [[package]] name = "uu_expr" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "onig 4.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "onig", + "uucore", + "uucore_procs", ] [[package]] name = "uu_factor" version = "0.0.4" dependencies = [ - "criterion 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "criterion", + "num-traits", + "paste", + "quickcheck", + "rand 0.7.3", + "rand_chacha", + "smallvec", + "uucore", + "uucore_procs", ] [[package]] name = "uu_false" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_fmt" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "libc", + "unicode-width", + "uucore", + "uucore_procs", ] [[package]] name = "uu_fold" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_groups" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_hashsum" version = "0.0.4" dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.23 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "blake2-rfc", + "clap", + "digest 0.6.2", + "hex", + "libc", + "md5", + "regex", + "regex-syntax", + "sha1", + "sha2", + "sha3", + "uucore", + "uucore_procs", ] [[package]] name = "uu_head" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_hostid" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_hostname" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "hostname", + "libc", + "uucore", + "uucore_procs", + "winapi 0.3.9", ] [[package]] name = "uu_id" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_install" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "libc", + "time", + "uucore", + "uucore_procs", ] [[package]] name = "uu_join" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_kill" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_link" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_ln" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_logname" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_ls" version = "0.0.4" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "number_prefix 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term_grid 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "termsize 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "atty", + "getopts", + "lazy_static", + "number_prefix", + "term_grid", + "termsize", + "time", + "unicode-width", + "uucore", + "uucore_procs", ] [[package]] name = "uu_mkdir" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_mkfifo" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_mknod" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_mktemp" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "rand 0.5.6", + "tempfile", + "uucore", + "uucore_procs", ] [[package]] name = "uu_more" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "nix 0.8.1", + "redox_syscall 0.1.57", + "redox_termios", + "uucore", + "uucore_procs", ] [[package]] name = "uu_mv" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fs_extra 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "fs_extra", + "uucore", + "uucore_procs", ] [[package]] name = "uu_nice" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_nl" version = "0.0.4" dependencies = [ - "aho-corasick 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.23 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "aho-corasick", + "getopts", + "libc", + "memchr 2.3.4", + "regex", + "regex-syntax", + "uucore", + "uucore_procs", ] [[package]] name = "uu_nohup" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_nproc" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "libc", + "num_cpus", + "uucore", + "uucore_procs", ] [[package]] name = "uu_numfmt" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_od" version = "0.0.4" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "half 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "byteorder", + "getopts", + "half", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_paste" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_pathchk" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_pinky" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_printenv" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_printf" version = "0.0.4" dependencies = [ - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "itertools 0.8.2", + "uucore", + "uucore_procs", ] [[package]] name = "uu_ptx" version = "0.0.4" dependencies = [ - "aho-corasick 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.23 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "aho-corasick", + "getopts", + "libc", + "memchr 2.3.4", + "regex", + "regex-syntax", + "uucore", + "uucore_procs", ] [[package]] name = "uu_pwd" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_readlink" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_realpath" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_relpath" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "uucore", + "uucore_procs", ] [[package]] name = "uu_rm" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "remove_dir_all", + "uucore", + "uucore_procs", + "walkdir", ] [[package]] name = "uu_rmdir" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_seq" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_shred" version = "0.0.4" dependencies = [ - "filetime 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "filetime", + "getopts", + "libc", + "rand 0.5.6", + "time", + "uucore", + "uucore_procs", ] [[package]] name = "uu_shuf" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "rand 0.5.6", + "uucore", + "uucore_procs", ] [[package]] name = "uu_sleep" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_sort" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "itertools 0.8.2", + "semver", + "uucore", + "uucore_procs", ] [[package]] name = "uu_split" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_stat" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "time", + "uucore", + "uucore_procs", ] [[package]] name = "uu_stdbuf" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uu_stdbuf_libstdbuf 0.0.4", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "tempfile", + "uu_stdbuf_libstdbuf", + "uucore", + "uucore_procs", ] [[package]] name = "uu_stdbuf_libstdbuf" version = "0.0.4" dependencies = [ - "cpp 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cpp_build 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "cpp", + "cpp_build", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_sum" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "uucore", + "uucore_procs", ] [[package]] name = "uu_sync" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "libc", + "uucore", + "uucore_procs", + "winapi 0.3.9", ] [[package]] name = "uu_tac" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "uucore", + "uucore_procs", ] [[package]] name = "uu_tail" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "libc", + "redox_syscall 0.1.57", + "uucore", + "uucore_procs", + "winapi 0.3.9", ] [[package]] name = "uu_tee" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_test" version = "0.0.4" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "libc", + "redox_syscall 0.1.57", + "uucore", + "uucore_procs", ] [[package]] name = "uu_timeout" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_touch" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "filetime", + "time", + "uucore", + "uucore_procs", ] [[package]] name = "uu_tr" version = "0.0.4" dependencies = [ - "bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "bit-set", + "fnv", + "getopts", + "uucore", + "uucore_procs", ] [[package]] name = "uu_true" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_truncate" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_tsort" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "uucore", + "uucore_procs", ] [[package]] name = "uu_tty" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_uname" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "platform-info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "platform-info", + "uucore", + "uucore_procs", ] [[package]] name = "uu_unexpand" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "unicode-width", + "uucore", + "uucore_procs", ] [[package]] name = "uu_uniq" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_unlink" version = "0.0.4" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "getopts", + "libc", + "uucore", + "uucore_procs", ] [[package]] name = "uu_uptime" version = "0.0.4" dependencies = [ - "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "chrono", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_users" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_wc" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uu_who" version = "0.0.4" dependencies = [ - "uucore 0.0.7", - "uucore_procs 0.0.5", + "uucore", + "uucore_procs", ] [[package]] name = "uu_whoami" version = "0.0.4" dependencies = [ - "advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "advapi32-sys", + "clap", + "uucore", + "uucore_procs", + "winapi 0.3.9", ] [[package]] name = "uu_yes" version = "0.0.4" dependencies = [ - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uucore 0.0.7", - "uucore_procs 0.0.5", + "clap", + "uucore", + "uucore_procs", ] [[package]] name = "uucore" version = "0.0.7" dependencies = [ - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dunce 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "platform-info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "wild 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "data-encoding", + "dunce", + "getopts", + "lazy_static", + "libc", + "nix 0.13.1", + "platform-info", + "termion", + "thiserror", + "time", + "wild", ] [[package]] name = "uucore_procs" version = "0.0.5" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote 1.0.9", + "syn", ] [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ - "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file", + "winapi 0.3.9", + "winapi-util", ] [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" dependencies = [ - "bumpalo 3.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote 1.0.9", + "syn", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" dependencies = [ - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.9", + "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote 1.0.9", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" [[package]] name = "web-sys" version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" dependencies = [ - "js-sys 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "wasm-bindgen", ] [[package]] name = "wild" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "035793abb854745033f01a07647a79831eba29ec0be377205f2a25b0aa830020" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0", ] [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xattr" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" dependencies = [ - "libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] - -[metadata] -"checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" -"checksum aho-corasick 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -"checksum bit-vec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" -"checksum block-buffer 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -"checksum bstr 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" -"checksum bumpalo 3.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" -"checksum cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)" = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" -"checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" -"checksum cpp 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4875a08600be48dcc9cb6ee07f104a3e0752e95184dede6a30044d6480bf50e8" -"checksum cpp_build 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c47531e7e09532ad4827098729794f5e1a5b1c2ccbb5e295498d2e7ab451c445" -"checksum cpp_common 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79e39149a7943affa02f5b6e347ca2840a129cc78d5883ee229f0f1c4027d628" -"checksum cpp_common 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "df78ad28e5fe814285016779fb3d3b874520c799a847e6190bf2b834cc4ff283" -"checksum cpp_macros 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f93a21e618c10abc84ebb63ffa5952e1f7a4568b8141d542d5ef860e4a8fc25" -"checksum cpp_syn 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8cd649bf5b3804d92fe12a60c7698f5a538a6033ed8a668bf5241d4d4f1644e" -"checksum cpp_synmap 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "897e4f9cdbe2874edd3ffe53718ee5d8b89e2a970057b2c93d3214104f2e90b6" -"checksum cpp_synom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc8da5694233b646150c785118f77835ad0a49680c7f312a10ef30957c67b6d" -"checksum criterion 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" -"checksum criterion-plot 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" -"checksum crossbeam-channel 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" -"checksum crossbeam-deque 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" -"checksum crossbeam-epoch 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" -"checksum crossbeam-utils 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" -"checksum csv 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -"checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" -"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" -"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" -"checksum digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -"checksum dunce 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b2641c4a7c0c4101df53ea572bffdc561c146f6c2eb09e4df02bc4811e3feeb4" -"checksum either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum filetime 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" -"checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -"checksum fs_extra 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum generic-array 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -"checksum generic-array 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2297fb0e3ea512e380da24b52dca3924028f59df5e3a17a18f81d8349ca7ebe" -"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -"checksum getrandom 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -"checksum getrandom 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -"checksum half 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" -"checksum hermit-abi 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" -"checksum hex-literal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" -"checksum hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -"checksum if_rust_version 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46dbcb333e86939721589d25a3557e180b52778cb33c7fdfe9e0158ff790d5ec" -"checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a" -"checksum itertools 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" -"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" -"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -"checksum itoa 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -"checksum js-sys 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)" = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)" = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" -"checksum log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -"checksum match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum md-5 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -"checksum md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "79c56d6a0b07f9e19282511c83fc5b086364cbae4ba8c7d5f190c3d9b0425a48" -"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" -"checksum memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" -"checksum memoffset 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" -"checksum nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbdc256eaac2e3bd236d93ad999d3479ef775c863dbda3068c4006a92eec51b" -"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -"checksum num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -"checksum number_prefix 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" -"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" -"checksum onig 4.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8518fcb2b1b8c2f45f0ad499df4fda6087fc3475ca69a185c173b8315d2fb383" -"checksum onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388410bf5fa341f10e58e6db3975f4bea1ac30247dd79d37a9e5ced3cb4cc3b0" -"checksum oorandom 11.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -"checksum opaque-debug 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -"checksum paste 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" -"checksum paste-impl 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" -"checksum pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" -"checksum platform-info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16ea9cd21d89bffb387b6c7363d23bead0807be9de676c671b474dd29e7436d3" -"checksum plotters 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" -"checksum plotters-backend 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" -"checksum plotters-svg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" -"checksum ppv-lite86 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" -"checksum proc-macro-hack 0.5.19 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -"checksum proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" -"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -"checksum quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" -"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -"checksum rand_chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_hc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -"checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -"checksum rayon 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" -"checksum rayon-core 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" -"checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -"checksum redox_syscall 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" -"checksum redox_termios 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" -"checksum regex 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "54fd1046a3107eb58f42de31d656fee6853e5d276c455fd943742dce89fc3dd3" -"checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -"checksum regex-syntax 0.6.23 (registry+https://github.com/rust-lang/crates.io-index)" = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" -"checksum remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -"checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" -"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)" = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" -"checksum serde_cbor 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" -"checksum serde_derive 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)" = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" -"checksum serde_json 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" -"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -"checksum sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a" -"checksum sha3 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26405905b6a56a94c60109cfda62610507ac14a65be531f5767dec5c5a8dd6a0" -"checksum smallvec 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum term_grid 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf" -"checksum term_size 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" -"checksum termion 1.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" -"checksum termsize 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5e86d824a8e90f342ad3ef4bd51ef7119a9b681b0cc9f8ee7b2852f02ccd2517" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" -"checksum thiserror-impl 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" -"checksum thread_local 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tinytemplate 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -"checksum typenum 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" -"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -"checksum unindent 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" -"checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" -"checksum users 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" -"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -"checksum version_check 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -"checksum wasi 0.10.2+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasm-bindgen 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" -"checksum wasm-bindgen-backend 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" -"checksum wasm-bindgen-macro 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" -"checksum wasm-bindgen-macro-support 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" -"checksum wasm-bindgen-shared 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" -"checksum web-sys 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)" = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" -"checksum wild 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "035793abb854745033f01a07647a79831eba29ec0be377205f2a25b0aa830020" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 6c2263fdb..f521b6824 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -19,11 +19,11 @@ uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } # Probably best to keep this identical to the version of getopts in the uucore crate getopts = "<= 0.2.21" +gcd = "2.0" [dev-dependencies] md-5 = "0.9" hex-literal = "0.3" -rand = "0.8" [[bin]] name = "dd" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 5263a4052..884dfd3ff 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -18,6 +18,7 @@ mod parseargs; mod conversion_tables; use conversion_tables::*; +use gcd::Gcd; use std::cmp; use std::convert::TryInto; use std::error::Error; @@ -783,21 +784,6 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us } } -/// Write obs-size blocks -// fn write_helper(o: &mut Output, buf: Vec) -> Result> -// { -// let mut base_idx = 0; -// -// while base_idx < buf.len() -// { -// let width = cmp::min(base_idx+o.obs, buf.len()); -// let wlen = o.write(&mut buf[base_idx..width])?; -// base_idx += wlen; -// } -// -// Ok(base_idx) -// } - /// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals. fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { @@ -822,11 +808,13 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () } } -/// Find the greatest common factor for the pair of integers. -fn gcf(u: usize, v: usize) -> usize +#[inline] +fn calc_bsize(ibs: usize, obs: usize) -> usize { - // TODO: 1 is not the gcf of all pairs of integers... - 1 + let gcd = Gcd::gcd(ibs, obs); + let lcm = (ibs*obs)/gcd; + + lcm } /// Perform the copy/convert opertaions. Stdout version @@ -836,8 +824,7 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usi { let mut bytes_in = 0; let mut bytes_out = 0; - let gcf = gcf(i.ibs, o.obs); - let buf_size = (i.ibs/gcf)*(o.obs/gcf); + let bsize = calc_bsize(i.ibs, o.obs); let prog_tx = if i.xfer_stats == StatusLevel::Progress { @@ -852,7 +839,7 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usi loop { - match read_helper(&mut i, &mut o, buf_size)? + match read_helper(&mut i, &mut o, bsize)? { (0, _) => break, @@ -891,8 +878,7 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, u { let mut bytes_in = 0; let mut bytes_out = 0; - let gcf = gcf(i.ibs, o.obs); - let buf_size = (i.ibs/gcf)*(o.obs/gcf); + let bsize = calc_bsize(i.ibs, o.obs); let prog_tx = if i.xfer_stats == StatusLevel::Progress { @@ -907,13 +893,13 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, u loop { - match read_helper(&mut i, &mut o, buf_size)? + match read_helper(&mut i, &mut o, bsize)? { (0, _) => break, (rlen, buf) => { - let wlen = o.write(&buf)?; + let wlen = o.write_blocks(buf)?; bytes_in += rlen; bytes_out += wlen; diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index f7a7ec448..6459ad41e 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -65,3 +65,81 @@ make_spec_test!( File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), format!("./test-resources/FAILED-{}.test", "random-73k-obs-lt-not-a-multiple-ibs") ); + +// Test internal buffer size fn +#[test] +fn bsize_test_primes() +{ + let (n,m) = (7901, 7919); + let res = calc_bsize(n, m); + assert!(res % n == 0); + assert!(res % m == 0); + + assert_eq!(res, n*m); +} + +#[test] +fn bsize_test_rel_prime_obs_greater() +{ + let (n,m) = (7*5119, 13*5119); + let res = calc_bsize(n, m); + assert!(res % n == 0); + assert!(res % m == 0); + + assert_eq!(res, 7*13*5119); +} + +#[test] +fn bsize_test_rel_prime_ibs_greater() +{ + let (n,m) = (13*5119, 7*5119); + let res = calc_bsize(n, m); + assert!(res % n == 0); + assert!(res % m == 0); + + assert_eq!(res, 7*13*5119); +} + +#[test] +fn bsize_test_3fac_rel_prime() +{ + let (n,m) = (11*13*5119, 7*11*5119); + let res = calc_bsize(n, m); + assert!(res % n == 0); + assert!(res % m == 0); + + assert_eq!(res, 7*11*13*5119); +} + +#[test] +fn bsize_test_ibs_greater() +{ + let (n,m) = (512*1024, 256*1024); + let res = calc_bsize(n, m); + assert!(res % n == 0); + assert!(res % m == 0); + + assert_eq!(res, n); +} + +#[test] +fn bsize_test_obs_greater() +{ + let (n,m) = (256*1024, 512*1024); + let res = calc_bsize(n, m); + assert!(res % n == 0); + assert!(res % m == 0); + + assert_eq!(res, m); +} + +#[test] +fn bsize_test_bs_eq() +{ + let (n,m) = (1024, 1024); + let res = calc_bsize(n, m); + assert!(res % n == 0); + assert!(res % m == 0); + + assert_eq!(res, m); +} From a511db504b9553493a3be14988427e15390f1700 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 8 Jun 2021 14:33:48 -0700 Subject: [PATCH 26/66] Minor optimization in calculation of lcm for internal r/w buffer. --- src/uu/dd/src/dd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 884dfd3ff..fb12086f2 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -812,7 +812,7 @@ fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () fn calc_bsize(ibs: usize, obs: usize) -> usize { let gcd = Gcd::gcd(ibs, obs); - let lcm = (ibs*obs)/gcd; + let lcm = (ibs/gcd)*obs; lcm } From fc110bb656062ea8eabc4970cb9db2eac72f21ec Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 11 Jun 2021 17:00:25 -0700 Subject: [PATCH 27/66] Implements status=LEVEL - Adds print fn's - Modifies internal fn's as needed to track read/write state - Modifies status update thread to respect status level - Adds signal handler for SIGUSR1 (print xfer stats) --- Cargo.lock | 43 ++ src/uu/dd/Cargo.toml | 7 +- src/uu/dd/src/dd.rs | 488 +++++++++++++----- .../src/dd_unit_tests/block_unblock_tests.rs | 61 ++- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 6 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 8 +- src/uu/dd/src/dd_unit_tests/mod.rs | 2 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 4 +- src/uu/dd/src/parseargs.rs | 2 +- 9 files changed, 476 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45764d0a7..779d4b99e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,6 +134,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" +[[package]] +name = "byte-unit" +version = "4.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063197e6eb4b775b64160dedde7a0986bb2836cce140e9492e9e96f28e18bcd8" +dependencies = [ + "utf8-width", +] + [[package]] name = "byteorder" version = "1.3.4" @@ -548,6 +557,12 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" +[[package]] +name = "debug_print" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f215f9b7224f49fb73256115331f677d868b34d18b65dbe4db392e6021eea90" + [[package]] name = "digest" version = "0.6.2" @@ -1370,6 +1385,25 @@ dependencies = [ "generic-array 0.8.4", ] +[[package]] +name = "signal-hook" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "smallvec" version = "0.6.14" @@ -1564,6 +1598,12 @@ dependencies = [ "log", ] +[[package]] +name = "utf8-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b" + [[package]] name = "uu_arch" version = "0.0.4" @@ -1715,10 +1755,13 @@ dependencies = [ name = "uu_dd" version = "0.0.4" dependencies = [ + "byte-unit", + "debug_print", "gcd", "getopts", "hex-literal", "md-5", + "signal-hook", "uucore", "uucore_procs", ] diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index f521b6824..ce53439b2 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -15,11 +15,14 @@ edition = "2018" path = "src/dd.rs" [dependencies] -uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } +byte-unit = "4.0" +debug_print = "1.0" # Probably best to keep this identical to the version of getopts in the uucore crate getopts = "<= 0.2.21" gcd = "2.0" +signal-hook = "0.3.9" +uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] md-5 = "0.9" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index fb12086f2..def82aafb 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -18,31 +18,61 @@ mod parseargs; mod conversion_tables; use conversion_tables::*; +use byte_unit::Byte; +#[macro_use] +use debug_print::debug_println; use gcd::Gcd; +use getopts; +use signal_hook::consts::signal; use std::cmp; use std::convert::TryInto; use std::error::Error; +use std::env; use std::fs::{ File, OpenOptions, }; -use getopts; use std::io::{ self, Read, Write, Seek, }; -use std::sync::mpsc; +use std::sync::{ + Arc, atomic::AtomicUsize, mpsc, atomic::Ordering, +}; use std::thread; +use std::time; const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; const SUMMARY: &str = "convert, and optionally copy, a file"; const LONG_HELP: &str = ""; - const BUF_INIT_BYTE: u8 = 0xDD; - const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; // ----- Datatypes ----- +struct ProgUpdate +{ + reads_complete: u64, + reads_partial: u64, + writes_complete: u64, + writes_partial: u64, + bytes_total: u128, + records_truncated: u32, + duration: time::Duration, +} + +struct ReadStat +{ + reads_complete: u64, + reads_partial: u64, + records_truncated: u32, +} + +struct WriteStat +{ + writes_complete: u64, + writes_partial: u64, + bytes_total: u128, +} type Cbs = usize; @@ -112,7 +142,7 @@ pub struct OFlags /// The value of the status cl-option. /// Controls printing of transfer stats -#[derive(PartialEq)] +#[derive(Copy, Clone, PartialEq)] pub enum StatusLevel { Progress, @@ -149,7 +179,7 @@ struct Input src: R, non_ascii: bool, ibs: usize, - xfer_stats: StatusLevel, + xfer_stats: Option, cflags: IConvFlags, iflags: IFlags, } @@ -252,60 +282,96 @@ impl Input /// Fills a given obs-sized buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read follows the previous one. - fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> + fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> { + let mut reads_complete = 0; + let mut reads_partial = 0; let mut base_idx = 0; while base_idx < buf.len() { let next_blk = cmp::min(base_idx+self.ibs, buf.len()); - let rlen = self.read(&mut buf[base_idx..next_blk])?; - if rlen > 0 + match self.read(&mut buf[base_idx..next_blk])? { - base_idx += rlen; - } - else - { - break; + rlen if rlen == self.ibs => + { + base_idx += rlen; + reads_complete += 1; + }, + rlen if rlen > 0 => + { + base_idx += rlen; + reads_partial += 1; + }, + _ => + break, } } buf.truncate(base_idx); - Ok(base_idx) + Ok(ReadStat { + reads_complete, + reads_partial, + records_truncated: 0, + }) } /// Fills a given obs-sized buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read is aligned to multiples of ibs; remaing space is filled with the 'pad' byte. - fn fill_blocks(&mut self, buf: &mut Vec, obs: usize, pad: u8) -> Result> + fn fill_blocks(&mut self, buf: &mut Vec, obs: usize, pad: u8) -> Result> { + let mut reads_complete = 0; + let mut reads_partial = 0; let mut base_idx = 0; - let mut rbytes = 0; while base_idx < buf.len() { let next_blk = cmp::min(base_idx+self.ibs, buf.len()); let plen = next_blk - base_idx; - let rlen = self.read(&mut buf[base_idx..next_blk])?; - - if rlen < plen + match self.read(&mut buf[base_idx..next_blk])? { - let padding = vec![pad; plen-rlen]; - buf.splice(base_idx+rlen..next_blk, padding.into_iter()); - } - if rlen == 0 - { - break; + 0 => + break, + rlen if rlen < plen => + { + reads_partial += 1; + let padding = vec![pad; plen-rlen]; + buf.splice(base_idx+rlen..next_blk, padding.into_iter()); + }, + _ => + { + reads_complete += 1; + }, } + // TODO: Why does this cause the conv=sync tests to hang? + // let rlen = self.read(&mut buf[base_idx..next_blk])?; + // if rlen < plen + // { + // reads_partial += 1; + // let padding = vec![pad; plen-rlen]; + // buf.splice(base_idx+rlen..next_blk, padding.into_iter()); + // } + // else + // { + // reads_complete += 1; + // } + // if rlen == 0 + // { + // break; + // } - rbytes += rlen; base_idx += self.ibs; } buf.truncate(base_idx); - Ok(rbytes) + Ok(ReadStat { + reads_complete, + reads_partial, + records_truncated: 0, + }) } /// Force-fills a buffer, ignoring zero-length reads which would otherwise be @@ -468,46 +534,84 @@ impl Write for Output impl Output { - fn write_blocks(&mut self, buf: Vec) -> io::Result + fn write_blocks(&mut self, buf: Vec) -> io::Result { + let mut writes_complete = 0; + let mut writes_partial = 0; let mut base_idx = 0; while base_idx < buf.len() { let next_blk = cmp::min(base_idx+self.obs, buf.len()); - let wlen = self.write(&buf[base_idx..next_blk])?; - base_idx += wlen; + let plen = next_blk - base_idx; + + match self.write(&buf[base_idx..next_blk])? + { + wlen if wlen < plen => + { + writes_partial += 1; + base_idx += wlen; + }, + wlen => + { + writes_partial += 1; + base_idx += wlen; + }, + } } - Ok(base_idx) + Ok(WriteStat { + writes_complete, + writes_partial, + bytes_total: base_idx.try_into().unwrap_or(0u128), + }) } } impl Output { - fn write_blocks(&mut self, buf: Vec) -> io::Result + fn write_blocks(&mut self, buf: Vec) -> io::Result { + let mut writes_complete = 0; + let mut writes_partial = 0; let mut base_idx = 0; while base_idx < buf.len() { let next_blk = cmp::min(base_idx+self.obs, buf.len()); let wlen = self.write(&buf[base_idx..next_blk])?; + + if wlen == self.obs + { + writes_complete += 1; + } + else + { + writes_partial += 1; + } base_idx += wlen; } - Ok(base_idx) + Ok(WriteStat { + writes_complete, + writes_partial, + bytes_total: base_idx.try_into().unwrap_or(0u128), + }) } } /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N -fn block(buf: Vec, cbs: usize) -> Vec> +fn block(buf: Vec, cbs: usize, rstats: &mut ReadStat) -> Vec> { let mut blocks = buf.split(| &e | e == '\n' as u8) .fold(Vec::new(), | mut blocks, split | { let mut split = split.to_vec(); + if split.len() > cbs + { + rstats.records_truncated += 1; + } split.resize(cbs, ' ' as u8); blocks.push(split); @@ -561,7 +665,7 @@ fn unblock(buf: Vec, cbs: usize) -> Vec block } - else if let Some(32u8) = block.get(0) + else if let Some(32u8/* ' ' as u8 */) = block.get(0) { vec!['\n' as u8] } @@ -579,7 +683,7 @@ fn unblock(buf: Vec, cbs: usize) -> Vec .collect() } -fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output) -> Result, Box> +fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output, rstats: &mut ReadStat) -> Result, Box> { // Local Predicate Fns ------------------------------------------------- #[inline] @@ -633,7 +737,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< { // ascii input so perform the block first let cbs = i.cflags.block.unwrap(); - let mut blocks = block(buf, cbs); + let mut blocks = block(buf, cbs, rstats); if let Some(ct) = i.cflags.ctable { @@ -658,7 +762,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< apply_ct(&mut buf, &ct); } - let blocks = block(buf, cbs) + let blocks = block(buf, cbs, rstats) .into_iter() .flatten() .collect(); @@ -702,7 +806,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< } } -fn read_helper(i: &mut Input, o: &mut Output, bsize: usize) -> Result<(usize, Vec), Box> +fn read_helper(i: &mut Input, o: &mut Output, bsize: usize) -> Result<(ReadStat, Vec), Box> { // Local Predicate Fns ----------------------------------------------- #[inline] @@ -756,58 +860,151 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us { // Read let mut buf = vec![BUF_INIT_BYTE; bsize]; - let rlen = match i.cflags.sync { + let mut rstats = match i.cflags.sync + { Some(ch) => i.fill_blocks(&mut buf, o.obs, ch)?, _ => i.fill_consecutive(&mut buf)?, }; - if rlen == 0 + // Return early if no data + if rstats.reads_complete == 0 && rstats.reads_partial == 0 { - return Ok((0,buf)); + return Ok((rstats,buf)); } - // Conv etc... + // Perform any conv=x[,x...] options if i.cflags.swab { perform_swab(&mut buf); } if is_conv(&i) || is_block(&i) || is_unblock(&i) { - let buf = conv_block_unblock_helper(buf, i, o)?; - Ok((rlen, buf)) + let buf = conv_block_unblock_helper(buf, i, o, &mut rstats)?; + Ok((rstats, buf)) } else { - Ok((rlen, buf)) + Ok((rstats, buf)) } } } +fn print_io_lines(update: &ProgUpdate) +{ + eprintln!("{}+{} records in", update.reads_complete, update.reads_partial); + if update.records_truncated > 0 + { + eprintln!("{} truncated records", update.records_truncated); + } + eprintln!("{}+{} records out", update.writes_complete, update.writes_partial); +} +fn make_prog_line(update: &ProgUpdate) -> String +{ + let btotal_metric = Byte::from_bytes(update.bytes_total) + .get_appropriate_unit(false) + .format(0); + let btotal_bin = Byte::from_bytes(update.bytes_total) + .get_appropriate_unit(true) + .format(0); + let safe_millis = cmp::max(1, update.duration.as_millis()); + let xfer_rate = Byte::from_bytes(1000 * (update.bytes_total / safe_millis)) + .get_appropriate_unit(false) + .format(1); + + format!("{} bytes ({}, {}) copied, {} s, {}/s", + update.bytes_total, + btotal_metric, + btotal_bin, + safe_millis * 1000, + xfer_rate + ).to_string() +} +fn reprint_prog_line(update: &ProgUpdate) +{ + eprint!("\r{}", make_prog_line(update)); +} +fn print_prog_line(update: &ProgUpdate) +{ + eprint!("{}", make_prog_line(update)); +} +fn print_xfer_stats(update: &ProgUpdate) +{ + print_io_lines(update); + print_prog_line(update); + +} + /// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals. -fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () +fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option) -> impl Fn() -> () { + // -------------------------------------------------------------- + fn posixly_correct() -> bool + { + !env::var("POSIXLY_CORRECT").is_err() + } + // -------------------------------------------------------------- move || { + const SIGUSR1_USIZE: usize = signal::SIGUSR1 as usize; - // TODO: Replace ?? with accurate info - print!("\rProgress ({}/??)", 0); + let sigval = Arc::new(AtomicUsize::new(0)); + + // TODO: SIGINFO seems to only exist for BSD (and therefore MACOS) + // I will probably want put this behind a feature-gate and may need to pass the value to handle as my own constant. + // This may involve some finagling with the library. + // see -> https://unix.stackexchange.com/questions/179481/siginfo-on-gnu-linux-arch-linux-missing + // if let Err(e) = signal_hook::flag::register_usize(signal::SIGINFO, sigval.clone(), signal::SIGINFO as usize) + // { + // debug_println!("Internal dd Warning: Unable to register SIGINFO handler \n\t{}", e); + // } + if !posixly_correct() + { + if let Err(e) = signal_hook::flag::register_usize(signal::SIGUSR1, sigval.clone(), SIGUSR1_USIZE) + { + debug_println!("Internal dd Warning: Unable to register SIGUSR1 handler \n\t{}", e); + } + } loop { - match rx.recv() + // Wait for update + let update = match (rx.recv(), xfer_stats) { - Ok(wr_total) => { - print!("\rProgress ({}/??)", wr_total); + (Ok(update), Some(StatusLevel::Progress)) => + { + reprint_prog_line(&update); + + update }, - Err(_) => { - println!(""); - break + (Ok(update), _) => + { + update }, - } + (Err(e), _) => + { + debug_println!("Internal dd Warning: Error in progress update thread\n\t{}", e); + + continue; + }, + }; + // Handle signals + match sigval.load(Ordering::Relaxed) + { + SIGUSR1_USIZE => + { + print_xfer_stats(&update); + }, + _ => {/* no signals recv'd */}, + }; } } } +/// Calculate a 'good' internal buffer size. +/// For performance of the read/write functions, the buffer should hold +/// both an itegral number of reads and an itegral number of writes. For +/// sane real-world memory use, it should not be too large. I believe +/// the least common multiple is a good representation of these interests. #[inline] fn calc_bsize(ibs: usize, obs: usize) -> usize { @@ -820,43 +1017,60 @@ fn calc_bsize(ibs: usize, obs: usize) -> usize /// Perform the copy/convert opertaions. Stdout 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> +fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), Box> { - let mut bytes_in = 0; - let mut bytes_out = 0; + let mut rstats = ReadStat { + reads_complete: 0, + reads_partial: 0, + records_truncated: 0, + }; + let mut wstats = WriteStat { + writes_complete: 0, + writes_partial: 0, + bytes_total: 0, + }; + let start = time::Instant::now(); let bsize = calc_bsize(i.ibs, o.obs); - 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 prog_tx = { + let (tx, rx) = mpsc::channel(); + thread::spawn(gen_prog_updater(rx, i.xfer_stats)); + tx }; loop { + // Read/Write match read_helper(&mut i, &mut o, bsize)? { - (0, _) => + (ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) => break, - (rlen, buf) => + (rstat_update, buf) => { - let wlen = o.write_blocks(buf)?; + let wstats_update = o.write_blocks(buf)?; - bytes_in += rlen; - bytes_out += wlen; + rstats = ReadStat { + reads_complete: rstats.reads_complete + rstat_update.reads_complete, + reads_partial: rstats.reads_partial + rstat_update.reads_partial, + records_truncated: rstats.records_truncated + rstat_update.records_truncated, + }; + wstats = WriteStat { + writes_complete: wstats.writes_complete + wstats_update.writes_complete, + writes_partial: wstats.writes_partial + wstats_update.writes_partial, + bytes_total: wstats.bytes_total + wstats_update.bytes_total, + }; }, }; - - // Prog - if let Some(prog_tx) = &prog_tx - { - prog_tx.send(bytes_out)?; - } + // Update Prog + prog_tx.send(ProgUpdate { + reads_complete: rstats.reads_complete, + reads_partial: rstats.reads_partial, + writes_complete: wstats.writes_complete, + writes_partial: wstats.writes_partial, + bytes_total: wstats.bytes_total, + records_truncated: rstats.records_truncated, + duration: start.elapsed(), + })?; } if o.cflags.fsync @@ -868,49 +1082,81 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(usi o.fdatasync()?; } - Ok((bytes_in, bytes_out)) + match i.xfer_stats + { + Some(StatusLevel::Noxfer) | + Some(StatusLevel::None) => {}, + _ => + print_xfer_stats(&ProgUpdate { + reads_complete: rstats.reads_complete, + reads_partial: rstats.reads_partial, + writes_complete: wstats.writes_complete, + writes_partial: wstats.writes_partial, + bytes_total: wstats.bytes_total, + records_truncated: rstats.records_truncated, + duration: start.elapsed(), + }), + } + Ok(()) } /// 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> +fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box> { - let mut bytes_in = 0; - let mut bytes_out = 0; + let mut rstats = ReadStat { + reads_complete: 0, + reads_partial: 0, + records_truncated: 0, + }; + let mut wstats = WriteStat { + writes_complete: 0, + writes_partial: 0, + bytes_total: 0, + }; + let start = time::Instant::now(); let bsize = calc_bsize(i.ibs, o.obs); - 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 prog_tx = { + let (tx, rx) = mpsc::channel(); + thread::spawn(gen_prog_updater(rx, i.xfer_stats)); + tx }; loop { + // Read/Write match read_helper(&mut i, &mut o, bsize)? { - (0, _) => + (ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) => break, - (rlen, buf) => + (rstat_update, buf) => { - let wlen = o.write_blocks(buf)?; + let wstats_update = o.write_blocks(buf)?; - bytes_in += rlen; - bytes_out += wlen; + rstats = ReadStat { + reads_complete: rstats.reads_complete + rstat_update.reads_complete, + reads_partial: rstats.reads_partial + rstat_update.reads_partial, + records_truncated: rstats.records_truncated + rstat_update.records_truncated, + }; + wstats = WriteStat { + writes_complete: wstats.writes_complete + wstats_update.writes_complete, + writes_partial: wstats.writes_partial + wstats_update.writes_partial, + bytes_total: wstats.bytes_total + wstats_update.bytes_total, + }; }, }; - - // Prog - if let Some(prog_tx) = &prog_tx - { - prog_tx.send(bytes_out)?; - } + // Update Prog + prog_tx.send(ProgUpdate { + reads_complete: rstats.reads_complete, + reads_partial: rstats.reads_partial, + writes_complete: wstats.writes_complete, + writes_partial: wstats.writes_partial, + bytes_total: wstats.bytes_total, + records_truncated: rstats.records_truncated, + duration: start.elapsed(), + })?; } if o.cflags.fsync @@ -922,7 +1168,22 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(usize, u o.fdatasync()?; } - Ok((bytes_in, bytes_out)) + match i.xfer_stats + { + Some(StatusLevel::Noxfer) | + Some(StatusLevel::None) => {}, + _ => + print_xfer_stats(&ProgUpdate { + reads_complete: rstats.reads_complete, + reads_partial: rstats.reads_partial, + writes_complete: wstats.writes_complete, + writes_partial: wstats.writes_partial, + bytes_total: wstats.bytes_total, + records_truncated: rstats.records_truncated, + duration: start.elapsed(), + }), + } + Ok(()) } #[macro_export] @@ -1010,9 +1271,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 let dashed_args = args.collect_str() .iter() .fold(Vec::new(), append_dashes_if_not_present); - let matches = build_app!().parse(dashed_args); - let result = match (matches.opt_present("if"), matches.opt_present("of")) { (true, true) => @@ -1052,18 +1311,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 dd_stdout(i,o) }, }; - match result { - Ok((b_in, b_out)) => + Ok(_) => { - // TODO: Print final xfer stats - // print_stats(b_in, b_out); - - RTN_SUCCESS + RTN_SUCCESS + }, + Err(e) => + { + debug_println!("dd exiting with error:\n\t{}", e); + RTN_FAILURE }, - Err(_) => - RTN_FAILURE, } } diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index 241088e63..b0a1ba6fc 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -1,7 +1,18 @@ use super::*; -static NL: u8 = '\n' as u8; -static SPACE: u8 = ' ' as u8; +const NL: u8 = '\n' as u8; +const SPACE: u8 = ' ' as u8; + +macro_rules! rs ( + () => + { + ReadStat { + reads_complete: 0, + reads_partial: 0, + records_truncated: 0, + } + }; + ); macro_rules! make_block_test ( ( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) => @@ -12,7 +23,7 @@ macro_rules! make_block_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: IConvFlags { ctable: None, block: $block, @@ -44,7 +55,7 @@ macro_rules! make_unblock_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: IConvFlags { ctable: None, block: None, @@ -70,8 +81,9 @@ macro_rules! make_unblock_test ( #[test] fn block_test_no_nl() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], @@ -81,8 +93,9 @@ fn block_test_no_nl() #[test] fn block_test_no_nl_short_record() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8]; - let res = block(buf, 8); + let res = block(buf, 8, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], @@ -92,19 +105,22 @@ fn block_test_no_nl_short_record() #[test] fn block_test_no_nl_trunc() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8/*, 4u8*/], ]); + assert_eq!(rs.records_truncated, 1); } #[test] fn block_test_nl_gt_cbs_trunc() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], @@ -113,13 +129,15 @@ fn block_test_nl_gt_cbs_trunc() // vec![4u8, SPACE, SPACE, SPACE], vec![5u8, 6u8, 7u8, 8u8], ]); + assert_eq!(rs.records_truncated, 2); } #[test] fn block_test_surrounded_nl() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; - let res = block(buf, 8); + let res = block(buf, 8, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], @@ -130,8 +148,9 @@ fn block_test_surrounded_nl() #[test] fn block_test_multiple_nl_same_cbs_block() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8]; - let res = block(buf, 8); + let res = block(buf, 8, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], @@ -143,8 +162,9 @@ fn block_test_multiple_nl_same_cbs_block() #[test] fn block_test_multiple_nl_diff_cbs_block() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8]; - let res = block(buf, 8); + let res = block(buf, 8, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], @@ -156,8 +176,9 @@ fn block_test_multiple_nl_diff_cbs_block() #[test] fn block_test_end_nl_diff_cbs_block() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], @@ -167,8 +188,9 @@ fn block_test_end_nl_diff_cbs_block() #[test] fn block_test_end_nl_same_cbs_block() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, NL]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, SPACE] @@ -178,8 +200,9 @@ fn block_test_end_nl_same_cbs_block() #[test] fn block_test_double_end_nl() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, NL, NL]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, SPACE], @@ -190,8 +213,9 @@ fn block_test_double_end_nl() #[test] fn block_test_start_nl() { + let mut rs = rs!(); let buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![SPACE, SPACE, SPACE, SPACE], @@ -202,8 +226,9 @@ fn block_test_start_nl() #[test] fn block_test_double_surrounded_nl_no_trunc() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; - let res = block(buf, 8); + let res = block(buf, 8, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], @@ -215,14 +240,16 @@ fn block_test_double_surrounded_nl_no_trunc() #[test] fn block_test_double_surrounded_nl_double_trunc() { + let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; - let res = block(buf, 4); + let res = block(buf, 4, &mut rs); assert_eq!(res, vec![ vec![0u8, 1u8, 2u8, 3u8], vec![SPACE, SPACE, SPACE, SPACE], vec![4u8, 5u8, 6u8, 7u8/*, 8u8*/], ]); + assert_eq!(rs.records_truncated, 1); } make_block_test!( diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index ffb095c4a..070fdb217 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -9,8 +9,8 @@ impl Read for LazyReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { - let half = buf.len() / 2; - self.src.read(&mut buf[..half]) + let reduced = cmp::max(buf.len() / 2, 1); + self.src.read(&mut buf[..reduced]) } } @@ -23,7 +23,7 @@ macro_rules! make_sync_test ( src: $src, non_ascii: false, ibs: $ibs, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: IConvFlags { ctable: None, block: None, diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 521dd11f4..b7d7ccb9b 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -9,7 +9,7 @@ macro_rules! make_conv_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: icf!($ctable), iflags: DEFAULT_IFLAGS, }, @@ -34,7 +34,7 @@ macro_rules! make_icf_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: $icf, iflags: DEFAULT_IFLAGS, }, @@ -137,7 +137,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), non_ascii: false, ibs: 128, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: icf!(Some(&ASCII_TO_EBCDIC)), iflags: DEFAULT_IFLAGS, }; @@ -159,7 +159,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() src: File::open(&tmp_fname_ae).unwrap(), non_ascii: false, ibs: 256, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: icf!(Some(&EBCDIC_TO_ASCII)), iflags: DEFAULT_IFLAGS, }; diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index 7e654ec14..8904a2372 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -94,7 +94,7 @@ macro_rules! make_spec_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, }, diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index 6459ad41e..8724474a0 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -31,7 +31,7 @@ make_spec_test!( src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), non_ascii: false, ibs: 521, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, }, @@ -52,7 +52,7 @@ make_spec_test!( src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), non_ascii: false, ibs: 1031, - xfer_stats: StatusLevel::None, + xfer_stats: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, }, diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index f115a6a2c..96e58ffa2 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -297,7 +297,7 @@ fn parse_cbs(matches: &getopts::Matches) -> Result, ParseError> } } -pub fn parse_status_level(matches: &getopts::Matches) -> Result +pub fn parse_status_level(matches: &getopts::Matches) -> Result, ParseError> { unimplemented!() } From 81419190644a9b73c78a09baee4c02681be24fb9 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 15 Jun 2021 11:06:36 -0700 Subject: [PATCH 28/66] Implements count=N - Adds tests for count=READS and count=BYTES. - Implements count logic for read count and bytes count limits. --- src/uu/dd/src/dd.rs | 243 +++++++++++------- .../src/dd_unit_tests/block_unblock_tests.rs | 2 + .../dd/src/dd_unit_tests/conv_sync_tests.rs | 1 + .../dd/src/dd_unit_tests/conversion_tests.rs | 4 + src/uu/dd/src/dd_unit_tests/mod.rs | 3 + src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 162 +++++++++++- src/uu/dd/src/parseargs.rs | 22 ++ .../gnudd-deadbeef-first-12345.spec | 1 + .../gnudd-deadbeef-first-16k.spec | 1 + .../gnudd-random-first-32k.spec | Bin 0 -> 32768 bytes 10 files changed, 341 insertions(+), 98 deletions(-) create mode 100644 src/uu/dd/test-resources/gnudd-deadbeef-first-12345.spec create mode 100644 src/uu/dd/test-resources/gnudd-deadbeef-first-16k.spec create mode 100644 src/uu/dd/test-resources/gnudd-random-first-32k.spec diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index def82aafb..8d1c7121a 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -66,6 +66,17 @@ struct ReadStat reads_partial: u64, records_truncated: u32, } +impl std::ops::AddAssign for ReadStat +{ + fn add_assign(&mut self, other: Self) + { + *self = Self { + reads_complete: self.reads_complete + other.reads_complete, + reads_partial: self.reads_partial + other.reads_partial, + records_truncated: self.records_truncated + other.records_truncated, + } + } +} struct WriteStat { @@ -73,6 +84,17 @@ struct WriteStat writes_partial: u64, bytes_total: u128, } +impl std::ops::AddAssign for WriteStat +{ + fn add_assign(&mut self, other: Self) + { + *self = Self { + writes_complete: self.writes_complete + other.writes_complete, + writes_partial: self.writes_partial + other.writes_partial, + bytes_total: self.bytes_total + other.bytes_total, + } + } +} type Cbs = usize; @@ -150,6 +172,16 @@ pub enum StatusLevel None, } +/// The value of count=N +/// Defaults to Reads(N) +/// if iflag=count_bytes +/// then becomes Bytes(N) +pub enum CountType +{ + Reads(usize), + Bytes(usize), +} + #[derive(Debug)] enum InternalError { @@ -180,6 +212,7 @@ struct Input non_ascii: bool, ibs: usize, xfer_stats: Option, + count: Option, cflags: IConvFlags, iflags: IFlags, } @@ -194,12 +227,14 @@ impl Input let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; + let count = parseargs::parse_count(&iflags, matches)?; let mut i = Input { src: io::stdin(), non_ascii, ibs, xfer_stats, + count, cflags, iflags, }; @@ -225,6 +260,7 @@ impl Input let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; + let count = parseargs::parse_count(&iflags, matches)?; if let Some(fname) = matches.opt_str("if") { @@ -241,6 +277,7 @@ impl Input non_ascii, ibs, xfer_stats, + count, cflags, iflags, }; @@ -279,7 +316,7 @@ impl Read for Input impl Input { - /// Fills a given obs-sized buffer. + /// Fills a given buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read follows the previous one. fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> @@ -317,7 +354,7 @@ impl Input }) } - /// Fills a given obs-sized buffer. + /// Fills a given buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read is aligned to multiples of ibs; remaing space is filled with the 'pad' byte. fn fill_blocks(&mut self, buf: &mut Vec, obs: usize, pad: u8) -> Result> @@ -375,23 +412,19 @@ impl Input } /// Force-fills a buffer, ignoring zero-length reads which would otherwise be - /// interpreted as EOF. Does not continue after errors. - /// Note: This may never return. - fn force_fill(&mut self, mut buf: &mut [u8], target_len: usize) -> Result<(), Box> + /// interpreted as EOF. + /// Note: This will not return unless the source (eventually) produces + /// enough bytes to meet target_len. + fn force_fill(&mut self, mut buf: &mut [u8], target_len: usize) -> Result> { - let mut total_len = 0; - - loop + let mut base_idx = 0; + while base_idx < target_len { - total_len += self.read(&mut buf)?; - - if total_len == target_len - { - return Ok(()); - } + base_idx += self.read(&mut buf[base_idx..target_len])?; } - } + Ok(base_idx) + } } struct Output @@ -602,7 +635,7 @@ impl Output /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N -fn block(buf: Vec, cbs: usize, rstats: &mut ReadStat) -> Vec> +fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> { let mut blocks = buf.split(| &e | e == '\n' as u8) .fold(Vec::new(), | mut blocks, split | @@ -610,7 +643,7 @@ fn block(buf: Vec, cbs: usize, rstats: &mut ReadStat) -> Vec> let mut split = split.to_vec(); if split.len() > cbs { - rstats.records_truncated += 1; + rstat.records_truncated += 1; } split.resize(cbs, ' ' as u8); blocks.push(split); @@ -683,7 +716,7 @@ fn unblock(buf: Vec, cbs: usize) -> Vec .collect() } -fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output, rstats: &mut ReadStat) -> Result, Box> +fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output, rstat: &mut ReadStat) -> Result, Box> { // Local Predicate Fns ------------------------------------------------- #[inline] @@ -737,7 +770,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< { // ascii input so perform the block first let cbs = i.cflags.block.unwrap(); - let mut blocks = block(buf, cbs, rstats); + let mut blocks = block(buf, cbs, rstat); if let Some(ct) = i.cflags.ctable { @@ -762,7 +795,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< apply_ct(&mut buf, &ct); } - let blocks = block(buf, cbs, rstats) + let blocks = block(buf, cbs, rstat) .into_iter() .flatten() .collect(); @@ -860,7 +893,7 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us { // Read let mut buf = vec![BUF_INIT_BYTE; bsize]; - let mut rstats = match i.cflags.sync + let mut rstat = match i.cflags.sync { Some(ch) => i.fill_blocks(&mut buf, o.obs, ch)?, @@ -868,9 +901,9 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us i.fill_consecutive(&mut buf)?, }; // Return early if no data - if rstats.reads_complete == 0 && rstats.reads_partial == 0 + if rstat.reads_complete == 0 && rstat.reads_partial == 0 { - return Ok((rstats,buf)); + return Ok((rstat,buf)); } // Perform any conv=x[,x...] options @@ -880,12 +913,12 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us } if is_conv(&i) || is_block(&i) || is_unblock(&i) { - let buf = conv_block_unblock_helper(buf, i, o, &mut rstats)?; - Ok((rstats, buf)) + let buf = conv_block_unblock_helper(buf, i, o, &mut rstat)?; + Ok((rstat, buf)) } else { - Ok((rstats, buf)) + Ok((rstat, buf)) } } } @@ -935,13 +968,13 @@ fn print_xfer_stats(update: &ProgUpdate) } -/// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals. +/// Generate a progress updater that tracks progress, receives updates, and responds to signals. fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option) -> impl Fn() -> () { // -------------------------------------------------------------- fn posixly_correct() -> bool { - !env::var("POSIXLY_CORRECT").is_err() + env::var("POSIXLY_CORRECT").is_ok() } // -------------------------------------------------------------- move || { @@ -951,7 +984,7 @@ fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option https://unix.stackexchange.com/questions/179481/siginfo-on-gnu-linux-arch-linux-missing // if let Err(e) = signal_hook::flag::register_usize(signal::SIGINFO, sigval.clone(), signal::SIGINFO as usize) // { @@ -980,12 +1013,9 @@ fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option - { - debug_println!("Internal dd Warning: Error in progress update thread\n\t{}", e); - - continue; - }, + (Err(_), _) => + // recv only fails permenantly + break, }; // Handle signals match sigval.load(Ordering::Relaxed) @@ -1014,17 +1044,64 @@ fn calc_bsize(ibs: usize, obs: usize) -> usize lcm } +/// Calculate the buffer size appropriate for this loop iteration, respecting +/// a count=N if present. +fn calc_loop_bsize(count: &Option, rstat: &ReadStat, wstat: &WriteStat, ibs: usize, ideal_bsize: usize) -> usize +{ + match count + { + Some(CountType::Reads(rmax)) => + { + let rmax: u64 = (*rmax).try_into().unwrap(); + let rsofar = rstat.reads_complete + rstat.reads_partial; + let rremain: usize = (rmax - rsofar).try_into().unwrap(); + cmp::min(ideal_bsize, rremain*ibs) + }, + Some(CountType::Bytes(bmax)) => + { + let bmax: u128 = (*bmax).try_into().unwrap(); + let bremain: usize = (bmax - wstat.bytes_total).try_into().unwrap(); + cmp::min(ideal_bsize, bremain) + }, + None => + ideal_bsize, + } +} + +/// Decide if the current progress is below a count=N limit or return +/// true if no such limit is set. +fn below_count_limit(count: &Option, rstat: &ReadStat, wstat: &WriteStat) -> bool +{ + match count + { + Some(CountType::Reads(n)) => + { + let n = (*n).try_into().unwrap(); + // debug_assert!(rstat.reads_complete + rstat.reads_partial >= n); + rstat.reads_complete + rstat.reads_partial <= n + }, + Some(CountType::Bytes(n)) => + { + let n = (*n).try_into().unwrap(); + // debug_assert!(wstat.bytes_total >= n); + wstat.bytes_total <= n + }, + None => + true, + } +} + /// Perform the copy/convert opertaions. Stdout 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<(), Box> { - let mut rstats = ReadStat { + let mut rstat = ReadStat { reads_complete: 0, reads_partial: 0, records_truncated: 0, }; - let mut wstats = WriteStat { + let mut wstat = WriteStat { writes_complete: 0, writes_partial: 0, bytes_total: 0, @@ -1038,37 +1115,30 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), tx }; - loop + while below_count_limit(&i.count, &rstat, &wstat) { // Read/Write - match read_helper(&mut i, &mut o, bsize)? + let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); + match read_helper(&mut i, &mut o, loop_bsize)? { (ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) => break, (rstat_update, buf) => { - let wstats_update = o.write_blocks(buf)?; + let wstat_update = o.write_blocks(buf)?; - rstats = ReadStat { - reads_complete: rstats.reads_complete + rstat_update.reads_complete, - reads_partial: rstats.reads_partial + rstat_update.reads_partial, - records_truncated: rstats.records_truncated + rstat_update.records_truncated, - }; - wstats = WriteStat { - writes_complete: wstats.writes_complete + wstats_update.writes_complete, - writes_partial: wstats.writes_partial + wstats_update.writes_partial, - bytes_total: wstats.bytes_total + wstats_update.bytes_total, - }; - }, + rstat += rstat_update; + wstat += wstat_update; + }, }; // Update Prog prog_tx.send(ProgUpdate { - reads_complete: rstats.reads_complete, - reads_partial: rstats.reads_partial, - writes_complete: wstats.writes_complete, - writes_partial: wstats.writes_partial, - bytes_total: wstats.bytes_total, - records_truncated: rstats.records_truncated, + reads_complete: rstat.reads_complete, + reads_partial: rstat.reads_partial, + writes_complete: wstat.writes_complete, + writes_partial: wstat.writes_partial, + bytes_total: wstat.bytes_total, + records_truncated: rstat.records_truncated, duration: start.elapsed(), })?; } @@ -1088,12 +1158,12 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), Some(StatusLevel::None) => {}, _ => print_xfer_stats(&ProgUpdate { - reads_complete: rstats.reads_complete, - reads_partial: rstats.reads_partial, - writes_complete: wstats.writes_complete, - writes_partial: wstats.writes_partial, - bytes_total: wstats.bytes_total, - records_truncated: rstats.records_truncated, + reads_complete: rstat.reads_complete, + reads_partial: rstat.reads_partial, + writes_complete: wstat.writes_complete, + writes_partial: wstat.writes_partial, + bytes_total: wstat.bytes_total, + records_truncated: rstat.records_truncated, duration: start.elapsed(), }), } @@ -1105,12 +1175,12 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), // and should be fixed in the future. fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box> { - let mut rstats = ReadStat { + let mut rstat = ReadStat { reads_complete: 0, reads_partial: 0, records_truncated: 0, }; - let mut wstats = WriteStat { + let mut wstat = WriteStat { writes_complete: 0, writes_partial: 0, bytes_total: 0, @@ -1124,37 +1194,30 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box break, (rstat_update, buf) => { - let wstats_update = o.write_blocks(buf)?; + let wstat_update = o.write_blocks(buf)?; - rstats = ReadStat { - reads_complete: rstats.reads_complete + rstat_update.reads_complete, - reads_partial: rstats.reads_partial + rstat_update.reads_partial, - records_truncated: rstats.records_truncated + rstat_update.records_truncated, - }; - wstats = WriteStat { - writes_complete: wstats.writes_complete + wstats_update.writes_complete, - writes_partial: wstats.writes_partial + wstats_update.writes_partial, - bytes_total: wstats.bytes_total + wstats_update.bytes_total, - }; + rstat += rstat_update; + wstat += wstat_update; }, }; // Update Prog prog_tx.send(ProgUpdate { - reads_complete: rstats.reads_complete, - reads_partial: rstats.reads_partial, - writes_complete: wstats.writes_complete, - writes_partial: wstats.writes_partial, - bytes_total: wstats.bytes_total, - records_truncated: rstats.records_truncated, + reads_complete: rstat.reads_complete, + reads_partial: rstat.reads_partial, + writes_complete: wstat.writes_complete, + writes_partial: wstat.writes_partial, + bytes_total: wstat.bytes_total, + records_truncated: rstat.records_truncated, duration: start.elapsed(), })?; } @@ -1174,12 +1237,12 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box {}, _ => print_xfer_stats(&ProgUpdate { - reads_complete: rstats.reads_complete, - reads_partial: rstats.reads_partial, - writes_complete: wstats.writes_complete, - writes_partial: wstats.writes_partial, - bytes_total: wstats.bytes_total, - records_truncated: rstats.records_truncated, + reads_complete: rstat.reads_complete, + reads_partial: rstat.reads_partial, + writes_complete: wstat.writes_complete, + writes_partial: wstat.writes_partial, + bytes_total: wstat.bytes_total, + records_truncated: rstat.records_truncated, duration: start.elapsed(), }), } diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index b0a1ba6fc..eba49f9d0 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -24,6 +24,7 @@ macro_rules! make_block_test ( non_ascii: false, ibs: 512, xfer_stats: None, + count: None, cflags: IConvFlags { ctable: None, block: $block, @@ -56,6 +57,7 @@ macro_rules! make_unblock_test ( non_ascii: false, ibs: 512, xfer_stats: None, + count: None, cflags: IConvFlags { ctable: None, block: None, diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 070fdb217..959cb53fd 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -24,6 +24,7 @@ macro_rules! make_sync_test ( non_ascii: false, ibs: $ibs, xfer_stats: None, + count: None, cflags: IConvFlags { ctable: None, block: None, diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index b7d7ccb9b..f7ee7f92c 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -10,6 +10,7 @@ macro_rules! make_conv_test ( non_ascii: false, ibs: 512, xfer_stats: None, + count: None, cflags: icf!($ctable), iflags: DEFAULT_IFLAGS, }, @@ -35,6 +36,7 @@ macro_rules! make_icf_test ( non_ascii: false, ibs: 512, xfer_stats: None, + count: None, cflags: $icf, iflags: DEFAULT_IFLAGS, }, @@ -138,6 +140,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() non_ascii: false, ibs: 128, xfer_stats: None, + count: None, cflags: icf!(Some(&ASCII_TO_EBCDIC)), iflags: DEFAULT_IFLAGS, }; @@ -160,6 +163,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() non_ascii: false, ibs: 256, xfer_stats: None, + count: None, cflags: icf!(Some(&EBCDIC_TO_ASCII)), iflags: DEFAULT_IFLAGS, }; diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index 8904a2372..eb8af60df 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -95,6 +95,7 @@ macro_rules! make_spec_test ( non_ascii: false, ibs: 512, xfer_stats: None, + count: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, }, @@ -116,11 +117,13 @@ macro_rules! make_spec_test ( dd_fileout($i,$o).unwrap(); let res = File::open($tmp_fname).unwrap(); + // Check test file isn't empty (unless spec file is too) assert_eq!(res.metadata().unwrap().len(), $spec.metadata().unwrap().len()); let spec = BufReader::new($spec); let res = BufReader::new(res); + // Check all bytes match for (b_res, b_spec) in res.bytes().zip(spec.bytes()) { assert_eq!(b_res.unwrap(), diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index 8724474a0..e9923b4b1 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -1,5 +1,25 @@ use super::*; +const DST_PLACEHOLDER: Vec = Vec::new(); + +macro_rules! make_io_test ( + ( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr ) => + { + make_spec_test!($test_id, + $test_name, + $i, + Output { + dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), + obs: $o.obs, + cflags: $o.cflags, + oflags: $o.oflags, + }, + $spec, + format!("./test-resources/FAILED-{}.test", $test_name) + ); + }; +); + make_spec_test!( zeros_4k_test, "zeros-4k", @@ -24,7 +44,7 @@ make_spec_test!( File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); -make_spec_test!( +make_io_test!( random_73k_test_not_a_multiple_obs_gt_ibs, "random-73k-not-a-multiple-obs-gt-ibs", Input { @@ -32,20 +52,20 @@ make_spec_test!( non_ascii: false, ibs: 521, xfer_stats: None, + count: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, }, Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", "random-73k-not-a-multiple-obs-gt-ibs")).unwrap(), + dst: DST_PLACEHOLDER, obs: 1031, cflags: DEFAULT_CFO, oflags: DEFAULT_OFLAGS, }, - File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), - format!("./test-resources/FAILED-{}.test", "random-73k-not-a-multiple-obs-gt-ibs") + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); -make_spec_test!( +make_io_test!( random_73k_test_obs_lt_not_a_multiple_ibs, "random-73k-obs-lt-not-a-multiple-ibs", Input { @@ -53,17 +73,143 @@ make_spec_test!( non_ascii: false, ibs: 1031, xfer_stats: None, + count: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, }, Output { - dst: File::create(format!("./test-resources/FAILED-{}.test", "random-73k-obs-lt-not-a-multiple-ibs")).unwrap(), + dst: DST_PLACEHOLDER, obs: 521, cflags: DEFAULT_CFO, oflags: DEFAULT_OFLAGS, }, - File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), - format!("./test-resources/FAILED-{}.test", "random-73k-obs-lt-not-a-multiple-ibs") + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() +); + +make_io_test!( + deadbeef_all_32k_test_count_reads, + "deadbeef_all_32k_test_count_reads", + Input { + src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), + non_ascii: false, + ibs: 1024, + xfer_stats: None, + count: Some(CountType::Reads(32)), + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1024, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() +); + +make_io_test!( + deadbeef_all_32k_test_count_bytes, + "deadbeef_all_32k_test_count_bytes", + Input { + src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), + non_ascii: false, + ibs: 531, + xfer_stats: None, + count: Some(CountType::Bytes(32*1024)), + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() +); + +make_io_test!( + deadbeef_32k_to_16k_test_count_reads, + "deadbeef_32k_test_count_reads", + Input { + src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), + non_ascii: false, + ibs: 1024, + xfer_stats: None, + count: Some(CountType::Reads(16)), + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/gnudd-deadbeef-first-16k.spec").unwrap() +); + +make_io_test!( + deadbeef_32k_to_12345_test_count_bytes, + "deadbeef_32k_to_12345_test_count_bytes", + Input { + src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), + non_ascii: false, + ibs: 531, + xfer_stats: None, + count: Some(CountType::Bytes(12345)), + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/gnudd-deadbeef-first-12345.spec").unwrap() +); + +make_io_test!( + random_73k_test_count_reads, + "random-73k-test-count-reads", + Input { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + non_ascii: false, + ibs: 1024, + xfer_stats: None, + count: Some(CountType::Reads(32)), + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1024, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() +); + +make_io_test!( + random_73k_test_count_bytes, + "random-73k-test-count-bytes", + Input { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), + non_ascii: false, + ibs: 521, + xfer_stats: None, + count: Some(CountType::Bytes(32*1024)), + cflags: icf!(), + iflags: DEFAULT_IFLAGS, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); // Test internal buffer size fn diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 96e58ffa2..620f18250 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -3,6 +3,7 @@ mod unit_tests; use crate::conversion_tables::*; use crate::{ + CountType, IConvFlags, OConvFlags, StatusLevel, }; @@ -759,6 +760,27 @@ pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &getopts::Matches) } } +/// Parse the value of count=N and the type of N implied by iflags +pub fn parse_count(iflags: &IFlags, matches: &getopts::Matches) -> Result, ParseError> +{ + if let Some(amt) = matches.opt_str("count") + { + let n = parse_bytes_with_opt_multiplier(amt)?; + if iflags.count_bytes + { + Ok(Some(CountType::Bytes(n))) + } + else + { + Ok(Some(CountType::Reads(n))) + } + } + else + { + Ok(None) + } +} + /// Parse whether the args indicate the input is not ascii pub fn parse_input_non_ascii(matches: &getopts::Matches) -> Result { diff --git a/src/uu/dd/test-resources/gnudd-deadbeef-first-12345.spec b/src/uu/dd/test-resources/gnudd-deadbeef-first-12345.spec new file mode 100644 index 000000000..c36c9ec7a --- /dev/null +++ b/src/uu/dd/test-resources/gnudd-deadbeef-first-12345.spec @@ -0,0 +1 @@ +­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­ \ No newline at end of file diff --git a/src/uu/dd/test-resources/gnudd-deadbeef-first-16k.spec b/src/uu/dd/test-resources/gnudd-deadbeef-first-16k.spec new file mode 100644 index 000000000..dbd247457 --- /dev/null +++ b/src/uu/dd/test-resources/gnudd-deadbeef-first-16k.spec @@ -0,0 +1 @@ +­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾­Þï¾ \ No newline at end of file diff --git a/src/uu/dd/test-resources/gnudd-random-first-32k.spec b/src/uu/dd/test-resources/gnudd-random-first-32k.spec new file mode 100644 index 0000000000000000000000000000000000000000..8f42c0a646b6dcc2d30935151dcd46b9125eb59c GIT binary patch literal 32768 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 q{Z%FLQb2U7n&) Date: Tue, 15 Jun 2021 12:19:18 -0700 Subject: [PATCH 29/66] Implements status=LEVEL parser. --- src/uu/dd/src/dd.rs | 39 +++++++++- src/uu/dd/src/parseargs.rs | 100 ++++++++++++++++++-------- src/uu/dd/src/parseargs/unit_tests.rs | 63 ++++++++++++++++ 3 files changed, 171 insertions(+), 31 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 8d1c7121a..db1c077a1 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -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" + ) } ); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 620f18250..b4ee0efb2 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -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 + { + 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 { match s @@ -251,7 +272,7 @@ fn parse_bytes_with_opt_multiplier(s: String) -> Result { 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, ParseError> pub fn parse_status_level(matches: &getopts::Matches) -> Result, 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 @@ -321,45 +351,55 @@ pub fn parse_obs(matches: &getopts::Matches) -> Result fn parse_ctable(fmt: Option, case: Option) -> 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, } diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 33e082a1b..19ff317de 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -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] From 19996c10a9b6c733500661f463f036fb96cd6ef7 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 16 Jun 2021 12:59:43 -0700 Subject: [PATCH 30/66] Removes 'fast read' plumbing. - The dd info page mentions a special fast-read framework if no conv=FLAG is specified (see bs=N) which I left space for. As it turns out, this is performed already so it does not need to be implemented. --- src/uu/dd/src/dd.rs | 77 +++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 49 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index db1c077a1..564e8804d 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -843,17 +843,6 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us { // Local Predicate Fns ----------------------------------------------- #[inline] - fn is_fast_read(i: &Input, o: &Output) -> bool - { - // TODO: Enable this once fast_reads are implemented - false && - i.ibs == o.obs - && !is_conv(i) - && !is_block(i) - && !is_unblock(i) - && !i.cflags.swab - } - #[inline] fn is_conv(i: &Input) -> bool { i.cflags.ctable.is_some() @@ -882,45 +871,35 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us } } // ------------------------------------------------------------------ - if is_fast_read(&i, &o) - { - // TODO: fast reads are copies performed - // directly to output (without creating any buffers) - // as mentioned in the dd spec. - unimplemented!() - } - else - { - // Read - let mut buf = vec![BUF_INIT_BYTE; bsize]; - let mut rstat = match i.cflags.sync - { - Some(ch) => - i.fill_blocks(&mut buf, o.obs, ch)?, - _ => - i.fill_consecutive(&mut buf)?, - }; - // Return early if no data - if rstat.reads_complete == 0 && rstat.reads_partial == 0 - { - return Ok((rstat,buf)); - } + // Read + let mut buf = vec![BUF_INIT_BYTE; bsize]; + let mut rstat = match i.cflags.sync + { + Some(ch) => + i.fill_blocks(&mut buf, o.obs, ch)?, + _ => + i.fill_consecutive(&mut buf)?, + }; + // Return early if no data + if rstat.reads_complete == 0 && rstat.reads_partial == 0 + { + return Ok((rstat,buf)); + } - // Perform any conv=x[,x...] options - if i.cflags.swab - { - perform_swab(&mut buf); - } - if is_conv(&i) || is_block(&i) || is_unblock(&i) - { - let buf = conv_block_unblock_helper(buf, i, o, &mut rstat)?; - Ok((rstat, buf)) - } - else - { - Ok((rstat, buf)) - } - } + // Perform any conv=x[,x...] options + if i.cflags.swab + { + perform_swab(&mut buf); + } + if is_conv(&i) || is_block(&i) || is_unblock(&i) + { + let buf = conv_block_unblock_helper(buf, i, o, &mut rstat)?; + Ok((rstat, buf)) + } + else + { + Ok((rstat, buf)) + } } fn print_io_lines(update: &ProgUpdate) From d14b5843a3f42afa2d7f9e4a5e65ab51545231d0 Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 17 Jun 2021 14:33:30 -0700 Subject: [PATCH 31/66] Implements libc file open flags (unix only) --- Cargo.lock | 1 + src/uu/dd/Cargo.toml | 1 + src/uu/dd/src/dd.rs | 136 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 132 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 779d4b99e..0e9bd8847 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1760,6 +1760,7 @@ dependencies = [ "gcd", "getopts", "hex-literal", + "libc", "md-5", "signal-hook", "uucore", diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index ce53439b2..e10fd7f07 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -20,6 +20,7 @@ debug_print = "1.0" # Probably best to keep this identical to the version of getopts in the uucore crate getopts = "<= 0.2.21" gcd = "2.0" +libc = "0.2" signal-hook = "0.3.9" uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 564e8804d..5eccf032a 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -35,6 +35,10 @@ use std::io::{ self, Read, Write, Seek, }; +#[cfg(unix)] +use libc; +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; use std::sync::{ Arc, atomic::AtomicUsize, mpsc, atomic::Ordering, }; @@ -250,6 +254,53 @@ impl Input } } +fn make_unix_iflags(oflags: &IFlags) -> Option +{ + let mut flag = 0; + + if oflags.direct + { + flag |= libc::O_DIRECT; + } + if oflags.directory + { + flag |= libc::O_DIRECTORY; + } + if oflags.dsync + { + flag |= libc::O_DSYNC; + } + if oflags.noatime + { + flag |= libc::O_NOATIME; + } + if oflags.noctty + { + flag |= libc::O_NOCTTY; + } + if oflags.nofollow + { + flag |= libc::O_NOFOLLOW; + } + if oflags.nonblock + { + flag |= libc::O_NONBLOCK; + } + if oflags.sync + { + flag |= libc::O_SYNC; + } + + if flag != 0 + { + Some(flag) + } + else + { + None + } +} + impl Input { fn new(matches: &getopts::Matches) -> Result> @@ -264,7 +315,21 @@ impl Input if let Some(fname) = matches.opt_str("if") { - let mut src = File::open(fname)?; + let mut src = + { + let mut opts = OpenOptions::new(); + opts.read(true); + + if cfg!(unix) + { + if let Some(libc_flags) = make_unix_iflags(&iflags) + { + opts.custom_flags(libc_flags); + } + } + + opts.open(fname)? + }; if let Some(amt) = skip { @@ -461,6 +526,53 @@ impl Output { } } +fn make_unix_oflags(oflags: &OFlags) -> Option +{ + let mut flag = 0; + + if oflags.direct + { + flag |= libc::O_DIRECT; + } + if oflags.directory + { + flag |= libc::O_DIRECTORY; + } + if oflags.dsync + { + flag |= libc::O_DSYNC; + } + if oflags.noatime + { + flag |= libc::O_NOATIME; + } + if oflags.noctty + { + flag |= libc::O_NOCTTY; + } + if oflags.nofollow + { + flag |= libc::O_NOFOLLOW; + } + if oflags.nonblock + { + flag |= libc::O_NONBLOCK; + } + if oflags.sync + { + flag |= libc::O_SYNC; + } + + if flag != 0 + { + Some(flag) + } + else + { + None + } +} + impl Output { fn new(matches: &getopts::Matches) -> Result> { @@ -471,11 +583,23 @@ impl Output { if let Some(fname) = matches.opt_str("of") { - let mut dst = OpenOptions::new() - .write(true) - .create(!cflags.nocreat) - .truncate(!cflags.notrunc) - .open(fname)?; + let mut dst = { + let mut opts = OpenOptions::new(); + opts.write(true) + .append(oflags.append) + .create_new(cflags.excl || !cflags.nocreat) + .truncate(!cflags.notrunc); + + if cfg!(unix) + { + if let Some(libc_flags) = make_unix_oflags(&oflags) + { + opts.custom_flags(libc_flags); + } + } + + opts.open(fname)? + }; if let Some(amt) = seek { From 22265814ba5cb0c163a32526b5c5ece4ee49a73c Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 17 Jun 2021 14:49:08 -0700 Subject: [PATCH 32/66] Cleans up compiler warnings. --- src/uu/dd/src/dd.rs | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 5eccf032a..585300d68 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -19,7 +19,7 @@ mod conversion_tables; use conversion_tables::*; use byte_unit::Byte; -#[macro_use] +// #[macro_use] use debug_print::debug_println; use gcd::Gcd; use getopts; @@ -422,7 +422,7 @@ impl Input /// Fills a given buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read is aligned to multiples of ibs; remaing space is filled with the 'pad' byte. - fn fill_blocks(&mut self, buf: &mut Vec, obs: usize, pad: u8) -> Result> + fn fill_blocks(&mut self, buf: &mut Vec, pad: u8) -> Result> { let mut reads_complete = 0; let mut reads_partial = 0; @@ -480,7 +480,7 @@ impl Input /// interpreted as EOF. /// Note: This will not return unless the source (eventually) produces /// enough bytes to meet target_len. - fn force_fill(&mut self, mut buf: &mut [u8], target_len: usize) -> Result> + fn force_fill(&mut self, buf: &mut [u8], target_len: usize) -> Result> { let mut base_idx = 0; while base_idx < target_len @@ -711,7 +711,7 @@ impl Output }, wlen => { - writes_partial += 1; + writes_complete += 1; base_idx += wlen; }, } @@ -736,17 +736,21 @@ impl Output while base_idx < buf.len() { let next_blk = cmp::min(base_idx+self.obs, buf.len()); - let wlen = self.write(&buf[base_idx..next_blk])?; + let plen = next_blk - base_idx; - if wlen == self.obs + match self.write(&buf[base_idx..next_blk])? { - writes_complete += 1; + wlen if wlen < plen => + { + writes_partial += 1; + base_idx += wlen; + }, + wlen => + { + writes_complete += 1; + base_idx += wlen; + }, } - else - { - writes_partial += 1; - } - base_idx += wlen; } Ok(WriteStat { @@ -840,7 +844,7 @@ fn unblock(buf: Vec, cbs: usize) -> Vec .collect() } -fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, o: &Output, rstat: &mut ReadStat) -> Result, Box> +fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, rstat: &mut ReadStat) -> Result, Box> { // Local Predicate Fns ------------------------------------------------- #[inline] @@ -963,7 +967,7 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input< } } -fn read_helper(i: &mut Input, o: &mut Output, bsize: usize) -> Result<(ReadStat, Vec), Box> +fn read_helper(i: &mut Input, bsize: usize) -> Result<(ReadStat, Vec), Box> { // Local Predicate Fns ----------------------------------------------- #[inline] @@ -1000,7 +1004,7 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us let mut rstat = match i.cflags.sync { Some(ch) => - i.fill_blocks(&mut buf, o.obs, ch)?, + i.fill_blocks(&mut buf, ch)?, _ => i.fill_consecutive(&mut buf)?, }; @@ -1017,7 +1021,7 @@ fn read_helper(i: &mut Input, o: &mut Output, bsize: us } if is_conv(&i) || is_block(&i) || is_unblock(&i) { - let buf = conv_block_unblock_helper(buf, i, o, &mut rstat)?; + let buf = conv_block_unblock_helper(buf, i, &mut rstat)?; Ok((rstat, buf)) } else @@ -1222,7 +1226,7 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), { // Read/Write let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); - match read_helper(&mut i, &mut o, loop_bsize)? + match read_helper(&mut i, loop_bsize)? { (ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) => break, @@ -1301,7 +1305,7 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box break, From 47464f50a1cd3c3a4683c2ee4d91d01ffdf3b11b Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 21 Jun 2021 11:17:47 -0700 Subject: [PATCH 33/66] Begin project level testing. Minor fixes. --- src/uu/dd/src/dd.rs | 19 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 25 +- tests/by-util/test_dd.rs | 252 ++++++++++++++---- 3 files changed, 229 insertions(+), 67 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 585300d68..459c66e54 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -361,7 +361,6 @@ impl Read for Input { fn read(&mut self, mut buf: &mut [u8]) -> io::Result { - // Read from source, ignore read errors if conv=noerror match self.src.read(&mut buf) { Ok(len) => @@ -1052,11 +1051,11 @@ fn make_prog_line(update: &ProgUpdate) -> String .get_appropriate_unit(false) .format(1); - format!("{} bytes ({}, {}) copied, {} s, {}/s", + format!("{} bytes ({}, {}) copied, {:.1} s, {}/s", update.bytes_total, btotal_metric, btotal_bin, - safe_millis * 1000, + update.duration.as_secs_f64(), xfer_rate ).to_string() } @@ -1066,7 +1065,7 @@ fn reprint_prog_line(update: &ProgUpdate) } fn print_prog_line(update: &ProgUpdate) { - eprint!("{}", make_prog_line(update)); + eprintln!("{}", make_prog_line(update)); } fn print_xfer_stats(update: &ProgUpdate) { @@ -1387,6 +1386,18 @@ macro_rules! build_app ( if it is smaller than the block size.", "BYTES" ) + .optopt( + "", + "iflag", + "read as per the comma separated symbol list of flags", + "FLAG" + ) + .optopt( + "", + "oflag", + "write as per the comma separated symbol list of flags", + "FLAG" + ) .optopt( "", "if", diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index f7ee7f92c..a1a7ad892 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -177,20 +177,23 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() dd_fileout(i,o).unwrap(); - let res = { - let res = File::open(&tmp_fname_ea).unwrap(); - let res = BufReader::new(res); + // Final Comparison + let res = File::open(&tmp_fname_ea).unwrap(); + let spec = File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(); - let mut h = Md5::new(); - for b in res.bytes() - { - h.update([b.unwrap()]); - } + assert_eq!(res.metadata().unwrap().len(), spec.metadata().unwrap().len()); - h.finalize() - }; + let res = BufReader::new(res); + let spec = BufReader::new(spec); - assert_eq!(hex!("37eff01866ba3f538421b30b7cbefcac"), res[..]); + let res = BufReader::new(res); + + // Check all bytes match + for (b_res, b_spec) in res.bytes().zip(spec.bytes()) + { + assert_eq!(b_res.unwrap(), + b_spec.unwrap()); + } fs::remove_file(&tmp_fname_ae).unwrap(); fs::remove_file(&tmp_fname_ea).unwrap(); diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 40ea7e430..b9eb7f3ac 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,61 +1,209 @@ use crate::common::util::*; -use std::io::prelude::*; -use std::io::BufReader; -use std::fs::{self, File}; - #[test] -fn dd_zeros_to_stdout_test_from_args() +fn version() { - new_ucmd!() - .args(&[ - "if=../../src/uu/dd/test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test", - ]) - .succeeds() - .stdout_only( - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\ - " - ); + new_ucmd!() + .args(&["--version"]) + .succeeds(); } #[test] -fn dd_ones_to_file_test_from_args() +fn help() { - let tmp_fname = "../../src/uu/dd/test-resources/FAILED-ones-to-file-from-args.test"; - - new_ucmd!() - .args(&[ - "if=../../src/uu/dd/test-resources/ones-6ae59e64850377ee5470c854761551ea.test", - &format!("of={}", &tmp_fname), - ]) - .succeeds(); - - let res = File::open(&tmp_fname).unwrap(); - let res = BufReader::new(res); - - let spec = File::open("../../src/uu/dd/test-resources/ones-6ae59e64850377ee5470c854761551ea.test").unwrap(); - let spec = BufReader::new(spec); - - for (b_res, b_spec) in res.bytes().zip(spec.bytes()) - { - assert_eq!(b_res.unwrap(), - b_spec.unwrap()); - } - - fs::remove_file(&tmp_fname).unwrap(); + new_ucmd!() + .args(&["--help"]) + .succeeds(); } + +fn build_ascii_block(n: usize) -> Vec +{ + vec!['a', 'b', 'c', 'd', 'e', 'f'] + .into_iter() + .map(|c| c as u8) + .cycle() + .take(n) + .collect() +} + +#[test] +fn test_stdin_stdout() +{ + let input = build_ascii_block(521); + let output = String::from_utf8(input.clone()).unwrap(); + new_ucmd!() + .args(&["status=none"]) + .pipe_in(input) + .succeeds() + .stdout_only(output); +} + +#[test] +fn test_stdin_stdout_count() +{ + let input = build_ascii_block(521); + let mut output = String::from_utf8(input.clone()).unwrap(); + output.truncate(256); + new_ucmd!() + .args(&[ + "status=none", + "count=2", + "ibs=128", + ]) + .pipe_in(input) + .succeeds() + .stdout_only(output); +} + +#[test] +fn test_stdin_stdout_count_bytes() +{ + let input = build_ascii_block(521); + let mut output = String::from_utf8(input.clone()).unwrap(); + output.truncate(256); + new_ucmd!() + .args(&[ + "status=none", + "count=256", + "iflag=count_bytes", + ]) + .pipe_in(input) + .succeeds() + .stdout_only(output); +} + +#[test] +fn test_stdin_stdout_skip() +{ + let input = build_ascii_block(521); + let mut output = String::from_utf8(input.clone()).unwrap(); + let _ = output.drain(..256); + new_ucmd!() + .args(&[ + "status=none", + "skip=2", + "ibs=128", + ]) + .pipe_in(input) + .succeeds() + .stdout_only(output); +} + +#[test] +fn test_stdin_stdout_skip_bytes() +{ + let input = build_ascii_block(521); + let mut output = String::from_utf8(input.clone()).unwrap(); + let _ = output.drain(..256); + new_ucmd!() + .args(&[ + "status=none", + "skip=256", + "ibs=128", + "iflag=skip_bytes", + ]) + .pipe_in(input) + .succeeds() + .stdout_only(output); +} + +#[test] +fn test_final_stats_noxfer() +{ + new_ucmd!() + .args(&[ + "status=noxfer", + ]) + .succeeds() + .stderr_only(""); +} + +#[test] +fn test_final_stats_unspec() +{ + let output = vec![ + "0+0 records in", + "0+0 records out", + "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s", + ]; + let output = output.into_iter() + .fold(String::new(), | mut acc, s | { + acc.push_str(s); + acc.push('\n'); + acc + }); + new_ucmd!() + .succeeds() + .stderr_only(&output); +} + +#[test] +fn test_self_transfer() +{ + panic!(); + // TODO: Make new copy per-test + new_ucmd!() + .args(&[ + "conv=notruc", + "if=../fixtures/dd/zero-256k.copy", + "of=../fixtures/dd/zero-256k.copy", + ]) + .succeeds(); + assert!(false/* Must check that zero256k.copy still == zero-256k.txt */) +} + +#[cfg(unix)] +#[test] +fn test_null() +{ + let stats = vec![ + "0+0 records in", + "0+0 records out", + "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s", + ]; + let stats = stats.into_iter() + .fold(String::new(), | mut acc, s | { + acc.push_str(s); + acc.push('\n'); + acc + }); + new_ucmd!() + .args(&[ + "if=/dev/null", + ]) + .succeeds() + .stderr_only(stats) + .stdout_only(""); +} + +#[test] +fn test_ys_to_stdout() +{ + let output: Vec<_> = String::from("y\n") + .bytes() + .cycle() + .take(1024) + .collect(); + let output = String::from_utf8(output).unwrap(); + + new_ucmd!() + .args(&[ + "if=../fixtures/dd/y-nl-1k.txt", + ]) + .run() + .stdout_only(output); +} + +#[test] +fn test_zeros_to_stdout() +{ + let output = vec![0; 256*1024]; + let output = String::from_utf8(output).unwrap(); + new_ucmd!() + .args(&[ + "if=../fixtures/dd/zero-256k.txt", + ]) + .run() + .stdout_only(output); +} + From 17cfba41cce761288670d119ed979d2f9f7a38e2 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 30 Jun 2021 14:47:48 -0700 Subject: [PATCH 34/66] Implements project testfing from root. - conv=FLAG testing. (1) WIP conv=nocreat - iflag & oflag testing. - conv=CONV ascii,...,ucase,...,block,...sync tests at unit-test-level (project root is todo) --- Cargo.lock | 148 ++--- src/uu/dd/Cargo.toml | 6 +- src/uu/dd/src/dd.rs | 98 ++- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 14 - src/uu/dd/src/dd_unit_tests/mod.rs | 40 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 80 +++ src/uu/dd/src/parseargs.rs | 77 ++- tests/by-util/test_dd.rs | 586 ++++++++++++++++-- tests/fixtures/dd/ascii-10k.txt | Bin 0 -> 1048576 bytes .../dd/gnudd-conv-atoe-seq-byte-values.spec | Bin 0 -> 256 bytes .../dd/gnudd-conv-atoibm-seq-byte-values.spec | Bin 0 -> 256 bytes ...nudd-conv-ebcdic-ltou-seq-byte-values.spec | Bin 0 -> 256 bytes ...nudd-conv-ebcdic-utol-seq-byte-values.spec | Bin 0 -> 256 bytes .../dd/gnudd-conv-etoa-seq-byte-values.spec | Bin 0 -> 256 bytes .../gnudd-conv-ibm-ltou-seq-byte-values.spec | Bin 0 -> 256 bytes .../gnudd-conv-ibm-utol-seq-byte-values.spec | Bin 0 -> 256 bytes .../dd/gnudd-conv-ltou-seq-byte-values.spec | Bin 0 -> 256 bytes .../dd/gnudd-conv-utol-seq-byte-values.spec | Bin 0 -> 256 bytes tests/fixtures/dd/null.txt | 0 tests/fixtures/dd/self-transfer-256k.txt | Bin 0 -> 262144 bytes tests/fixtures/dd/seq-byte-values.test | Bin 0 -> 256 bytes tests/fixtures/dd/this-file-exists-excl.txt | 0 .../dd/this-file-exists-no-noatime.txt | 0 .../fixtures/dd/this-file-exists-notrunc.txt | 1 + .../dd/this-file-exists-truncated.txt | 1 + .../fixtures/dd/this-ifile-exists-noatime.txt | 0 .../fixtures/dd/this-ofile-exists-noatime.txt | 0 tests/fixtures/dd/y-nl-1k.txt | 512 +++++++++++++++ tests/fixtures/dd/zero-256k.txt | Bin 0 -> 262144 bytes 29 files changed, 1376 insertions(+), 187 deletions(-) create mode 100644 tests/fixtures/dd/ascii-10k.txt create mode 100644 tests/fixtures/dd/gnudd-conv-atoe-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-atoibm-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-ebcdic-ltou-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-ebcdic-utol-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-etoa-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-ibm-ltou-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-ibm-utol-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-ltou-seq-byte-values.spec create mode 100644 tests/fixtures/dd/gnudd-conv-utol-seq-byte-values.spec create mode 100644 tests/fixtures/dd/null.txt create mode 100644 tests/fixtures/dd/self-transfer-256k.txt create mode 100644 tests/fixtures/dd/seq-byte-values.test create mode 100644 tests/fixtures/dd/this-file-exists-excl.txt create mode 100644 tests/fixtures/dd/this-file-exists-no-noatime.txt create mode 100644 tests/fixtures/dd/this-file-exists-notrunc.txt create mode 100644 tests/fixtures/dd/this-file-exists-truncated.txt create mode 100644 tests/fixtures/dd/this-ifile-exists-noatime.txt create mode 100644 tests/fixtures/dd/this-ofile-exists-noatime.txt create mode 100644 tests/fixtures/dd/y-nl-1k.txt create mode 100644 tests/fixtures/dd/zero-256k.txt diff --git a/Cargo.lock b/Cargo.lock index 0e9bd8847..314c7559e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,16 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" dependencies = [ "byte-tools", - "generic-array 0.8.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -569,16 +560,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" dependencies = [ - "generic-array 0.8.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -655,16 +637,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getopts" version = "0.2.21" @@ -682,7 +654,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -718,12 +701,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" -[[package]] -name = "hex-literal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" - [[package]] name = "hostname" version = "0.3.1" @@ -832,17 +809,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "md5" version = "0.3.8" @@ -973,12 +939,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "paste" version = "0.1.18" @@ -1115,14 +1075,26 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", - "rand_chacha", + "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", "rand_pcg", ] +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -1133,6 +1105,16 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -1154,7 +1136,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", ] [[package]] @@ -1166,6 +1157,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + [[package]] name = "rand_pcg" version = "0.2.1" @@ -1366,11 +1366,11 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a" dependencies = [ - "block-buffer 0.2.0", + "block-buffer", "byte-tools", - "digest 0.6.2", + "digest", "fake-simd", - "generic-array 0.8.4", + "generic-array", ] [[package]] @@ -1379,10 +1379,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26405905b6a56a94c60109cfda62610507ac14a65be531f5767dec5c5a8dd6a0" dependencies = [ - "block-buffer 0.2.0", + "block-buffer", "byte-tools", - "digest 0.6.2", - "generic-array 0.8.4", + "digest", + "generic-array", ] [[package]] @@ -1759,10 +1759,10 @@ dependencies = [ "debug_print", "gcd", "getopts", - "hex-literal", "libc", - "md-5", + "rand 0.8.4", "signal-hook", + "tempfile", "uucore", "uucore_procs", ] @@ -1854,7 +1854,7 @@ dependencies = [ "paste", "quickcheck", "rand 0.7.3", - "rand_chacha", + "rand_chacha 0.2.2", "smallvec", "uucore", "uucore_procs", @@ -1902,7 +1902,7 @@ version = "0.0.4" dependencies = [ "blake2-rfc", "clap", - "digest 0.6.2", + "digest", "hex", "libc", "md5", @@ -2621,12 +2621,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - [[package]] name = "void" version = "1.0.2" @@ -2650,6 +2644,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.71" diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index e10fd7f07..df7dbf483 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -17,7 +17,7 @@ path = "src/dd.rs" [dependencies] byte-unit = "4.0" debug_print = "1.0" -# Probably best to keep this identical to the version of getopts in the uucore crate +# Probably best to keep the getopts version identical to the version of getopts in the uucore crate getopts = "<= 0.2.21" gcd = "2.0" libc = "0.2" @@ -26,8 +26,8 @@ uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] -md-5 = "0.9" -hex-literal = "0.3" +rand = "0.8" +tempfile = "^3" [[bin]] name = "dd" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 459c66e54..74ee24c46 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -361,19 +361,32 @@ impl Read for Input { fn read(&mut self, mut buf: &mut [u8]) -> io::Result { - match self.src.read(&mut buf) + let mut base_idx = 0; + let tlen = buf.len(); + loop { - Ok(len) => - Ok(len), - Err(e) => - if !self.cflags.noerror + match self.src.read(&mut buf[base_idx..]) + { + Ok(0) => + return Ok(base_idx), + Ok(rlen) if self.iflags.fullblock => { - Err(e) - } - else - { - Ok(0) + base_idx += rlen; + + if base_idx >= tlen + { + return Ok(tlen) + } }, + Ok(len) => + return Ok(len), + Err(e) if e.kind() == io::ErrorKind::Interrupted => + continue, + Err(_) if self.cflags.noerror => + return Ok(base_idx), + Err(e) => + return Err(e), + } } } } @@ -529,6 +542,11 @@ fn make_unix_oflags(oflags: &OFlags) -> Option { let mut flag = 0; + // oflag=FLAG + if oflags.append + { + flag |= libc::O_APPEND; + } if oflags.direct { flag |= libc::O_DIRECT; @@ -585,9 +603,11 @@ impl Output { let mut dst = { let mut opts = OpenOptions::new(); opts.write(true) + .create(true) + .truncate(!cflags.notrunc) .append(oflags.append) - .create_new(cflags.excl || !cflags.nocreat) - .truncate(!cflags.notrunc); + // 'create_new' overrides 'create' + .create_new(cflags.excl && !cflags.nocreat); if cfg!(unix) { @@ -1484,47 +1504,57 @@ fn append_dashes_if_not_present(mut acc: Vec, s: &String) -> Vec } } +macro_rules! unpack_or_rtn ( + ($i:expr, $o:expr) => + {{ + match ($i, $o) + { + (Ok(i), Ok(o)) => + (i,o), + (Err(e), _) => + { + eprintln!("dd Error: {}", e); + return RTN_FAILURE; + }, + (_, Err(e)) => + { + eprintln!("dd Error: {}", e); + return RTN_FAILURE; + }, + } + }}; +); + pub fn uumain(args: impl uucore::Args) -> i32 { let dashed_args = args.collect_str() .iter() .fold(Vec::new(), append_dashes_if_not_present); let matches = build_app!().parse(dashed_args); + let result = match (matches.opt_present("if"), matches.opt_present("of")) { (true, true) => { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); + + dd_fileout(i,o) + }, + (false, true) => + { + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); dd_fileout(i,o) }, (true, false) => { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); dd_stdout(i,o) }, - (false, true) => + (false, false) => { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); - - dd_fileout(i,o) - }, - (false, false) => - { - let i = Input::::new(&matches) - .expect("TODO: Return correct error code"); - let o = Output::::new(&matches) - .expect("TODO: Return correct error code"); + let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); dd_stdout(i,o) }, diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 959cb53fd..2195b1b84 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -1,19 +1,5 @@ use super::*; -struct LazyReader -{ - src: R, -} - -impl Read for LazyReader -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result - { - let reduced = cmp::max(buf.len() / 2, 1); - self.src.read(&mut buf[..reduced]) - } -} - macro_rules! make_sync_test ( ( $test_id:ident, $test_name:expr, $src:expr, $sync:expr, $ibs:expr, $obs:expr, $spec:expr ) => { diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index eb8af60df..019943e59 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -5,14 +5,10 @@ mod conversion_tests; mod block_unblock_tests; mod conv_sync_tests; +use rand::prelude::*; use std::io::prelude::*; use std::io::BufReader; use std::fs; -use md5::{ Md5, Digest, }; -use hex_literal::hex; - -// use tempfile::tempfile; -// TODO: (Maybe) Use tempfiles in the tests. const DEFAULT_CFO: OConvFlags = OConvFlags { sparse: false, @@ -60,6 +56,40 @@ const DEFAULT_OFLAGS: OFlags = OFlags { seek_bytes: false, }; +struct LazyReader +{ + src: R, +} + +impl Read for LazyReader +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result + { + let reduced = cmp::max(buf.len() / 2, 1); + self.src.read(&mut buf[..reduced]) + } +} + +struct FickleReader +{ + src: R, +} + +impl Read for FickleReader +{ + fn read(&mut self, mut buf: &mut [u8]) -> io::Result + { + if rand::random() + { + self.src.read(&mut buf) + } + else + { + Ok(0) + } + } +} + #[macro_export] macro_rules! icf ( () => diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index e9923b4b1..8920ac04d 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -212,6 +212,86 @@ make_io_test!( File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); +make_io_test!( + random_73k_test_lazy_fullblock, + "random-73k-test-lazy-fullblock", + Input { + src: LazyReader { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() + }, + non_ascii: false, + ibs: 521, + xfer_stats: None, + count: None, + cflags: icf!(), + iflags: IFlags { + fullblock: true, + 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, + count_bytes: false, + skip_bytes: false, + }, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() +); + +make_io_test!( + random_73k_test_fickle_fullblock, + "random-73k-test-fickle-fullblock", + Input { + src: FickleReader { + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() + }, + non_ascii: false, + ibs: 521, + xfer_stats: None, + count: None, + cflags: icf!(), + iflags: IFlags { + fullblock: true, + 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, + count_bytes: false, + skip_bytes: false, + }, + }, + Output { + dst: DST_PLACEHOLDER, + obs: 1031, + cflags: DEFAULT_CFO, + oflags: DEFAULT_OFLAGS, + }, + File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() +); + // Test internal buffer size fn #[test] fn bsize_test_primes() diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index b4ee0efb2..a8ec0527c 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -33,7 +33,53 @@ pub enum ParseError impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "dd-args: Parse Error") + match self + { + Self::MultipleFmtTable => + { + write!(f, "Only one of conv=ascii conv=ebcdic or conv=ibm may be specified") + }, + Self::MultipleUCaseLCase => + { + write!(f, "Only one of conv=lcase or conv=ucase may be specified") + }, + Self::MultipleBlockUnblock => + { + write!(f, "Only one of conv=block or conv=unblock may be specified") + }, + Self::MultipleExclNoCreat => + { + write!(f, "Only one ov conv=excl or conv=nocreat may be specified") + }, + Self::FlagNoMatch(arg) => + { + write!(f, "Unrecognized iflag=FLAG or oflag=FLAG -> {}", arg) + }, + Self::ConvFlagNoMatch(arg) => + { + write!(f, "Unrecognized conv=CONV -> {}", arg) + }, + Self::NoMatchingMultiplier(arg) => + { + write!(f, "Unrecognized byte multiplier -> {}", arg) + }, + Self::ByteStringContainsNoValue(arg) => + { + write!(f, "Unrecognized byte value -> {}", arg) + }, + Self::MultiplierStringWouldOverflow(arg) => + { + write!(f, "Multiplier string would overflow on current system -> {}", arg) + }, + Self::BlockUnblockWithoutCBS => + { + write!(f, "conv=block or conv=unblock specified without cbs=N") + }, + Self::StatusLevelNotRecognized(arg) => + { + write!(f, "status=LEVEL not recognized -> {}", arg) + }, + } } } @@ -270,23 +316,24 @@ fn parse_bytes_only(s: &str) -> Result fn parse_bytes_with_opt_multiplier(s: String) -> Result { - if let Some(idx) = s.find(char::is_alphabetic) + match s.find(char::is_alphabetic) { - let base = parse_bytes_only(&s[..idx])?; - let mult = parse_multiplier(&s[idx..])?; + Some(idx) => + { + let base = parse_bytes_only(&s[..idx])?; + let mult = parse_multiplier(&s[idx..])?; - if let Some(bytes) = base.checked_mul(mult) - { - Ok(bytes) + if let Some(bytes) = base.checked_mul(mult) + { + Ok(bytes) + } + else + { + Err(ParseError::MultiplierStringWouldOverflow(s)) + } } - else - { - Err(ParseError::MultiplierStringWouldOverflow(s)) - } - } - else - { - parse_bytes_only(&s) + _ => + parse_bytes_only(&s), } } diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index b9eb7f3ac..cf8be384c 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,5 +1,107 @@ use crate::common::util::*; +use std::io::{BufReader, Read, Write}; +use std::path::{Path, PathBuf}; +use std::fs::{self, OpenOptions, File}; +use std::time::{Duration, SystemTime}; +use tempfile::tempfile; + +macro_rules! inf +{ + ($fname:expr) => + {{ + &format!("if={}", $fname) + }}; +} + +macro_rules! of +{ + ($fname:expr) => + {{ + &format!("of={}", $fname) + }}; +} + +macro_rules! fixture_path +{ + ($fname:expr) => + {{ + PathBuf::from(format!("./tests/fixtures/dd/{}", $fname)) + }}; +} + +macro_rules! assert_fixture_exists +{ + ($fname:expr) => + {{ + let fpath = fixture_path!($fname); + if !fpath.exists() + { + panic!("Fixture missing: {:?}", fpath); + } + }}; +} + +macro_rules! assert_fixture_not_exists +{ + ($fname:expr) => + {{ + let fpath = PathBuf::from(format!("./fixtures/dd/{}", $fname)); + if fpath.exists() + { + panic!("Fixture present: {:?}", fpath); + } + }}; +} + +macro_rules! build_test_file { + ($fp:expr, $data:expr) => {{ + OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open($fp) + .unwrap() + .write_all($data) + .unwrap() + }}; +} + +macro_rules! cmp_file ( + ($spec:expr, $test:expr) => + { + let specfile_len = $spec.metadata().unwrap().len(); + let testfile_len = $test.metadata().unwrap().len(); + assert_eq!(testfile_len, specfile_len); + + let spec = BufReader::new($spec); + let test = BufReader::new($test); + + for (b_spec, b_test) in spec.bytes().zip(test.bytes()) + { + assert_eq!(b_spec.unwrap(), + b_test.unwrap()); + } + }; +); + +fn build_ascii_block(n: usize) -> Vec +{ + (0..=127) + .cycle() + .take(n) + .collect() +} + +fn build_ebcdic_block(n: usize) -> Vec +{ + (0..=255) + .cycle() + .take(n) + .collect() +} + +// Sanity Tests #[test] fn version() { @@ -16,16 +118,6 @@ fn help() .succeeds(); } -fn build_ascii_block(n: usize) -> Vec -{ - vec!['a', 'b', 'c', 'd', 'e', 'f'] - .into_iter() - .map(|c| c as u8) - .cycle() - .take(n) - .collect() -} - #[test] fn test_stdin_stdout() { @@ -34,10 +126,13 @@ fn test_stdin_stdout() new_ucmd!() .args(&["status=none"]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } +// Top-Level Items +// count=N, skip=N, status=LEVEL, conv=FLAG, *flag=FLAG #[test] fn test_stdin_stdout_count() { @@ -51,7 +146,8 @@ fn test_stdin_stdout_count() "ibs=128", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } @@ -68,7 +164,8 @@ fn test_stdin_stdout_count_bytes() "iflag=count_bytes", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } @@ -85,7 +182,8 @@ fn test_stdin_stdout_skip() "ibs=128", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } @@ -103,14 +201,51 @@ fn test_stdin_stdout_skip_bytes() "iflag=skip_bytes", ]) .pipe_in(input) - .succeeds() + .run() + .no_stderr() .stdout_only(output); } +#[test] +fn test_stdin_stdout_skip_w_multiplier() +{ + let input = build_ascii_block(10*1024); + let output = String::from_utf8(input[5*1024..].to_vec()).unwrap(); + new_ucmd!() + .args(&[ + "status=none", + "skip=5K", + "iflag=skip_bytes" + ]) + .pipe_in(input) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + +#[test] +fn test_stdin_stdout_count_w_multiplier() +{ + let input = build_ascii_block(5*1024); + let output = String::from_utf8(input[..2*1024].to_vec()).unwrap(); + new_ucmd!() + .args(&[ + "status=none", + "count=2KiB", + "iflag=count_bytes", + ]) + .pipe_in(input) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + #[test] fn test_final_stats_noxfer() { - new_ucmd!() + new_ucmd!() .args(&[ "status=noxfer", ]) @@ -133,49 +268,246 @@ fn test_final_stats_unspec() acc }); new_ucmd!() - .succeeds() - .stderr_only(&output); + .run() + .stderr_only(&output) + .success(); } #[test] -fn test_self_transfer() +fn test_excl_causes_failure_when_present() { - panic!(); - // TODO: Make new copy per-test - new_ucmd!() - .args(&[ - "conv=notruc", - "if=../fixtures/dd/zero-256k.copy", - "of=../fixtures/dd/zero-256k.copy", - ]) - .succeeds(); - assert!(false/* Must check that zero256k.copy still == zero-256k.txt */) + let fname = "this-file-exists-excl.txt"; + assert_fixture_exists!(&fname); + + let (_fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "of=this-file-exists-excl.txt", + "conv=excl", + ]) + .fails(); } -#[cfg(unix)] #[test] -fn test_null() +fn test_atime_updated() +{ + let fname = "this-file-exists-no-noatime.txt"; + assert_fixture_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + inf!(fname), + ]); + + let pre_atime = fix.metadata(&fname).accessed().unwrap(); + + ucmd.pipe_in("") + .run() + .no_stderr() + .success(); + + let post_atime = fix.metadata(&fname).accessed().unwrap(); + assert!(pre_atime != post_atime); +} + +#[test] +fn test_noatime_does_not_update_infile_atime() +{ + let fname = "this-ifile-exists-noatime.txt"; + assert_fixture_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "iflag=noatime", + inf!(fname), + ]); + + let pre_atime = fix.metadata(&fname).accessed().unwrap(); + + ucmd.run() + .no_stderr() + .success(); + + let post_atime = fix.metadata(&fname).accessed().unwrap(); + assert_eq!(pre_atime, post_atime); +} + +#[test] +fn test_noatime_does_not_update_ofile_atime() +{ + let fname = "this-ofile-exists-noatime.txt"; + assert_fixture_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "oflag=noatime", + of!(fname), + ]); + + let pre_atime = fix.metadata(&fname).accessed().unwrap(); + + ucmd.pipe_in("") + .run() + .no_stderr() + .success(); + + let post_atime = fix.metadata(&fname).accessed().unwrap(); + assert_eq!(pre_atime, post_atime); +} + +#[test] +fn test_nocreat_causes_failure_when_not_present() +{ + let fname = "this-file-does-not-exist.txt"; + assert_fixture_not_exists!(&fname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "conv=nocreat", + of!(&fname), + ]) + .run(); + + assert!(!fix.file_exists(&fname)); + + ucmd.fails(); +} + +#[test] +fn test_notrunc_does_not_truncate() +{ + // Set up test if needed (eg. after failure) + let fname = "this-file-exists-notrunc.txt"; + let fpath = fixture_path!(fname); + match fpath.metadata() + { + Ok(m) if m.len() == 256 => {}, + _ => + build_test_file!(&fpath, &build_ascii_block(256)), + } + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "conv=notrunc", + of!(&fname), + "if=null.txt", + ]) + .run() + .no_stdout() + .no_stderr() + .success(); + + assert_eq!(256, fix.metadata(&fname).len()); +} + +#[test] +fn test_existing_file_truncated() +{ + // Set up test if needed (eg. after failure) + let fname = "this-file-exists-truncated.txt"; + let fpath = fixture_path!(fname); + match fpath.metadata() + { + Ok(m) if m.len() == 256 => {}, + _ => + build_test_file!(&fpath, &vec![0; 256]), + } + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "if=null.txt", + of!(fname), + ]) + .run() + .no_stdout() + .no_stderr() + .success(); + + assert_eq!(0, fix.metadata(&fname).len()); +} + +#[test] +fn test_null_stats() { let stats = vec![ - "0+0 records in", - "0+0 records out", - "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s", + "0+0 records in\n", + "0+0 records out\n", + "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s\n", ]; let stats = stats.into_iter() .fold(String::new(), | mut acc, s | { acc.push_str(s); - acc.push('\n'); acc }); + new_ucmd!() .args(&[ - "if=/dev/null", + "if=null.txt", ]) - .succeeds() + .run() .stderr_only(stats) - .stdout_only(""); + .success(); } +#[test] +fn test_null_fullblock() +{ + new_ucmd!() + .args(&[ + "if=null.txt", + "status=none", + "iflag=fullblock", + ]) + .run() + .no_stdout() + .no_stderr() + .success(); +} + +#[cfg(unix)] +// #[ignore] // See note below before running this test! +#[test] +fn test_fullblock() +{ + let tname = "fullblock-from-urand"; + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + let stats = vec![ + "1+0 records in\n", + "1+0 records out\n", + "134217728 bytes (134 MB, 128 MiB) copied,", + ]; + let stats = stats.into_iter() + .fold(String::new(), | mut acc, s | { + acc.push_str(s); + acc + }); + + let res = new_ucmd!() + .args(&[ + "if=/dev/urandom", + of!(&tmp_fn), + "bs=128M", + // Note: In order for this test to actually test iflag=fullblock, the bs=VALUE + // must be big enough to 'overwhelm' urandom's store of bytes. + // Try executing 'dd if=/dev/urandom bs=128M count=1' (i.e without iflag=fullblock). + // The stats should contain the line: '0+1 records in' indicating a partial read. + // Since my system only copies 32 MiB without fullblock, I expect 128 MiB to be + // a reasonable value for testing most systems. + "count=1", + "iflag=fullblock", + ]) + .run(); + + res.success(); + let res_stats = &res.stderr[..stats.len()]; + assert_eq!(&stats, res_stats); +} + +// Fileio #[test] fn test_ys_to_stdout() { @@ -188,10 +520,13 @@ fn test_ys_to_stdout() new_ucmd!() .args(&[ - "if=../fixtures/dd/y-nl-1k.txt", + "status=none", + "if=y-nl-1k.txt", ]) .run() - .stdout_only(output); + .no_stderr() + .stdout_is(output) + .success(); } #[test] @@ -201,9 +536,176 @@ fn test_zeros_to_stdout() let output = String::from_utf8(output).unwrap(); new_ucmd!() .args(&[ - "if=../fixtures/dd/zero-256k.txt", + "status=none", + "if=zero-256k.txt", ]) .run() - .stdout_only(output); + .no_stderr() + .stdout_is(output) + .success(); } +#[test] +fn test_to_stdout_with_ibs_obs() +{ + let output: Vec<_> = String::from("y\n") + .bytes() + .cycle() + .take(1024) + .collect(); + let output = String::from_utf8(output).unwrap(); + + new_ucmd!() + .args(&[ + "status=none", + "if=y-nl-1k.txt", + "ibs=521", + "obs=1031", + ]) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + +#[test] +fn test_ascii_10k_to_stdout() +{ + let output = build_ascii_block(1024*1024); + // build_test_file!("ascii-10k.txt", &output); + let output = String::from_utf8(output).unwrap(); + + new_ucmd!() + .args(&[ + "status=none", + "if=ascii-10k.txt", + ]) + .run() + .no_stderr() + .stdout_is(output) + .success(); +} + +#[test] +fn test_zeros_to_file() +{ + let tname = "zero-256k"; + let test_fn = format!("{}.txt", tname); + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + inf!(test_fn), + of!(tmp_fn), + ]) + .run() + .no_stderr() + .no_stdout() + .success(); + + cmp_file!(File::open(fixture_path!(&test_fn)).unwrap(), + fix.open(&tmp_fn)); +} + +#[test] +fn test_to_file_with_ibs_obs() +{ + let tname = "zero-256k"; + let test_fn = format!("{}.txt", tname); + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + inf!(test_fn), + of!(tmp_fn), + "ibs=222", + "obs=111", + ]) + .run() + .no_stderr() + .no_stdout() + .success(); + + cmp_file!(File::open(fixture_path!(&test_fn)).unwrap(), + fix.open(&tmp_fn)); +} + +#[test] +fn test_ascii_521k_to_file() +{ + let tname = "ascii-521k"; + let input = build_ascii_block(512*1024); + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + of!(tmp_fn), + ]) + .pipe_in(input.clone()) + .run() + .no_stderr() + .no_stdout() + .success(); + + assert_eq!(512*1024, fix.metadata(&tmp_fn).len()); + + cmp_file!({ let mut input_f = tempfile().unwrap(); + input_f.write(&input).unwrap(); + input_f }, + fix.open(&tmp_fn)); +} + +#[ignore] +#[cfg(unix)] +#[test] +fn test_ascii_5_gibi_to_file() +{ + let tname = "ascii-5G"; + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "count=5G", + "iflag=count_bytes", + "if=/dev/zero", + of!(tmp_fn), + ]) + .run() + .no_stderr() + .no_stdout() + .success(); + + assert_eq!(5*1024*1024*1024, fix.metadata(&tmp_fn).len()); +} + +#[test] +fn test_self_transfer() +{ + let fname = "self-transfer-256k.txt"; + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + "conv=notrunc", + inf!(fname), + of!(fname), + ]); + + assert!(fix.file_exists(fname)); + assert_eq!(256*1024, fix.metadata(fname).len()); + + ucmd.run() + .no_stdout() + .no_stderr() + .success(); + + assert!(fix.file_exists(fname)); + assert_eq!(256*1024, fix.metadata(fname).len()); +} + +// conv=[ascii,ebcdic,ibm], conv=[ucase,lcase], conv=[block,unblock], conv=sync +// TODO: Move conv tests from unit test module diff --git a/tests/fixtures/dd/ascii-10k.txt b/tests/fixtures/dd/ascii-10k.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e72e94d4bb4478a252903b4ffe86c95348c1c9d GIT binary patch literal 1048576 zcmZQzWMXDvWn<^yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13D6@)8ANzlYqbA4+W#IMe;6Hq86AHb9e*1ge;gfu9UXrj9e*F4e;A#A8J&L` zoqrphe;l2E9i4w3oqr!)e=xfKVs!n<==z({^+%)YuSVCOjjq2NU4J;b{&IBv>FD~~ z(e=lp>#s-GpO3D;AKia2y8mKy|H0uIJ*CGbpPq-{@c<0 z$D{kNNB5tP?!O;Be_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< zVD$Wf(enpJ&mR~)e_-_dfzk5^M$aD@J%3>I{DIN)2S(2y7(IVr^!$O*^9M%H9~eD< PVD$Wf(enp}{`ms{2jM%3 literal 0 HcmV?d00001 diff --git a/tests/fixtures/dd/gnudd-conv-atoe-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-atoe-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..8701c67fdb6b591c4d6a5edaa4862cd00bd84101 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=l|!?3wfDZr{3l=l;Fd Ruin0S|L*s%zkmM!0{}7Ge}Mo1 literal 0 HcmV?d00001 diff --git a/tests/fixtures/dd/gnudd-conv-atoibm-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-atoibm-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..3835d57cfb07e4c9021a7b8e0e2447a212c3032b GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=F{MD=0u35isa}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&vL?)#J#X%U`KwoXty#Zr&h5-No literal 0 HcmV?d00001 diff --git a/tests/fixtures/dd/gnudd-conv-ebcdic-utol-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ebcdic-utol-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..13c82e5e3c8500dbc52f6820c5fe57b5ab5e9762 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=;wv8K7HwWYnSvt!c4DU+v7oiTmU%q5GLEnTsE?aI9ma%VMG z;*vkG@`68myrP1#lBkL}o2G`g7N?Fhm!W~Nl&J}anWcrbl?cm2J10jMXE#?5_dw6! zpwN);u*ityq|}u3w9Jgcg5sjmlJc^Kiteu7p8me933F!8o4a8C>Q!EA*00;RVe_V~ sTeff8xnuXPg?skzJALZxne*pv-@1F}{=L_)-oAPN?)R_1fBydi0I5)U0RR91 literal 0 HcmV?d00001 diff --git a/tests/fixtures/dd/gnudd-conv-etoa-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-etoa-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..433b1f600bebcdb8e30264c727d5e5c7b6eac6af GIT binary patch literal 256 zcmZQ%U}oZ+Q{UD*ox6{hho4VSKv<}CZaashME@jN8F{(JhUO-&7HRR$j_xkj9(J~g z6DEsIox(D0=8W01L`9_LEl^mraLM9jOIIw{yQ*PRqrFmX^{TaN*00;RK}AthtIk?q zck`yLTeff8xkKl4ysEw3?p=HL?B93rK*AwqD|H7Y+r-1kNvSF6X_*;Ej~qXC@gD!SFT^XdE@r2yLay2d-&k-qu3|UpE*`L zJGr{JyLo!NeDV6#+c#ddKHh%50scXO@85m=@cGmHn2_MG(1`G;$gf|%fBX5v(7@Qp R)WqD((&G28zkmM!0{|_%e}Mo1 literal 0 HcmV?d00001 diff --git a/tests/fixtures/dd/gnudd-conv-ibm-ltou-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ibm-ltou-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..d425e7d012beb1c314cc4e1f72fafed1afe378e2 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=_JxWH%1WXt;%u54+FG1C(p-iH#!{vx9A=gl)>a}c5AB>BU7X!qJ=_C5 zgM>!owmXl9N(X($g|C3JZ#hN=wSi8Y;TGdVBi&W=)tgd*0jy^H;B0yJr2mjT<&^ t+PY=?ww*h6@7lX(|Gv|w&Yn4c?)I&_ckbVN{p#(T_wRoH`upeqKL7->hPMC! literal 0 HcmV?d00001 diff --git a/tests/fixtures/dd/gnudd-conv-ibm-utol-seq-byte-values.spec b/tests/fixtures/dd/gnudd-conv-ibm-utol-seq-byte-values.spec new file mode 100644 index 0000000000000000000000000000000000000000..1dcaccbcdf8adfd826979f9273c87cd9568b0209 GIT binary patch literal 256 zcmZQ%U}n-a*Vkhe<5uP6;pY<+5EinvQ8SX1P`8(rk(Y~dsIJe6t__Iw^@~Z!PW1lx z;q#}jU%r3)`6I5X&L=;wv8K7HwWYnSvt!c4DU+v7oiTmU%q5GLEnTsE?aI9ma^o8- zamgQ8dBLB3p`wDalBkL}o2G`g7N?Fhm!W~Nl&J}anWcrbl?cm2J10jMXE#?5_dw6! zpwN);u*ityq|}u3w9Jgcg5sjmlJc^Kiteu7p8mdB6XwjGH+RAO)vMO7S-)=MhRvI{ sZrQ$V=Z@XG_U_rg@ARp&XU?Cyee3R>`}bbIdi&=6yWhY5{`vn80K=@5f_t`kd~5_k(X0cP*ze^QCHK{(ALt`(bqFH zFg7wZF*mccu(qcc@%IZ12o4Gj2@i{mh>nVliH~zkz$ITD zRa;YE*Vxe9)Y{VC*4feB)!WnGH*vz`NmHjxpEh&G>{)Z?%%8V#!Qw?rmn>hla>eRZ zYuBt_w{gSfO@5f_t`kd~5_k(X0cP*ze^QCHK{(ALt`(bqFH zFg7wZF*mccu(q{)Z?%%8V#!Qw?rmn>hla>eRZ zYuBt_w{gSfOMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v` Date: Fri, 2 Jul 2021 10:53:15 -0700 Subject: [PATCH 35/66] Build changes after merge with uutils main --- Cargo.lock | 2 +- src/uu/dd/Cargo.toml | 3 +- src/uu/dd/src/conversion_tables.rs | 1 - src/uu/dd/src/datastructures.rs | 178 +++++++++++++++++++ src/uu/dd/src/dd.rs | 274 +++++------------------------ src/uu/dd/src/parseargs.rs | 67 +++---- 6 files changed, 258 insertions(+), 267 deletions(-) create mode 100644 src/uu/dd/src/datastructures.rs diff --git a/Cargo.lock b/Cargo.lock index 1d7634aa2..9c273695c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1928,9 +1928,9 @@ name = "uu_dd" version = "0.0.4" dependencies = [ "byte-unit", + "clap", "debug_print", "gcd", - "getopts", "libc", "rand 0.8.4", "signal-hook", diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index df7dbf483..6296b83ce 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -16,9 +16,8 @@ path = "src/dd.rs" [dependencies] byte-unit = "4.0" +clap = "2.33.3" debug_print = "1.0" -# Probably best to keep the getopts version identical to the version of getopts in the uucore crate -getopts = "<= 0.2.21" gcd = "2.0" libc = "0.2" signal-hook = "0.3.9" diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs index 9ba604465..755331eed 100644 --- a/src/uu/dd/src/conversion_tables.rs +++ b/src/uu/dd/src/conversion_tables.rs @@ -1,4 +1,3 @@ - // Conversion tables are just lookup tables. // eg. The ASCII->EBCDIC table stores the EBCDIC code at the index // obtained by treating the ASCII representation as a number. diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs new file mode 100644 index 000000000..cb720aeb7 --- /dev/null +++ b/src/uu/dd/src/datastructures.rs @@ -0,0 +1,178 @@ +use crate::conversion_tables::*; + +use std::error::Error; +use std::time; + +pub struct ProgUpdate +{ + pub reads_complete: u64, + pub reads_partial: u64, + pub writes_complete: u64, + pub writes_partial: u64, + pub bytes_total: u128, + pub records_truncated: u32, + pub duration: time::Duration, +} + +pub struct ReadStat +{ + pub reads_complete: u64, + pub reads_partial: u64, + pub records_truncated: u32, +} +impl std::ops::AddAssign for ReadStat +{ + fn add_assign(&mut self, other: Self) + { + *self = Self { + reads_complete: self.reads_complete + other.reads_complete, + reads_partial: self.reads_partial + other.reads_partial, + records_truncated: self.records_truncated + other.records_truncated, + } + } +} + +pub struct WriteStat +{ + pub writes_complete: u64, + pub writes_partial: u64, + pub bytes_total: u128, +} +impl std::ops::AddAssign for WriteStat +{ + fn add_assign(&mut self, other: Self) + { + *self = Self { + writes_complete: self.writes_complete + other.writes_complete, + writes_partial: self.writes_partial + other.writes_partial, + bytes_total: self.bytes_total + other.bytes_total, + } + } +} + +type Cbs = usize; + +/// Stores all Conv Flags that apply to the input +pub struct IConvFlags +{ + pub ctable: Option<&'static ConversionTable>, + pub block: Option, + pub unblock: Option, + pub swab: bool, + pub sync: Option, + pub noerror: bool, +} + +/// Stores all Conv Flags that apply to the output +#[derive(Debug, PartialEq)] +pub struct OConvFlags +{ + pub sparse: bool, + pub excl: bool, + pub nocreat: bool, + pub notrunc: bool, + pub fdatasync: bool, + pub fsync: bool, +} + +/// Stores all Flags that apply to the input +pub struct IFlags +{ + pub cio: bool, + pub direct: bool, + pub directory: bool, + pub dsync: bool, + pub sync: bool, + pub nocache: bool, + pub nonblock: bool, + pub noatime: bool, + pub noctty: bool, + pub nofollow: bool, + pub nolinks: bool, + pub binary: bool, + pub text: bool, + pub fullblock: bool, + pub count_bytes: bool, + pub skip_bytes: bool, +} + +/// Stores all Flags that apply to the output +pub struct OFlags +{ + pub append: bool, + pub cio: bool, + pub direct: bool, + pub directory: bool, + pub dsync: bool, + pub sync: bool, + pub nocache: bool, + pub nonblock: bool, + pub noatime: bool, + pub noctty: bool, + pub nofollow: bool, + pub nolinks: bool, + pub binary: bool, + pub text: bool, + pub seek_bytes: bool, +} + +/// The value of the status cl-option. +/// Controls printing of transfer stats +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum StatusLevel +{ + Progress, + Noxfer, + None, +} + +/// The value of count=N +/// Defaults to Reads(N) +/// if iflag=count_bytes +/// then becomes Bytes(N) +pub enum CountType +{ + Reads(usize), + Bytes(usize), +} + +#[derive(Debug)] +pub enum InternalError +{ + WrongInputType, + WrongOutputType, + InvalidConvBlockUnblockCase, +} + +impl std::fmt::Display for InternalError +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self + { + Self::WrongInputType | + Self::WrongOutputType => + write!(f, "Internal dd error: Wrong Input/Output data type"), + Self::InvalidConvBlockUnblockCase => + write!(f, "Internal dd error: Invalid Conversion, Block, or Unblock data"), + } + } +} + +impl Error for InternalError {} + +pub mod options +{ + pub const INFILE: &'static str = "if"; + pub const OUTFILE: &'static str = "of"; + pub const IBS: &'static str = "ibs"; + pub const OBS: &'static str = "obs"; + pub const BS: &'static str = "bs"; + pub const CBS: &'static str = "cbs"; + pub const COUNT: &'static str = "count"; + pub const SKIP: &'static str = "skip"; + pub const SEEK: &'static str = "seek"; + pub const STATUS: &'static str = "status"; + pub const CONV: &'static str = "conv"; + pub const IFLAG: &'static str = "iflag"; + pub const OFLAG: &'static str = "oflag"; +} diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index c81d8da2f..fe43be397 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -14,202 +14,40 @@ use uucore::InvalidEncodingHandling; #[cfg(test)] mod dd_unit_tests; +mod datastructures; +use datastructures::*; + mod parseargs; +use parseargs::Matches; mod conversion_tables; use conversion_tables::*; +use clap::{self, crate_version}; use byte_unit::Byte; use debug_print::debug_println; use gcd::Gcd; -use getopts; use signal_hook::consts::signal; use std::cmp; use std::convert::TryInto; use std::error::Error; use std::env; -use std::fs::{ - File, OpenOptions, -}; -use std::io::{ - self, Read, Write, - Seek, -}; +use std::fs::{File, OpenOptions, }; +use std::io::{self, Read, Write, Seek, }; #[cfg(unix)] use libc; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; -use std::sync::{ - Arc, atomic::AtomicUsize, mpsc, atomic::Ordering, -}; +use std::sync::{Arc, atomic::AtomicUsize, mpsc, atomic::Ordering, }; use std::thread; use std::time; const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; -const SUMMARY: &str = "copy, and optionally convert, a file system resource"; -const LONG_HELP: &str = ""; +const ABOUT: &str = "copy, and optionally convert, a file system resource"; const BUF_INIT_BYTE: u8 = 0xDD; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; -// ----- Datatypes ----- -struct ProgUpdate -{ - reads_complete: u64, - reads_partial: u64, - writes_complete: u64, - writes_partial: u64, - bytes_total: u128, - records_truncated: u32, - duration: time::Duration, -} - -struct ReadStat -{ - reads_complete: u64, - reads_partial: u64, - records_truncated: u32, -} -impl std::ops::AddAssign for ReadStat -{ - fn add_assign(&mut self, other: Self) - { - *self = Self { - reads_complete: self.reads_complete + other.reads_complete, - reads_partial: self.reads_partial + other.reads_partial, - records_truncated: self.records_truncated + other.records_truncated, - } - } -} - -struct WriteStat -{ - writes_complete: u64, - writes_partial: u64, - bytes_total: u128, -} -impl std::ops::AddAssign for WriteStat -{ - fn add_assign(&mut self, other: Self) - { - *self = Self { - writes_complete: self.writes_complete + other.writes_complete, - writes_partial: self.writes_partial + other.writes_partial, - bytes_total: self.bytes_total + other.bytes_total, - } - } -} - -type Cbs = usize; - -/// Stores all Conv Flags that apply to the input -pub struct IConvFlags -{ - ctable: Option<&'static ConversionTable>, - block: Option, - unblock: Option, - swab: bool, - sync: Option, - noerror: bool, -} - -/// Stores all Conv Flags that apply to the output -#[derive(Debug, PartialEq)] -pub struct OConvFlags -{ - sparse: bool, - excl: bool, - nocreat: bool, - notrunc: bool, - fdatasync: bool, - 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(Copy, Clone, Debug, PartialEq)] -pub enum StatusLevel -{ - Progress, - Noxfer, - None, -} - -/// The value of count=N -/// Defaults to Reads(N) -/// if iflag=count_bytes -/// then becomes Bytes(N) -pub enum CountType -{ - Reads(usize), - Bytes(usize), -} - -#[derive(Debug)] -enum InternalError -{ - WrongInputType, - WrongOutputType, - InvalidConvBlockUnblockCase, -} - -impl std::fmt::Display for InternalError -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self - { - Self::WrongInputType | - Self::WrongOutputType => - write!(f, "Internal dd error: Wrong Input/Output data type"), - Self::InvalidConvBlockUnblockCase => - write!(f, "Internal dd error: Invalid Conversion, Block, or Unblock data"), - } - } -} - -impl Error for InternalError {} - struct Input { src: R, @@ -223,7 +61,7 @@ struct Input impl Input { - fn new(matches: &getopts::Matches) -> Result> + fn new(matches: &Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?; @@ -303,7 +141,7 @@ fn make_unix_iflags(oflags: &IFlags) -> Option impl Input { - fn new(matches: &getopts::Matches) -> Result> + fn new(matches: &Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?; @@ -313,7 +151,7 @@ impl Input let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; let count = parseargs::parse_count(&iflags, matches)?; - if let Some(fname) = matches.opt_str("if") + if let Some(fname) = matches.value_of("if") { let mut src = { @@ -359,7 +197,7 @@ impl Input impl Read for Input { - fn read(&mut self, mut buf: &mut [u8]) -> io::Result + fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut base_idx = 0; let tlen = buf.len(); @@ -513,21 +351,16 @@ struct Output } impl Output { - fn new(matches: &getopts::Matches) -> Result> + fn new(matches: &Matches) -> Result> { let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; let oflags = parseargs::parse_oflags(matches)?; - let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; - if let Some(amt) = seek - { - let amt: u64 = amt.try_into()?; - dst.seek(io::SeekFrom::Start(amt))?; - } + let dst = io::stdout(); Ok(Output { - dst: io::stdout(), + dst, obs, cflags, oflags, @@ -598,14 +431,14 @@ fn make_unix_oflags(oflags: &OFlags) -> Option } impl Output { - fn new(matches: &getopts::Matches) -> Result> + fn new(matches: &Matches) -> Result> { let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; let oflags = parseargs::parse_oflags(matches)?; let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; - if let Some(fname) = matches.opt_str("of") + if let Some(fname) = matches.value_of("of") { let mut dst = { let mut opts = OpenOptions::new(); @@ -702,37 +535,11 @@ impl Write for Output } } -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 { - #[inline] - fn is_sparse(buf: &[u8]) -> bool - { - buf.iter() - .all(|&e| e == 0u8) - } - // ----------------------------- - if self.cflags.sparse && is_sparse(buf) - { - let seek_amt: i64 = buf.len() - .try_into() - .expect("Internal dd Error: Seek amount greater than signed 64-bit integer"); - self.dst.seek(io::SeekFrom::Current(seek_amt))?; - Ok(buf.len()) - } - else - { - self.dst.write(buf) - } + self.dst.write(buf) } fn flush(&mut self) -> io::Result<()> @@ -1446,9 +1253,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 .accept_any() .iter() .fold(Vec::new(), append_dashes_if_not_present); - let matches = build_app!().parse(dashed_args); - let result = match (matches.opt_present("if"), matches.opt_present("of")) + let matches = build_dd_app!() + // TODO: usage, after_help + //.usage(...) + //.after_help(...) + .get_matches_from(dashed_args); + + let result = match (matches.is_present(options::INFILE), matches.is_present(options::OUTFILE)) { (true, true) => { @@ -1489,74 +1301,74 @@ pub fn uumain(args: impl uucore::Args) -> i32 } } -pub fn uu_app() -> App<'static, 'static> +pub fn uu_app() -> clap::App<'static, 'static> { - build_app!() + build_dd_app!() } #[macro_export] -macro_rules! build_app ( +macro_rules! build_dd_app ( () => { - App::new(executable!()) + clap::App::new(executable!()) .version(crate_version!()) .about(ABOUT) .arg( - Arg::with_name(options::INFILE) + clap::Arg::with_name(options::INFILE) .long(options::INFILE) .takes_value(true) .help("if=FILE (alternatively --if FILE) specifies the file used for input. When not specified, stdin is used instead") ) .arg( - Arg::with_name(options::OUTFILE) + clap::Arg::with_name(options::OUTFILE) .long(options::OUTFILE) .takes_value(true) .help("of=FILE (alternatively --of FILE) specifies the file used for output. When not specified, stdout is used instead") ) .arg( - Arg::with_name(options::IBS) + clap::Arg::with_name(options::IBS) .long(options::IBS) .takes_value(true) .help("ibs=N (alternatively --ibs N) specifies the size of buffer used for reads (default: 512). Multiplier strings permitted.") ) .arg( - Arg::with_name(options::OBS) + clap::Arg::with_name(options::OBS) .long(options::OBS) .takes_value(true) .help("obs=N (alternatively --obs N) specifies the size of buffer used for writes (default: 512). Multiplier strings permitted.") ) .arg( - Arg::with_name(options::BS) + clap::Arg::with_name(options::BS) .long(options::BS) .takes_value(true) .help("bs=N (alternatively --bs N) specifies ibs=N and obs=N (default: 512). If ibs or obs are also specified, bs=N takes presedence. Multiplier strings permitted.") ) .arg( - Arg::with_name(options::CBS) + clap::Arg::with_name(options::CBS) .long(options::CBS) .takes_value(true) .help("cbs=BYTES (alternatively --cbs BYTES) specifies the 'conversion block size' in bytes. Applies to the conv=block, and conv=unblock operations. Multiplier strings permitted.") ) .arg( - Arg::with_name(options::SKIP) + clap::Arg::with_name(options::SKIP) .long(options::SKIP) .takes_value(true) .help("skip=N (alternatively --skip N) causes N ibs-sized records of input to be skipped before beginning copy/convert operations. See iflag=count_bytes if skipping N bytes is prefered. Multiplier strings permitted.") ) .arg( - Arg::with_name(options::SEEK) + clap::Arg::with_name(options::SEEK) .long(options::SEEK) .takes_value(true) .help("seek=N (alternatively --seek N) seeks N obs-sized records into output before beginning copy/convert operations. See oflag=seek_bytes if seeking N bytes is prefered. Multiplier strings permitted.") ) .arg( - Arg::with_name(options::COUNT) + clap::Arg::with_name(options::COUNT) .long(options::COUNT) .takes_value(true) .help("count=N (alternatively --count N) stop reading input after N ibs-sized read operations rather than proceeding until EOF. See iflag=count_bytes if stopping after N bytes is prefered. Multiplier strings permitted.") ) .arg( - Arg::with_name(options::STATUS) + clap::Arg::with_name(options::STATUS) .long(options::STATUS) .takes_value(true) .help("status=LEVEL (alternatively --status LEVEL) controls whether volume and performace stats are written to stderr. @@ -1577,7 +1389,7 @@ Printing performance stats is also triggered by the INFO signal (where supported ") ) .arg( - Arg::with_name(options::CONV) + clap::Arg::with_name(options::CONV) .long(options::CONV) .takes_value(true) .help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file-flags. Conversion options and file flags may be intermixed. @@ -1611,7 +1423,7 @@ Flags: ") ) .arg( - Arg::with_name(options::IFLAG) + clap::Arg::with_name(options::IFLAG) .long(options::IFLAG) .takes_value(true) .help("iflag=FLAG[,FLAG] (alternatively --iflag FLAG[,FLAG]) a comma separated list of input flags which specify how the input source is treated. FLAG may be any of the input-flags or general-flags specified below. @@ -1638,7 +1450,7 @@ Output-Flags ") ) .arg( - Arg::with_name(options::OFLAG) + clap::Arg::with_name(options::OFLAG) .long(options::OFLAG) .takes_value(true) .help("oflag=FLAG[,FLAG] (alternatively --oflag FLAG[,FLAG]) a comma separated list of output flags which specify how the output source is treated. FLAG may be any of the output-flags or general-flags specified below. diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index a8ec0527c..258a88315 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -13,6 +13,8 @@ use crate::{ use std::error::Error; +pub type Matches = clap::ArgMatches<'static>; + /// Parser Errors describe errors with parser input #[derive(Debug)] pub enum ParseError @@ -300,21 +302,22 @@ fn parse_multiplier<'a>(s: &'a str) -> Result // "Y" | "YiB" => // Ok(1024*1024*1024*1024*1024*1024*1024*1024), _ => - Err(ParseError::NoMatchingMultiplier(String::from(s))), + Err(ParseError::NoMatchingMultiplier(s.to_string())), } } fn parse_bytes_only(s: &str) -> Result { - let bytes: usize = match s.parse() + match s.parse() { - Ok(val) => val, - Err(_) => return Err(ParseError::ByteStringContainsNoValue(String::from(s))), - }; - Ok(bytes) + Ok(bytes) => + Ok(bytes), + Err(_) => + Err(ParseError::ByteStringContainsNoValue(s.to_string())), + } } -fn parse_bytes_with_opt_multiplier(s: String) -> Result +fn parse_bytes_with_opt_multiplier(s: &str) -> Result { match s.find(char::is_alphabetic) { @@ -329,7 +332,7 @@ fn parse_bytes_with_opt_multiplier(s: String) -> Result } else { - Err(ParseError::MultiplierStringWouldOverflow(s)) + Err(ParseError::MultiplierStringWouldOverflow(s.to_string())) } } _ => @@ -337,13 +340,13 @@ fn parse_bytes_with_opt_multiplier(s: String) -> Result } } -pub fn parse_ibs(matches: &getopts::Matches) -> Result +pub fn parse_ibs(matches: &Matches) -> Result { - if let Some(mixed_str) = matches.opt_str("bs") + if let Some(mixed_str) = matches.value_of("bs") { parse_bytes_with_opt_multiplier(mixed_str) } - else if let Some(mixed_str) = matches.opt_str("ibs") + else if let Some(mixed_str) = matches.value_of("ibs") { parse_bytes_with_opt_multiplier(mixed_str) } @@ -353,9 +356,9 @@ pub fn parse_ibs(matches: &getopts::Matches) -> Result } } -fn parse_cbs(matches: &getopts::Matches) -> Result, ParseError> +fn parse_cbs(matches: &Matches) -> Result, ParseError> { - if let Some(s) = matches.opt_str("cbs") + if let Some(s) = matches.value_of("cbs") { let bytes = parse_bytes_with_opt_multiplier(s)?; Ok(Some(bytes)) @@ -366,9 +369,9 @@ fn parse_cbs(matches: &getopts::Matches) -> Result, ParseError> } } -pub fn parse_status_level(matches: &getopts::Matches) -> Result, ParseError> +pub fn parse_status_level(matches: &Matches) -> Result, ParseError> { - match matches.opt_str("status") + match matches.value_of("status") { Some(s) => { @@ -380,13 +383,13 @@ pub fn parse_status_level(matches: &getopts::Matches) -> Result Result +pub fn parse_obs(matches: &Matches) -> Result { - if let Some(mixed_str) = matches.opt_str("bs") + if let Some(mixed_str) = matches.value_of("bs") { parse_bytes_with_opt_multiplier(mixed_str) } - else if let Some(mixed_str) = matches.opt_str("obs") + else if let Some(mixed_str) = matches.value_of("obs") { parse_bytes_with_opt_multiplier(mixed_str) } @@ -452,11 +455,11 @@ fn parse_ctable(fmt: Option, case: Option) -> Option<&'stati } } -fn parse_flag_list>(tag: &str, matches: &getopts::Matches) -> Result, ParseError> +fn parse_flag_list>(tag: &str, matches: &Matches) -> Result, ParseError> { let mut flags = Vec::new(); - if let Some(comma_str) = matches.opt_str(tag) + if let Some(comma_str) = matches.value_of(tag) { for s in comma_str.split(",") { @@ -470,7 +473,7 @@ fn parse_flag_list>(tag: &str, matches: & /// Parse Conversion Options (Input Variety) /// Construct and validate a IConvFlags -pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result +pub fn parse_conv_flag_input(matches: &Matches) -> Result { let flags = parse_flag_list("conv", matches)?; let cbs = parse_cbs(matches)?; @@ -588,7 +591,7 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result Result +pub fn parse_conv_flag_output(matches: &Matches) -> Result { let flags = parse_flag_list("conv", matches)?; @@ -644,7 +647,7 @@ pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result Result +pub fn parse_iflags(matches: &Matches) -> Result { let mut cio = false; let mut direct = false; @@ -726,7 +729,7 @@ pub fn parse_iflags(matches: &getopts::Matches) -> Result } /// Parse OFlags struct from CL-input -pub fn parse_oflags(matches: &getopts::Matches) -> Result +pub fn parse_oflags(matches: &Matches) -> Result { let mut append = false; let mut cio = false; @@ -804,9 +807,9 @@ pub fn parse_oflags(matches: &getopts::Matches) -> Result } /// Parse the amount of the input file to skip. -pub fn parse_skip_amt(ibs: &usize, iflags: &IFlags, matches: &getopts::Matches) -> Result, ParseError> +pub fn parse_skip_amt(ibs: &usize, iflags: &IFlags, matches: &Matches) -> Result, ParseError> { - if let Some(amt) = matches.opt_str("skip") + if let Some(amt) = matches.value_of("skip") { if iflags.skip_bytes { @@ -826,9 +829,9 @@ pub fn parse_skip_amt(ibs: &usize, iflags: &IFlags, matches: &getopts::Matches) } /// Parse the amount of the output file to seek. -pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &getopts::Matches) -> Result, ParseError> +pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &Matches) -> Result, ParseError> { - if let Some(amt) = matches.opt_str("seek") + if let Some(amt) = matches.value_of("seek") { if oflags.seek_bytes { @@ -848,9 +851,9 @@ pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &getopts::Matches) } /// Parse the value of count=N and the type of N implied by iflags -pub fn parse_count(iflags: &IFlags, matches: &getopts::Matches) -> Result, ParseError> +pub fn parse_count(iflags: &IFlags, matches: &Matches) -> Result, ParseError> { - if let Some(amt) = matches.opt_str("count") + if let Some(amt) = matches.value_of("count") { let n = parse_bytes_with_opt_multiplier(amt)?; if iflags.count_bytes @@ -869,9 +872,9 @@ pub fn parse_count(iflags: &IFlags, matches: &getopts::Matches) -> Result Result +pub fn parse_input_non_ascii(matches: &Matches) -> Result { - if let Some(conv_opts) = matches.opt_str("conv") + if let Some(conv_opts) = matches.value_of("conv") { Ok(conv_opts.contains("ascii")) } From 4d76cb45de9a7811ab302619c0e0260441e7dd96 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 2 Jul 2021 11:09:51 -0700 Subject: [PATCH 36/66] Fixes odd build error in file I did not touch. - Adds 'full' features required for imported struct. - see https://docs.rs/syn/1.0.73/syn/struct.ItemFn.html --- src/uucore_procs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uucore_procs/Cargo.toml b/src/uucore_procs/Cargo.toml index 195912ff6..0be04db5c 100644 --- a/src/uucore_procs/Cargo.toml +++ b/src/uucore_procs/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version="1.0" } +syn = { version="1.0", features = ["full"] } [features] default = [] From 951d9c0822974590567d2e6dd3f423d373dd5b04 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 2 Jul 2021 11:31:06 -0700 Subject: [PATCH 37/66] Completes transition to clap - fixes build issues in parser tests. --- src/uu/dd/src/parseargs.rs | 11 +-------- src/uu/dd/src/parseargs/unit_tests.rs | 35 +++++++++++++-------------- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 258a88315..699334416 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -1,16 +1,7 @@ #[cfg(test)] mod unit_tests; -use crate::conversion_tables::*; -use crate::{ - CountType, - IConvFlags, OConvFlags, - StatusLevel, -}; -use crate::{ - IFlags, OFlags, -}; - +use super::*; use std::error::Error; pub type Matches = clap::ArgMatches<'static>; diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 19ff317de..247d61250 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -1,8 +1,7 @@ use super::*; use crate::{ - build_app, - SYNTAX, SUMMARY, LONG_HELP, + build_dd_app, IConvFlags, OConvFlags, StatusLevel, }; @@ -16,7 +15,7 @@ fn test_status_level_absent() String::from("--of=bar.file"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap(); assert_eq!(st, None); @@ -32,7 +31,7 @@ fn test_status_level_none() String::from("--of=bar.file"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap().unwrap(); assert_eq!(st, StatusLevel::None); @@ -48,7 +47,7 @@ fn test_status_level_progress() String::from("--status=progress"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap().unwrap(); assert_eq!(st, StatusLevel::Progress); @@ -64,7 +63,7 @@ fn test_status_level_noxfer() String::from("--of=bar.file"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap().unwrap(); assert_eq!(st, StatusLevel::Noxfer); @@ -81,7 +80,7 @@ fn icf_ctable_error() String::from("--conv=ascii,ebcdic,ibm"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let icf_parsed = parse_conv_flag_input(&matches).unwrap(); } @@ -95,7 +94,7 @@ fn icf_case_error() String::from("--conv=ucase,lcase"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let icf_parsed = parse_conv_flag_input(&matches).unwrap(); } @@ -109,7 +108,7 @@ fn icf_block_error() String::from("--conv=block,unblock"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let icf_parsed = parse_conv_flag_input(&matches).unwrap(); } @@ -123,7 +122,7 @@ fn icf_creat_error() String::from("--conv=excl,nocreat"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let icf_parsed = parse_conv_flag_output(&matches).unwrap(); } @@ -139,7 +138,7 @@ fn parse_icf_token_ibm() String::from("dd"), String::from("--conv=ibm"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let act = parse_flag_list::("conv", &matches).unwrap(); @@ -163,7 +162,7 @@ fn parse_icf_tokens_elu() String::from("dd"), String::from("--conv=ebcdic,lcase,unblock"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); @@ -196,7 +195,7 @@ fn parse_icf_tokens_remaining() String::from("dd"), String::from("--conv=ascii,ucase,block,sparse,swab,sync,noerror,excl,nocreat,notrunc,noerror,fdatasync,fsync"), ]; - let matches = build_app!().parse(args); + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let act = parse_flag_list::("conv", &matches).unwrap(); @@ -215,8 +214,8 @@ macro_rules! test_byte_parser ( #[test] fn $test_name() { - let bs_str = String::from($bs_str); - assert_eq!($bs, parse_bytes_with_opt_multiplier(bs_str).unwrap()) + // let bs_str = String::from($bs_str); + assert_eq!($bs, parse_bytes_with_opt_multiplier($bs_str).unwrap()) } } ); @@ -346,7 +345,7 @@ test_byte_parser!( fn test_KB_multiplier_error() { // KB is not valid (kB, K, and KiB are) - let bs_str = String::from("2000KB"); + let bs_str = "2000KB"; parse_bytes_with_opt_multiplier(bs_str).unwrap(); } @@ -357,7 +356,7 @@ fn test_overflow_panic() { let bs_str = format!("{}KiB", usize::MAX); - parse_bytes_with_opt_multiplier(bs_str).unwrap(); + parse_bytes_with_opt_multiplier(&bs_str).unwrap(); } #[test] @@ -366,5 +365,5 @@ fn test_neg_panic() { let bs_str = format!("{}KiB", -1); - parse_bytes_with_opt_multiplier(bs_str).unwrap(); + parse_bytes_with_opt_multiplier(&bs_str).unwrap(); } From 5b030b48556e43629e80a14d5403a550b5e2d826 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 2 Jul 2021 11:50:33 -0700 Subject: [PATCH 38/66] Minor cleanup - Removes compiler warnings. - Renames conv=nocreat test to be more descriptive. --- Cargo.lock | 1 - src/uu/dd/Cargo.toml | 1 - src/uu/dd/src/dd_unit_tests/mod.rs | 21 ----------- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 40 --------------------- src/uu/dd/src/parseargs/unit_tests.rs | 9 +++-- tests/by-util/test_dd.rs | 11 ++---- 6 files changed, 6 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24686ec03..5a0c5b510 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1918,7 +1918,6 @@ dependencies = [ "debug_print", "gcd", "libc", - "rand 0.8.4", "signal-hook", "tempfile", "uucore", diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 6296b83ce..1a7905293 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -25,7 +25,6 @@ uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] -rand = "0.8" tempfile = "^3" [[bin]] diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index 019943e59..cb514e33d 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -5,7 +5,6 @@ mod conversion_tests; mod block_unblock_tests; mod conv_sync_tests; -use rand::prelude::*; use std::io::prelude::*; use std::io::BufReader; use std::fs; @@ -70,26 +69,6 @@ impl Read for LazyReader } } -struct FickleReader -{ - src: R, -} - -impl Read for FickleReader -{ - fn read(&mut self, mut buf: &mut [u8]) -> io::Result - { - if rand::random() - { - self.src.read(&mut buf) - } - else - { - Ok(0) - } - } -} - #[macro_export] macro_rules! icf ( () => diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index 8920ac04d..f423af212 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -252,46 +252,6 @@ make_io_test!( File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); -make_io_test!( - random_73k_test_fickle_fullblock, - "random-73k-test-fickle-fullblock", - Input { - src: FickleReader { - src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() - }, - non_ascii: false, - ibs: 521, - xfer_stats: None, - count: None, - cflags: icf!(), - iflags: IFlags { - fullblock: true, - 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, - count_bytes: false, - skip_bytes: false, - }, - }, - Output { - dst: DST_PLACEHOLDER, - obs: 1031, - cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, - }, - File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() -); - // Test internal buffer size fn #[test] fn bsize_test_primes() diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 247d61250..9742bcdcb 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -2,7 +2,6 @@ use super::*; use crate::{ build_dd_app, - IConvFlags, OConvFlags, StatusLevel, }; @@ -82,7 +81,7 @@ fn icf_ctable_error() let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); - let icf_parsed = parse_conv_flag_input(&matches).unwrap(); + let _ = parse_conv_flag_input(&matches).unwrap(); } #[test] @@ -96,7 +95,7 @@ fn icf_case_error() let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); - let icf_parsed = parse_conv_flag_input(&matches).unwrap(); + let _ = parse_conv_flag_input(&matches).unwrap(); } #[test] @@ -110,7 +109,7 @@ fn icf_block_error() let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); - let icf_parsed = parse_conv_flag_input(&matches).unwrap(); + let _ = parse_conv_flag_input(&matches).unwrap(); } #[test] @@ -124,7 +123,7 @@ fn icf_creat_error() let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); - let icf_parsed = parse_conv_flag_output(&matches).unwrap(); + let _ = parse_conv_flag_output(&matches).unwrap(); } #[test] diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 84d299aa1..de085bf11 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -92,14 +92,6 @@ fn build_ascii_block(n: usize) -> Vec .collect() } -fn build_ebcdic_block(n: usize) -> Vec -{ - (0..=255) - .cycle() - .take(n) - .collect() -} - // Sanity Tests #[test] fn version() @@ -357,7 +349,7 @@ fn test_noatime_does_not_update_ofile_atime() } #[test] -fn test_nocreat_causes_failure_when_not_present() +fn test_nocreat_causes_failure_when_outfile_not_present() { let fname = "this-file-does-not-exist.txt"; assert_fixture_not_exists!(&fname); @@ -367,6 +359,7 @@ fn test_nocreat_causes_failure_when_not_present() "conv=nocreat", of!(&fname), ]) + .pipe_in("") .run(); assert!(!fix.file_exists(&fname)); From 416488c560e993f931392f1a8378441e5bf5ab98 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 2 Jul 2021 11:57:35 -0700 Subject: [PATCH 39/66] Edits README. - Moves dd from To Do, to Semi-Done --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 083320ac0..379825c16 100644 --- a/README.md +++ b/README.md @@ -366,23 +366,23 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md). | Done | Semi-Done | To Do | |-----------|-----------|--------| | arch | cp | chcon | -| base32 | date | dd | -| base64 | df | runcon | -| basename | expr | stty | -| cat | install | | -| chgrp | join | | -| chmod | ls | | -| chown | more | | -| chroot | numfmt | | -| cksum | od (`--strings` and 128-bit data types missing) | | -| comm | pr | | -| csplit | printf | | -| cut | sort | | -| dircolors | split | | -| dirname | tac | | -| du | tail | | -| echo | test | | -| env | | | +| base32 | date | runcon | +| base64 | dd | stty | +| basename | df | | +| cat | expr | | +| chgrp | install | | +| chmod | join | | +| chown | ls | | +| chroot | more | | +| cksum | numfmt | | +| comm | od (`--strings` and 128-bit data types missing) | +| csplit | pr | | +| cut | printf | | +| dircolors | sort | | +| dirname | split | | +| du | tac | | +| echo | tail | | +| env | test | | | expand | | | | factor | | | | false | | | From 7f03ecf74b7010cc54a308a5989ab5f248a7e0ab Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 2 Jul 2021 13:17:34 -0700 Subject: [PATCH 40/66] Adds failures & tests for unimplmented flags. --- src/uu/dd/src/parseargs.rs | 102 ++++++++++++++++++++++---- src/uu/dd/src/parseargs/unit_tests.rs | 75 +++++++++++++++++++ 2 files changed, 163 insertions(+), 14 deletions(-) diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 699334416..f134886b2 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -7,7 +7,7 @@ use std::error::Error; pub type Matches = clap::ArgMatches<'static>; /// Parser Errors describe errors with parser input -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum ParseError { MultipleFmtTable, @@ -21,6 +21,7 @@ pub enum ParseError MultiplierStringWouldOverflow(String), BlockUnblockWithoutCBS, StatusLevelNotRecognized(String), + Unimplemented(String), } impl std::fmt::Display for ParseError @@ -72,6 +73,10 @@ impl std::fmt::Display for ParseError { write!(f, "status=LEVEL not recognized -> {}", arg) }, + Self::Unimplemented(arg) => + { + write!(f, "feature not implemented on this system -> {}", arg) + }, } } } @@ -193,31 +198,100 @@ impl std::str::FromStr for Flag Ok(Self::SkipBytes), // Either "cio" => - Ok(Self::Cio), + // Ok(Self::Cio), + Err(ParseError::Unimplemented(s.to_string())), "direct" => - Ok(Self::Direct), + // Ok(Self::Direct), + if cfg!(unix) + { + Ok(Self::Direct) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "directory" => - Ok(Self::Directory), + // Ok(Self::Directory), + if cfg!(unix) + { + Ok(Self::Directory) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "dsync" => - Ok(Self::Dsync), + // Ok(Self::Dsync), + if cfg!(unix) + { + Ok(Self::Dsync) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "sync" => - Ok(Self::Sync), + // Ok(Self::Sync), + if cfg!(unix) + { + Ok(Self::Sync) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "nocache" => - Ok(Self::NoCache), + // Ok(Self::NoCache), + Err(ParseError::Unimplemented(s.to_string())), "nonblock" => - Ok(Self::NonBlock), + // Ok(Self::NonBlock), + if cfg!(unix) + { + Ok(Self::NonBlock) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "noatime" => - Ok(Self::NoATime), + // Ok(Self::NoATime), + if cfg!(unix) + { + Ok(Self::NoATime) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "noctty" => - Ok(Self::NoCtty), + // Ok(Self::NoCtty), + if cfg!(unix) + { + Ok(Self::NoCtty) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "nofollow" => - Ok(Self::NoFollow), + // Ok(Self::NoFollow), + if cfg!(unix) + { + Ok(Self::NoFollow) + } + else + { + Err(ParseError::Unimplemented(s.to_string())) + }, "nolinks" => - Ok(Self::NoLinks), + // Ok(Self::NoLinks), + Err(ParseError::Unimplemented(s.to_string())), "binary" => - Ok(Self::Binary), + // Ok(Self::Binary), + Err(ParseError::Unimplemented(s.to_string())), "text" => - Ok(Self::Text), + // Ok(Self::Text), + Err(ParseError::Unimplemented(s.to_string())), // Output only "append" => Ok(Self::Append), diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 9742bcdcb..05d9176d0 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -5,6 +5,81 @@ use crate::{ StatusLevel, }; +#[cfg(not(unix))] +#[test] +fn unimplemented_flags_should_error_non_unix() +{ + let mut unfailed = Vec::new(); + + // The following flags are only implemented in unix + for flag in vec!["direct", "directory", "dsync", "sync", "nonblock", "noatime", "noctty", "nofollow"] + { + let args = vec![ + String::from("dd"), + format!("--iflag={}", flag), + format!("--oflag={}", flag), + ]; + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + + match parse_iflags(&matches) + { + Ok(_) => + unfailed.push(format!("iflag={}", flag)), + Err(_) => + {/* expected behaviour :-) */}, + } + match parse_oflags(&matches) + { + Ok(_) => + unfailed.push(format!("oflag={}", flag)), + Err(_) => + {/* expected behaviour :-) */}, + } + } + + if !unfailed.is_empty() + { + panic!("The following flags did not panic as expected: {:?}", unfailed); + } +} + +#[test] +fn unimplemented_flags_should_error() +{ + let mut unfailed = Vec::new(); + + // The following flags are not implemented + for flag in vec!["cio", "nocache", "nolinks", "text", "binary"] + { + let args = vec![ + String::from("dd"), + format!("--iflag={}", flag), + format!("--oflag={}", flag), + ]; + let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + + match parse_iflags(&matches) + { + Ok(_) => + unfailed.push(format!("iflag={}", flag)), + Err(_) => + {/* expected behaviour :-) */}, + } + match parse_oflags(&matches) + { + Ok(_) => + unfailed.push(format!("oflag={}", flag)), + Err(_) => + {/* expected behaviour :-) */}, + } + } + + if !unfailed.is_empty() + { + panic!("The following flags did not panic as expected: {:?}", unfailed); + } +} + #[test] fn test_status_level_absent() { From 8e862b86dd49354d49921086315dd07509470a62 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 2 Jul 2021 14:24:01 -0700 Subject: [PATCH 41/66] More minor cleanup. - Runs rustfmt. - Speel check help-text --- src/uu/dd/src/conversion_tables.rs | 4 - src/uu/dd/src/datastructures.rs | 63 +- src/uu/dd/src/dd.rs | 1149 +++++++---------- .../src/dd_unit_tests/block_unblock_tests.rs | 204 ++- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 4 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 27 +- src/uu/dd/src/dd_unit_tests/mod.rs | 16 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 50 +- src/uu/dd/src/parseargs.rs | 928 ++++++------- src/uu/dd/src/parseargs/unit_tests.rs | 284 ++-- 10 files changed, 1107 insertions(+), 1622 deletions(-) diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs index 755331eed..a3b9a81c0 100644 --- a/src/uu/dd/src/conversion_tables.rs +++ b/src/uu/dd/src/conversion_tables.rs @@ -39,7 +39,6 @@ pub const ASCII_LCASE_TO_UCASE: ConversionTable = [ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, - ]; pub const ASCII_TO_EBCDIC: ConversionTable = [ @@ -137,7 +136,6 @@ pub const ASCII_TO_IBM_UCASE_TO_LCASE: ConversionTable = [ 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; - pub const ASCII_TO_IBM_LCASE_TO_UCASE: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, @@ -176,7 +174,6 @@ pub const EBCDIC_TO_ASCII: ConversionTable = [ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; - pub const EBCDIC_TO_ASCII_UCASE_TO_LCASE: ConversionTable = [ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, @@ -214,4 +211,3 @@ pub const EBCDIC_TO_ASCII_LCASE_TO_UCASE: ConversionTable = [ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; - diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index cb720aeb7..42026c5b3 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -3,8 +3,7 @@ use crate::conversion_tables::*; use std::error::Error; use std::time; -pub struct ProgUpdate -{ +pub struct ProgUpdate { pub reads_complete: u64, pub reads_partial: u64, pub writes_complete: u64, @@ -14,16 +13,13 @@ pub struct ProgUpdate pub duration: time::Duration, } -pub struct ReadStat -{ +pub struct ReadStat { pub reads_complete: u64, pub reads_partial: u64, pub records_truncated: u32, } -impl std::ops::AddAssign for ReadStat -{ - fn add_assign(&mut self, other: Self) - { +impl std::ops::AddAssign for ReadStat { + fn add_assign(&mut self, other: Self) { *self = Self { reads_complete: self.reads_complete + other.reads_complete, reads_partial: self.reads_partial + other.reads_partial, @@ -32,16 +28,13 @@ impl std::ops::AddAssign for ReadStat } } -pub struct WriteStat -{ +pub struct WriteStat { pub writes_complete: u64, pub writes_partial: u64, pub bytes_total: u128, } -impl std::ops::AddAssign for WriteStat -{ - fn add_assign(&mut self, other: Self) - { +impl std::ops::AddAssign for WriteStat { + fn add_assign(&mut self, other: Self) { *self = Self { writes_complete: self.writes_complete + other.writes_complete, writes_partial: self.writes_partial + other.writes_partial, @@ -53,8 +46,7 @@ impl std::ops::AddAssign for WriteStat type Cbs = usize; /// Stores all Conv Flags that apply to the input -pub struct IConvFlags -{ +pub struct IConvFlags { pub ctable: Option<&'static ConversionTable>, pub block: Option, pub unblock: Option, @@ -65,8 +57,7 @@ pub struct IConvFlags /// Stores all Conv Flags that apply to the output #[derive(Debug, PartialEq)] -pub struct OConvFlags -{ +pub struct OConvFlags { pub sparse: bool, pub excl: bool, pub nocreat: bool, @@ -76,8 +67,7 @@ pub struct OConvFlags } /// Stores all Flags that apply to the input -pub struct IFlags -{ +pub struct IFlags { pub cio: bool, pub direct: bool, pub directory: bool, @@ -97,8 +87,7 @@ pub struct IFlags } /// Stores all Flags that apply to the output -pub struct OFlags -{ +pub struct OFlags { pub append: bool, pub cio: bool, pub direct: bool, @@ -119,8 +108,7 @@ pub struct OFlags /// The value of the status cl-option. /// Controls printing of transfer stats #[derive(Copy, Clone, Debug, PartialEq)] -pub enum StatusLevel -{ +pub enum StatusLevel { Progress, Noxfer, None, @@ -130,38 +118,35 @@ pub enum StatusLevel /// Defaults to Reads(N) /// if iflag=count_bytes /// then becomes Bytes(N) -pub enum CountType -{ +pub enum CountType { Reads(usize), Bytes(usize), } #[derive(Debug)] -pub enum InternalError -{ +pub enum InternalError { WrongInputType, WrongOutputType, InvalidConvBlockUnblockCase, } -impl std::fmt::Display for InternalError -{ +impl std::fmt::Display for InternalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self - { - Self::WrongInputType | - Self::WrongOutputType => - write!(f, "Internal dd error: Wrong Input/Output data type"), - Self::InvalidConvBlockUnblockCase => - write!(f, "Internal dd error: Invalid Conversion, Block, or Unblock data"), + match self { + Self::WrongInputType | Self::WrongOutputType => { + write!(f, "Internal dd error: Wrong Input/Output data type") + } + Self::InvalidConvBlockUnblockCase => write!( + f, + "Internal dd error: Invalid Conversion, Block, or Unblock data" + ), } } } impl Error for InternalError {} -pub mod options -{ +pub mod options { pub const INFILE: &'static str = "if"; pub const OUTFILE: &'static str = "of"; pub const IBS: &'static str = "ibs"; diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index fe43be397..64bedc828 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -23,22 +23,22 @@ use parseargs::Matches; mod conversion_tables; use conversion_tables::*; -use clap::{self, crate_version}; use byte_unit::Byte; +use clap::{self, crate_version}; use debug_print::debug_println; use gcd::Gcd; +#[cfg(unix)] +use libc; use signal_hook::consts::signal; use std::cmp; use std::convert::TryInto; -use std::error::Error; use std::env; -use std::fs::{File, OpenOptions, }; -use std::io::{self, Read, Write, Seek, }; -#[cfg(unix)] -use libc; +use std::error::Error; +use std::fs::{File, OpenOptions}; +use std::io::{self, Read, Seek, Write}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; -use std::sync::{Arc, atomic::AtomicUsize, mpsc, atomic::Ordering, }; +use std::sync::{atomic::AtomicUsize, atomic::Ordering, mpsc, Arc}; use std::thread; use std::time; @@ -48,8 +48,7 @@ const BUF_INIT_BYTE: u8 = 0xDD; const RTN_SUCCESS: i32 = 0; const RTN_FAILURE: i32 = 1; -struct Input -{ +struct Input { src: R, non_ascii: bool, ibs: usize, @@ -59,10 +58,8 @@ struct Input iflags: IFlags, } -impl Input -{ - fn new(matches: &Matches) -> Result> - { +impl Input { + fn new(matches: &Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?; let xfer_stats = parseargs::parse_status_level(matches)?; @@ -81,8 +78,7 @@ impl Input iflags, }; - if let Some(amt) = skip - { + if let Some(amt) = skip { let mut buf = vec![BUF_INIT_BYTE; amt]; i.force_fill(&mut buf, amt)?; @@ -92,57 +88,43 @@ impl Input } } -fn make_unix_iflags(oflags: &IFlags) -> Option -{ +fn make_unix_iflags(oflags: &IFlags) -> Option { let mut flag = 0; - if oflags.direct - { + if oflags.direct { flag |= libc::O_DIRECT; } - if oflags.directory - { + if oflags.directory { flag |= libc::O_DIRECTORY; } - if oflags.dsync - { + if oflags.dsync { flag |= libc::O_DSYNC; } - if oflags.noatime - { + if oflags.noatime { flag |= libc::O_NOATIME; } - if oflags.noctty - { + if oflags.noctty { flag |= libc::O_NOCTTY; } - if oflags.nofollow - { + if oflags.nofollow { flag |= libc::O_NOFOLLOW; } - if oflags.nonblock - { + if oflags.nonblock { flag |= libc::O_NONBLOCK; } - if oflags.sync - { + if oflags.sync { flag |= libc::O_SYNC; } - if flag != 0 - { + if flag != 0 { Some(flag) - } - else - { + } else { None } } -impl Input -{ - fn new(matches: &Matches) -> Result> - { +impl Input { + fn new(matches: &Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?; let xfer_stats = parseargs::parse_status_level(matches)?; @@ -151,17 +133,13 @@ impl Input let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; let count = parseargs::parse_count(&iflags, matches)?; - if let Some(fname) = matches.value_of("if") - { - let mut src = - { + if let Some(fname) = matches.value_of("if") { + let mut src = { let mut opts = OpenOptions::new(); opts.read(true); - if cfg!(unix) - { - if let Some(libc_flags) = make_unix_iflags(&iflags) - { + if cfg!(unix) { + if let Some(libc_flags) = make_unix_iflags(&iflags) { opts.custom_flags(libc_flags); } } @@ -169,8 +147,7 @@ impl Input opts.open(fname)? }; - if let Some(amt) = skip - { + if let Some(amt) = skip { let amt: u64 = amt.try_into()?; src.seek(io::SeekFrom::Start(amt))?; } @@ -186,78 +163,57 @@ impl Input }; Ok(i) - } - else - { + } else { Err(Box::new(InternalError::WrongInputType)) } - } } -impl Read for Input -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result - { +impl Read for Input { + fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut base_idx = 0; let tlen = buf.len(); - loop - { - match self.src.read(&mut buf[base_idx..]) - { - Ok(0) => - return Ok(base_idx), - Ok(rlen) if self.iflags.fullblock => - { + loop { + match self.src.read(&mut buf[base_idx..]) { + Ok(0) => return Ok(base_idx), + Ok(rlen) if self.iflags.fullblock => { base_idx += rlen; - if base_idx >= tlen - { - return Ok(tlen) + if base_idx >= tlen { + return Ok(tlen); } - }, - Ok(len) => - return Ok(len), - Err(e) if e.kind() == io::ErrorKind::Interrupted => - continue, - Err(_) if self.cflags.noerror => - return Ok(base_idx), - Err(e) => - return Err(e), + } + Ok(len) => return Ok(len), + Err(e) if e.kind() == io::ErrorKind::Interrupted => continue, + Err(_) if self.cflags.noerror => return Ok(base_idx), + Err(e) => return Err(e), } } } } -impl Input -{ +impl Input { /// Fills a given buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read follows the previous one. - fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> - { + fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> { let mut reads_complete = 0; let mut reads_partial = 0; let mut base_idx = 0; - while base_idx < buf.len() - { - let next_blk = cmp::min(base_idx+self.ibs, buf.len()); + while base_idx < buf.len() { + let next_blk = cmp::min(base_idx + self.ibs, buf.len()); - match self.read(&mut buf[base_idx..next_blk])? - { - rlen if rlen == self.ibs => - { + match self.read(&mut buf[base_idx..next_blk])? { + rlen if rlen == self.ibs => { base_idx += rlen; reads_complete += 1; - }, - rlen if rlen > 0 => - { + } + rlen if rlen > 0 => { base_idx += rlen; reads_partial += 1; - }, - _ => - break, + } + _ => break, } } @@ -267,36 +223,30 @@ impl Input reads_partial, records_truncated: 0, }) - } + } /// Fills a given buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read is aligned to multiples of ibs; remaing space is filled with the 'pad' byte. - fn fill_blocks(&mut self, buf: &mut Vec, pad: u8) -> Result> - { + fn fill_blocks(&mut self, buf: &mut Vec, pad: u8) -> Result> { let mut reads_complete = 0; let mut reads_partial = 0; let mut base_idx = 0; - while base_idx < buf.len() - { - let next_blk = cmp::min(base_idx+self.ibs, buf.len()); + while base_idx < buf.len() { + let next_blk = cmp::min(base_idx + self.ibs, buf.len()); let plen = next_blk - base_idx; - match self.read(&mut buf[base_idx..next_blk])? - { - 0 => - break, - rlen if rlen < plen => - { + match self.read(&mut buf[base_idx..next_blk])? { + 0 => break, + rlen if rlen < plen => { reads_partial += 1; - let padding = vec![pad; plen-rlen]; - buf.splice(base_idx+rlen..next_blk, padding.into_iter()); - }, - _ => - { + let padding = vec![pad; plen - rlen]; + buf.splice(base_idx + rlen..next_blk, padding.into_iter()); + } + _ => { reads_complete += 1; - }, + } } // TODO: Why does this cause the conv=sync tests to hang? // let rlen = self.read(&mut buf[base_idx..next_blk])?; @@ -324,26 +274,23 @@ impl Input reads_partial, records_truncated: 0, }) - } + } /// Force-fills a buffer, ignoring zero-length reads which would otherwise be /// interpreted as EOF. /// Note: This will not return unless the source (eventually) produces /// enough bytes to meet target_len. - fn force_fill(&mut self, buf: &mut [u8], target_len: usize) -> Result> - { + fn force_fill(&mut self, buf: &mut [u8], target_len: usize) -> Result> { let mut base_idx = 0; - while base_idx < target_len - { + while base_idx < target_len { base_idx += self.read(&mut buf[base_idx..target_len])?; } - Ok(base_idx) + Ok(base_idx) } } -struct Output -{ +struct Output { dst: W, obs: usize, cflags: OConvFlags, @@ -351,8 +298,7 @@ struct Output } impl Output { - fn new(matches: &Matches) -> Result> - { + fn new(matches: &Matches) -> Result> { let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; let oflags = parseargs::parse_oflags(matches)?; @@ -360,86 +306,69 @@ impl Output { let dst = io::stdout(); Ok(Output { - dst, - obs, - cflags, - oflags, + dst, + obs, + cflags, + oflags, }) } - fn fsync(&mut self) -> io::Result<()> - { + fn fsync(&mut self) -> io::Result<()> { self.dst.flush() } - fn fdatasync(&mut self) -> io::Result<()> - { + fn fdatasync(&mut self) -> io::Result<()> { self.dst.flush() } } -fn make_unix_oflags(oflags: &OFlags) -> Option -{ +fn make_unix_oflags(oflags: &OFlags) -> Option { let mut flag = 0; // oflag=FLAG - if oflags.append - { + if oflags.append { flag |= libc::O_APPEND; } - if oflags.direct - { + if oflags.direct { flag |= libc::O_DIRECT; } - if oflags.directory - { + if oflags.directory { flag |= libc::O_DIRECTORY; } - if oflags.dsync - { + if oflags.dsync { flag |= libc::O_DSYNC; } - if oflags.noatime - { + if oflags.noatime { flag |= libc::O_NOATIME; } - if oflags.noctty - { + if oflags.noctty { flag |= libc::O_NOCTTY; } - if oflags.nofollow - { + if oflags.nofollow { flag |= libc::O_NOFOLLOW; } - if oflags.nonblock - { + if oflags.nonblock { flag |= libc::O_NONBLOCK; } - if oflags.sync - { + if oflags.sync { flag |= libc::O_SYNC; } - if flag != 0 - { + if flag != 0 { Some(flag) - } - else - { + } else { None } } impl Output { - fn new(matches: &Matches) -> Result> - { + fn new(matches: &Matches) -> Result> { let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; let oflags = parseargs::parse_oflags(matches)?; let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; - if let Some(fname) = matches.value_of("of") - { + if let Some(fname) = matches.value_of("of") { let mut dst = { let mut opts = OpenOptions::new(); opts.write(true) @@ -449,10 +378,8 @@ impl Output { // 'create_new' overrides 'create' .create_new(cflags.excl && !cflags.nocreat); - if cfg!(unix) - { - if let Some(libc_flags) = make_unix_oflags(&oflags) - { + if cfg!(unix) { + if let Some(libc_flags) = make_unix_oflags(&oflags) { opts.custom_flags(libc_flags); } } @@ -460,8 +387,7 @@ impl Output { opts.open(fname)? }; - if let Some(amt) = seek - { + if let Some(amt) = seek { let amt: u64 = amt.try_into()?; dst.seek(io::SeekFrom::Start(amt))?; } @@ -472,9 +398,7 @@ impl Output { cflags, oflags, }) - } - else - { + } else { // The following error should only occur if someone // mistakenly calls Output::::new() without checking // if 'of' has been provided. In this case, @@ -483,96 +407,76 @@ impl Output { } } - fn fsync(&mut self) -> io::Result<()> - { + fn fsync(&mut self) -> io::Result<()> { self.dst.flush()?; self.dst.sync_all() } - fn fdatasync(&mut self) -> io::Result<()> - { + fn fdatasync(&mut self) -> io::Result<()> { self.dst.flush()?; self.dst.sync_data() } } -impl Seek for Output -{ - fn seek(&mut self, pos: io::SeekFrom) -> io::Result - { +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 - { +impl Write for Output { + fn write(&mut self, buf: &[u8]) -> io::Result { #[inline] - fn is_sparse(buf: &[u8]) -> bool - { - buf.iter() - .all(|&e| e == 0u8) + fn is_sparse(buf: &[u8]) -> bool { + buf.iter().all(|&e| e == 0u8) } // ----------------------------- - if self.cflags.sparse && is_sparse(buf) - { - let seek_amt: i64 = buf.len() - .try_into() - .expect("Internal dd Error: Seek amount greater than signed 64-bit integer"); + if self.cflags.sparse && is_sparse(buf) { + let seek_amt: i64 = buf + .len() + .try_into() + .expect("Internal dd Error: Seek amount greater than signed 64-bit integer"); self.dst.seek(io::SeekFrom::Current(seek_amt))?; Ok(buf.len()) - } - else - { + } else { self.dst.write(buf) } } - fn flush(&mut self) -> io::Result<()> - { + fn flush(&mut self) -> io::Result<()> { self.dst.flush() } } -impl Write for Output -{ - fn write(&mut self, buf: &[u8]) -> io::Result - { +impl Write for Output { + fn write(&mut self, buf: &[u8]) -> io::Result { self.dst.write(buf) } - fn flush(&mut self) -> io::Result<()> - { + fn flush(&mut self) -> io::Result<()> { self.dst.flush() } } -impl Output -{ - fn write_blocks(&mut self, buf: Vec) -> io::Result - { +impl Output { + fn write_blocks(&mut self, buf: Vec) -> io::Result { let mut writes_complete = 0; let mut writes_partial = 0; let mut base_idx = 0; - while base_idx < buf.len() - { - let next_blk = cmp::min(base_idx+self.obs, buf.len()); + while base_idx < buf.len() { + let next_blk = cmp::min(base_idx + self.obs, buf.len()); let plen = next_blk - base_idx; - match self.write(&buf[base_idx..next_blk])? - { - wlen if wlen < plen => - { + match self.write(&buf[base_idx..next_blk])? { + wlen if wlen < plen => { writes_partial += 1; base_idx += wlen; - }, - wlen => - { + } + wlen => { writes_complete += 1; base_idx += wlen; - }, + } } } @@ -581,34 +485,28 @@ impl Output writes_partial, bytes_total: base_idx.try_into().unwrap_or(0u128), }) - } + } } -impl Output -{ - fn write_blocks(&mut self, buf: Vec) -> io::Result - { +impl Output { + fn write_blocks(&mut self, buf: Vec) -> io::Result { let mut writes_complete = 0; let mut writes_partial = 0; let mut base_idx = 0; - while base_idx < buf.len() - { - let next_blk = cmp::min(base_idx+self.obs, buf.len()); + while base_idx < buf.len() { + let next_blk = cmp::min(base_idx + self.obs, buf.len()); let plen = next_blk - base_idx; - match self.write(&buf[base_idx..next_blk])? - { - wlen if wlen < plen => - { + match self.write(&buf[base_idx..next_blk])? { + wlen if wlen < plen => { writes_partial += 1; base_idx += wlen; - }, - wlen => - { + } + wlen => { writes_complete += 1; base_idx += wlen; - }, + } } } @@ -617,31 +515,27 @@ impl Output writes_partial, bytes_total: base_idx.try_into().unwrap_or(0u128), }) - } + } } /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N -fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> -{ - let mut blocks = buf.split(| &e | e == '\n' as u8) - .fold(Vec::new(), | mut blocks, split | - { - let mut split = split.to_vec(); - if split.len() > cbs - { - rstat.records_truncated += 1; - } - split.resize(cbs, ' ' as u8); - blocks.push(split); +fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> { + let mut blocks = buf + .split(|&e| e == '\n' as u8) + .fold(Vec::new(), |mut blocks, split| { + let mut split = split.to_vec(); + if split.len() > cbs { + rstat.records_truncated += 1; + } + split.resize(cbs, ' ' as u8); + blocks.push(split); - blocks - }); + blocks + }); - if let Some(last) = blocks.last() - { - if last.iter().all(| &e | e == ' ' as u8) - { + if let Some(last) = blocks.last() { + if last.iter().all(|&e| e == ' ' as u8) { blocks.pop(); } } @@ -651,19 +545,16 @@ fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> /// Trims padding from each cbs-length partition of buf /// as specified by conv=unblock and cbs=N -fn unblock(buf: Vec, cbs: usize) -> Vec -{ +fn unblock(buf: Vec, cbs: usize) -> Vec { // Local Helper Fns ---------------------------------------------------- #[inline] - fn build_blocks(buf: Vec, cbs: usize) -> Vec> - { + fn build_blocks(buf: Vec, cbs: usize) -> Vec> { let mut blocks = Vec::new(); let mut curr = buf; let mut next; let mut width; - while !curr.is_empty() - { + while !curr.is_empty() { width = cmp::min(cbs, curr.len()); next = curr.split_off(width); @@ -677,20 +568,15 @@ fn unblock(buf: Vec, cbs: usize) -> Vec // --------------------------------------------------------------------- build_blocks(buf, cbs) .into_iter() - .fold(Vec::new(), | mut unblocks, mut block | { - let block = if let Some(last_char_idx) = block.iter().rposition(| &e | e != ' ' as u8) - { - block.truncate(last_char_idx+1); + .fold(Vec::new(), |mut unblocks, mut block| { + let block = if let Some(last_char_idx) = block.iter().rposition(|&e| e != ' ' as u8) { + block.truncate(last_char_idx + 1); block.push('\n' as u8); block - } - else if let Some(32u8/* ' ' as u8 */) = block.get(0) - { + } else if let Some(32u8 /* ' ' as u8 */) = block.get(0) { vec!['\n' as u8] - } - else - { + } else { block }; @@ -703,120 +589,94 @@ fn unblock(buf: Vec, cbs: usize) -> Vec .collect() } -fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, rstat: &mut ReadStat) -> Result, Box> -{ +fn conv_block_unblock_helper( + mut buf: Vec, + i: &mut Input, + rstat: &mut ReadStat, +) -> Result, Box> { // Local Predicate Fns ------------------------------------------------- #[inline] - fn should_block_then_conv(i: &Input) -> bool - { - !i.non_ascii - && i.cflags.block.is_some() + fn should_block_then_conv(i: &Input) -> bool { + !i.non_ascii && i.cflags.block.is_some() } #[inline] - fn should_conv_then_block(i: &Input) -> bool - { - i.non_ascii - && i.cflags.block.is_some() + fn should_conv_then_block(i: &Input) -> bool { + i.non_ascii && i.cflags.block.is_some() } #[inline] - fn should_unblock_then_conv(i: &Input) -> bool - { - !i.non_ascii - && i.cflags.unblock.is_some() + fn should_unblock_then_conv(i: &Input) -> bool { + !i.non_ascii && i.cflags.unblock.is_some() } #[inline] - fn should_conv_then_unblock(i: &Input) -> bool - { - i.non_ascii - && i.cflags.unblock.is_some() + fn should_conv_then_unblock(i: &Input) -> bool { + i.non_ascii && i.cflags.unblock.is_some() } - fn conv_only(i: &Input) -> bool - { - i.cflags.ctable.is_some() - && i.cflags.block.is_none() - && i.cflags.unblock.is_none() + fn conv_only(i: &Input) -> bool { + i.cflags.ctable.is_some() && i.cflags.block.is_none() && i.cflags.unblock.is_none() } // Local Helper Fns ---------------------------------------------------- #[inline] - fn apply_ct(buf: &mut [u8], ct: &ConversionTable) - { - for idx in 0..buf.len() - { + fn apply_ct(buf: &mut [u8], ct: &ConversionTable) { + for idx in 0..buf.len() { buf[idx] = ct[buf[idx] as usize]; } } // -------------------------------------------------------------------- - if conv_only(&i) - { // no block/unblock + if conv_only(&i) { + // no block/unblock let ct = i.cflags.ctable.unwrap(); apply_ct(&mut buf, &ct); Ok(buf) - } - else if should_block_then_conv(&i) - { // ascii input so perform the block first + } else if should_block_then_conv(&i) { + // ascii input so perform the block first let cbs = i.cflags.block.unwrap(); let mut blocks = block(buf, cbs, rstat); - if let Some(ct) = i.cflags.ctable - { - for buf in blocks.iter_mut() - { + if let Some(ct) = i.cflags.ctable { + for buf in blocks.iter_mut() { apply_ct(buf, &ct); } } - let blocks = blocks.into_iter() - .flatten() - .collect(); + let blocks = blocks.into_iter().flatten().collect(); Ok(blocks) - } - else if should_conv_then_block(&i) - { // Non-ascii so perform the conversion first + } else if should_conv_then_block(&i) { + // Non-ascii so perform the conversion first let cbs = i.cflags.block.unwrap(); - if let Some(ct) = i.cflags.ctable - { - apply_ct(&mut buf, &ct); + if let Some(ct) = i.cflags.ctable { + apply_ct(&mut buf, &ct); } - let blocks = block(buf, cbs, rstat) - .into_iter() - .flatten() - .collect(); + let blocks = block(buf, cbs, rstat).into_iter().flatten().collect(); Ok(blocks) - } - else if should_unblock_then_conv(&i) - { // ascii input so perform the unblock first + } else if should_unblock_then_conv(&i) { + // ascii input so perform the unblock first let cbs = i.cflags.unblock.unwrap(); let mut buf = unblock(buf, cbs); - if let Some(ct) = i.cflags.ctable - { - apply_ct(&mut buf, &ct); + if let Some(ct) = i.cflags.ctable { + apply_ct(&mut buf, &ct); } Ok(buf) - } - else if should_conv_then_unblock(&i) - { // Non-ascii input so perform the conversion first + } else if should_conv_then_unblock(&i) { + // Non-ascii input so perform the conversion first let cbs = i.cflags.unblock.unwrap(); - if let Some(ct) = i.cflags.ctable - { - apply_ct(&mut buf, &ct); + if let Some(ct) = i.cflags.ctable { + apply_ct(&mut buf, &ct); } let buf = unblock(buf, cbs); Ok(buf) - } - else - { + } else { // The following error should not happen, as it results from // insufficient command line data. This case should be caught // by the parser before making it this far. @@ -826,80 +686,72 @@ fn conv_block_unblock_helper(mut buf: Vec, i: &mut Input, rstat: } } -fn read_helper(i: &mut Input, bsize: usize) -> Result<(ReadStat, Vec), Box> -{ +fn read_helper( + i: &mut Input, + bsize: usize, +) -> Result<(ReadStat, Vec), Box> { // Local Predicate Fns ----------------------------------------------- #[inline] - fn is_conv(i: &Input) -> bool - { + fn is_conv(i: &Input) -> bool { i.cflags.ctable.is_some() } #[inline] - fn is_block(i: &Input) -> bool - { + fn is_block(i: &Input) -> bool { i.cflags.block.is_some() } #[inline] - fn is_unblock(i: &Input) -> bool - { + fn is_unblock(i: &Input) -> bool { i.cflags.unblock.is_some() } // Local Helper Fns ------------------------------------------------- #[inline] - fn perform_swab(buf: &mut [u8]) - { + fn perform_swab(buf: &mut [u8]) { let mut tmp; - for base in (1..buf.len()).step_by(2) - { + for base in (1..buf.len()).step_by(2) { tmp = buf[base]; - buf[base] = buf[base-1]; - buf[base-1] = tmp; + buf[base] = buf[base - 1]; + buf[base - 1] = tmp; } } - // ------------------------------------------------------------------ - // Read - let mut buf = vec![BUF_INIT_BYTE; bsize]; - let mut rstat = match i.cflags.sync - { - Some(ch) => - i.fill_blocks(&mut buf, ch)?, - _ => - i.fill_consecutive(&mut buf)?, - }; - // Return early if no data - if rstat.reads_complete == 0 && rstat.reads_partial == 0 - { - return Ok((rstat,buf)); - } + // ------------------------------------------------------------------ + // Read + let mut buf = vec![BUF_INIT_BYTE; bsize]; + let mut rstat = match i.cflags.sync { + Some(ch) => i.fill_blocks(&mut buf, ch)?, + _ => i.fill_consecutive(&mut buf)?, + }; + // Return early if no data + if rstat.reads_complete == 0 && rstat.reads_partial == 0 { + return Ok((rstat, buf)); + } - // Perform any conv=x[,x...] options - if i.cflags.swab - { - perform_swab(&mut buf); - } - if is_conv(&i) || is_block(&i) || is_unblock(&i) - { - let buf = conv_block_unblock_helper(buf, i, &mut rstat)?; - Ok((rstat, buf)) - } - else - { - Ok((rstat, buf)) - } + // Perform any conv=x[,x...] options + if i.cflags.swab { + perform_swab(&mut buf); + } + if is_conv(&i) || is_block(&i) || is_unblock(&i) { + let buf = conv_block_unblock_helper(buf, i, &mut rstat)?; + Ok((rstat, buf)) + } else { + Ok((rstat, buf)) + } } -fn print_io_lines(update: &ProgUpdate) -{ - eprintln!("{}+{} records in", update.reads_complete, update.reads_partial); - if update.records_truncated > 0 - { +fn print_io_lines(update: &ProgUpdate) { + eprintln!( + "{}+{} records in", + update.reads_complete, update.reads_partial + ); + if update.records_truncated > 0 { eprintln!("{} truncated records", update.records_truncated); } - eprintln!("{}+{} records out", update.writes_complete, update.writes_partial); + eprintln!( + "{}+{} records out", + update.writes_complete, update.writes_partial + ); } -fn make_prog_line(update: &ProgUpdate) -> String -{ +fn make_prog_line(update: &ProgUpdate) -> String { let btotal_metric = Byte::from_bytes(update.bytes_total) .get_appropriate_unit(false) .format(0); @@ -911,35 +763,34 @@ fn make_prog_line(update: &ProgUpdate) -> String .get_appropriate_unit(false) .format(1); - format!("{} bytes ({}, {}) copied, {:.1} s, {}/s", - update.bytes_total, - btotal_metric, - btotal_bin, - update.duration.as_secs_f64(), - xfer_rate - ).to_string() + format!( + "{} bytes ({}, {}) copied, {:.1} s, {}/s", + update.bytes_total, + btotal_metric, + btotal_bin, + update.duration.as_secs_f64(), + xfer_rate + ) + .to_string() } -fn reprint_prog_line(update: &ProgUpdate) -{ +fn reprint_prog_line(update: &ProgUpdate) { eprint!("\r{}", make_prog_line(update)); } -fn print_prog_line(update: &ProgUpdate) -{ +fn print_prog_line(update: &ProgUpdate) { eprintln!("{}", make_prog_line(update)); } -fn print_xfer_stats(update: &ProgUpdate) -{ +fn print_xfer_stats(update: &ProgUpdate) { print_io_lines(update); print_prog_line(update); - } /// Generate a progress updater that tracks progress, receives updates, and responds to signals. -fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option) -> impl Fn() -> () -{ +fn gen_prog_updater( + rx: mpsc::Receiver, + xfer_stats: Option, +) -> impl Fn() -> () { // -------------------------------------------------------------- - fn posixly_correct() -> bool - { + fn posixly_correct() -> bool { env::var("POSIXLY_CORRECT").is_ok() } // -------------------------------------------------------------- @@ -956,41 +807,38 @@ fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option - { + let update = match (rx.recv(), xfer_stats) { + (Ok(update), Some(StatusLevel::Progress)) => { reprint_prog_line(&update); update - }, - (Ok(update), _) => - { - update - }, + } + (Ok(update), _) => update, (Err(_), _) => - // recv only fails permenantly - break, + // recv only fails permenantly + { + break + } }; // Handle signals - match sigval.load(Ordering::Relaxed) - { - SIGUSR1_USIZE => - { + match sigval.load(Ordering::Relaxed) { + SIGUSR1_USIZE => { print_xfer_stats(&update); - }, - _ => {/* no signals recv'd */}, + } + _ => { /* no signals recv'd */ } }; } } @@ -1002,66 +850,60 @@ fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option usize -{ +fn calc_bsize(ibs: usize, obs: usize) -> usize { let gcd = Gcd::gcd(ibs, obs); - let lcm = (ibs/gcd)*obs; + let lcm = (ibs / gcd) * obs; lcm } /// Calculate the buffer size appropriate for this loop iteration, respecting /// a count=N if present. -fn calc_loop_bsize(count: &Option, rstat: &ReadStat, wstat: &WriteStat, ibs: usize, ideal_bsize: usize) -> usize -{ - match count - { - Some(CountType::Reads(rmax)) => - { +fn calc_loop_bsize( + count: &Option, + rstat: &ReadStat, + wstat: &WriteStat, + ibs: usize, + ideal_bsize: usize, +) -> usize { + match count { + Some(CountType::Reads(rmax)) => { let rmax: u64 = (*rmax).try_into().unwrap(); let rsofar = rstat.reads_complete + rstat.reads_partial; let rremain: usize = (rmax - rsofar).try_into().unwrap(); - cmp::min(ideal_bsize, rremain*ibs) - }, - Some(CountType::Bytes(bmax)) => - { + cmp::min(ideal_bsize, rremain * ibs) + } + Some(CountType::Bytes(bmax)) => { let bmax: u128 = (*bmax).try_into().unwrap(); let bremain: usize = (bmax - wstat.bytes_total).try_into().unwrap(); cmp::min(ideal_bsize, bremain) - }, - None => - ideal_bsize, + } + None => ideal_bsize, } } /// Decide if the current progress is below a count=N limit or return /// true if no such limit is set. -fn below_count_limit(count: &Option, rstat: &ReadStat, wstat: &WriteStat) -> bool -{ - match count - { - Some(CountType::Reads(n)) => - { +fn below_count_limit(count: &Option, rstat: &ReadStat, wstat: &WriteStat) -> bool { + match count { + Some(CountType::Reads(n)) => { let n = (*n).try_into().unwrap(); // debug_assert!(rstat.reads_complete + rstat.reads_partial >= n); rstat.reads_complete + rstat.reads_partial <= n - }, - Some(CountType::Bytes(n)) => - { + } + Some(CountType::Bytes(n)) => { let n = (*n).try_into().unwrap(); // debug_assert!(wstat.bytes_total >= n); wstat.bytes_total <= n - }, - None => - true, + } + None => true, } } /// Perform the copy/convert opertaions. Stdout 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<(), Box> -{ +fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), Box> { let mut rstat = ReadStat { reads_complete: 0, reads_partial: 0, @@ -1081,100 +923,24 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), tx }; - while below_count_limit(&i.count, &rstat, &wstat) - { + while below_count_limit(&i.count, &rstat, &wstat) { // Read/Write let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); - match read_helper(&mut i, loop_bsize)? - { - (ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) => - break, - (rstat_update, buf) => - { + match read_helper(&mut i, loop_bsize)? { + ( + ReadStat { + reads_complete: 0, + reads_partial: 0, + .. + }, + _, + ) => break, + (rstat_update, buf) => { let wstat_update = o.write_blocks(buf)?; rstat += rstat_update; wstat += wstat_update; - }, - }; - // Update Prog - prog_tx.send(ProgUpdate { - reads_complete: rstat.reads_complete, - reads_partial: rstat.reads_partial, - writes_complete: wstat.writes_complete, - writes_partial: wstat.writes_partial, - bytes_total: wstat.bytes_total, - records_truncated: rstat.records_truncated, - duration: start.elapsed(), - })?; - } - - if o.cflags.fsync - { - o.fsync()?; - } - else if o.cflags.fdatasync - { - o.fdatasync()?; - } - - match i.xfer_stats - { - Some(StatusLevel::Noxfer) | - Some(StatusLevel::None) => {}, - _ => - print_xfer_stats(&ProgUpdate { - reads_complete: rstat.reads_complete, - reads_partial: rstat.reads_partial, - writes_complete: wstat.writes_complete, - writes_partial: wstat.writes_partial, - bytes_total: wstat.bytes_total, - records_truncated: rstat.records_truncated, - duration: start.elapsed(), - }), - } - Ok(()) -} - -/// 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<(), Box> -{ - let mut rstat = ReadStat { - reads_complete: 0, - reads_partial: 0, - records_truncated: 0, - }; - let mut wstat = WriteStat { - writes_complete: 0, - writes_partial: 0, - bytes_total: 0, - }; - let start = time::Instant::now(); - let bsize = calc_bsize(i.ibs, o.obs); - - let prog_tx = { - let (tx, rx) = mpsc::channel(); - thread::spawn(gen_prog_updater(rx, i.xfer_stats)); - tx - }; - - while below_count_limit(&i.count, &rstat, &wstat) - { - // Read/Write - let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); - match read_helper(&mut i, loop_bsize)? - { - (ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) => - break, - (rstat_update, buf) => - { - let wstat_update = o.write_blocks(buf)?; - - rstat += rstat_update; - wstat += wstat_update; - }, + } }; // Update Prog prog_tx.send(ProgUpdate { @@ -1188,36 +954,103 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box {}, - _ => - print_xfer_stats(&ProgUpdate { - reads_complete: rstat.reads_complete, - reads_partial: rstat.reads_partial, - writes_complete: wstat.writes_complete, - writes_partial: wstat.writes_partial, - bytes_total: wstat.bytes_total, - records_truncated: rstat.records_truncated, - duration: start.elapsed(), - }), + match i.xfer_stats { + Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {} + _ => print_xfer_stats(&ProgUpdate { + reads_complete: rstat.reads_complete, + reads_partial: rstat.reads_partial, + writes_complete: wstat.writes_complete, + writes_partial: wstat.writes_partial, + bytes_total: wstat.bytes_total, + records_truncated: rstat.records_truncated, + duration: start.elapsed(), + }), } Ok(()) } +/// 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<(), Box> { + let mut rstat = ReadStat { + reads_complete: 0, + reads_partial: 0, + records_truncated: 0, + }; + let mut wstat = WriteStat { + writes_complete: 0, + writes_partial: 0, + bytes_total: 0, + }; + let start = time::Instant::now(); + let bsize = calc_bsize(i.ibs, o.obs); -fn append_dashes_if_not_present(mut acc: Vec, s: &String) -> Vec -{ + let prog_tx = { + let (tx, rx) = mpsc::channel(); + thread::spawn(gen_prog_updater(rx, i.xfer_stats)); + tx + }; + + while below_count_limit(&i.count, &rstat, &wstat) { + // Read/Write + let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); + match read_helper(&mut i, loop_bsize)? { + ( + ReadStat { + reads_complete: 0, + reads_partial: 0, + .. + }, + _, + ) => break, + (rstat_update, buf) => { + let wstat_update = o.write_blocks(buf)?; + + rstat += rstat_update; + wstat += wstat_update; + } + }; + // Update Prog + prog_tx.send(ProgUpdate { + reads_complete: rstat.reads_complete, + reads_partial: rstat.reads_partial, + writes_complete: wstat.writes_complete, + writes_partial: wstat.writes_partial, + bytes_total: wstat.bytes_total, + records_truncated: rstat.records_truncated, + duration: start.elapsed(), + })?; + } + + if o.cflags.fsync { + o.fsync()?; + } else if o.cflags.fdatasync { + o.fdatasync()?; + } + + match i.xfer_stats { + Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {} + _ => print_xfer_stats(&ProgUpdate { + reads_complete: rstat.reads_complete, + reads_partial: rstat.reads_partial, + writes_complete: wstat.writes_complete, + writes_partial: wstat.writes_partial, + bytes_total: wstat.bytes_total, + records_truncated: rstat.records_truncated, + duration: start.elapsed(), + }), + } + Ok(()) +} + +fn append_dashes_if_not_present(mut acc: Vec, s: &String) -> Vec { if Some("--") == s.get(0..=1) { acc } else { @@ -1247,12 +1080,12 @@ macro_rules! unpack_or_rtn ( }}; ); -pub fn uumain(args: impl uucore::Args) -> i32 -{ - let dashed_args = args.collect_str(InvalidEncodingHandling::Ignore) - .accept_any() - .iter() - .fold(Vec::new(), append_dashes_if_not_present); +pub fn uumain(args: impl uucore::Args) -> i32 { + let dashed_args = args + .collect_str(InvalidEncodingHandling::Ignore) + .accept_any() + .iter() + .fold(Vec::new(), append_dashes_if_not_present); let matches = build_dd_app!() // TODO: usage, after_help @@ -1260,49 +1093,51 @@ pub fn uumain(args: impl uucore::Args) -> i32 //.after_help(...) .get_matches_from(dashed_args); - let result = match (matches.is_present(options::INFILE), matches.is_present(options::OUTFILE)) - { - (true, true) => - { - let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); + let result = match ( + matches.is_present(options::INFILE), + matches.is_present(options::OUTFILE), + ) { + (true, true) => { + let (i, o) = + unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); - dd_fileout(i,o) - }, - (false, true) => - { - let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); + dd_fileout(i, o) + } + (false, true) => { + let (i, o) = unpack_or_rtn!( + Input::::new(&matches), + Output::::new(&matches) + ); - dd_fileout(i,o) - }, - (true, false) => - { - let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); + dd_fileout(i, o) + } + (true, false) => { + let (i, o) = unpack_or_rtn!( + Input::::new(&matches), + Output::::new(&matches) + ); - dd_stdout(i,o) - }, - (false, false) => - { - let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); + dd_stdout(i, o) + } + (false, false) => { + let (i, o) = unpack_or_rtn!( + Input::::new(&matches), + Output::::new(&matches) + ); - dd_stdout(i,o) - }, + dd_stdout(i, o) + } }; - match result - { - Ok(_) => - { - RTN_SUCCESS - }, - Err(e) => - { + match result { + Ok(_) => RTN_SUCCESS, + Err(e) => { debug_println!("dd exiting with error:\n\t{}", e); RTN_FAILURE - }, + } } } -pub fn uu_app() -> clap::App<'static, 'static> -{ +pub fn uu_app() -> clap::App<'static, 'static> { build_dd_app!() } @@ -1341,7 +1176,7 @@ macro_rules! build_dd_app ( clap::Arg::with_name(options::BS) .long(options::BS) .takes_value(true) - .help("bs=N (alternatively --bs N) specifies ibs=N and obs=N (default: 512). If ibs or obs are also specified, bs=N takes presedence. Multiplier strings permitted.") + .help("bs=N (alternatively --bs N) specifies ibs=N and obs=N (default: 512). If ibs or obs are also specified, bs=N takes precedence. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::CBS) @@ -1353,25 +1188,25 @@ macro_rules! build_dd_app ( clap::Arg::with_name(options::SKIP) .long(options::SKIP) .takes_value(true) - .help("skip=N (alternatively --skip N) causes N ibs-sized records of input to be skipped before beginning copy/convert operations. See iflag=count_bytes if skipping N bytes is prefered. Multiplier strings permitted.") + .help("skip=N (alternatively --skip N) causes N ibs-sized records of input to be skipped before beginning copy/convert operations. See iflag=count_bytes if skipping N bytes is preferred. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::SEEK) .long(options::SEEK) .takes_value(true) - .help("seek=N (alternatively --seek N) seeks N obs-sized records into output before beginning copy/convert operations. See oflag=seek_bytes if seeking N bytes is prefered. Multiplier strings permitted.") + .help("seek=N (alternatively --seek N) seeks N obs-sized records into output before beginning copy/convert operations. See oflag=seek_bytes if seeking N bytes is preferred. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::COUNT) .long(options::COUNT) .takes_value(true) - .help("count=N (alternatively --count N) stop reading input after N ibs-sized read operations rather than proceeding until EOF. See iflag=count_bytes if stopping after N bytes is prefered. Multiplier strings permitted.") + .help("count=N (alternatively --count N) stop reading input after N ibs-sized read operations rather than proceeding until EOF. See iflag=count_bytes if stopping after N bytes is preferred. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::STATUS) .long(options::STATUS) .takes_value(true) - .help("status=LEVEL (alternatively --status LEVEL) controls whether volume and performace stats are written to stderr. + .help("status=LEVEL (alternatively --status LEVEL) controls whether volume and performance stats are written to stderr. When unspecified, dd will print stats upon completion. An example is below. \t6+0 records in @@ -1380,12 +1215,13 @@ When unspecified, dd will print stats upon completion. An example is below. The first two lines are the 'volume' stats and the final line is the 'performance' stats. The volume stats indicate the number of complete and partial ibs-sized reads, or obs-sized writes that took place during the copy. The format of the volume stats is +. If records have been truncated (see conv=block), the volume stats will contain the number of truncated records. -Permissable LEVEL values are: -\t- progress: Print periodic performance stats as the copy proceedes. -\t- noxfer: Print final volume stats, but not performance stats. -\t- none: Do not print any stats. +Permissible LEVEL values are: +\t progress: Print periodic performance stats as the copy proceeds. +\t noxfer: Print final volume stats, but not performance stats. +\t none: Do not print any stats. + +Printing performance stats is also triggered by the INFO signal (where supported), or the USR1 signal. Setting the POSIXLY_CORRECT environment variable to any value (including an empty value) will cause the USR1 signal to be ignored. -Printing performance stats is also triggered by the INFO signal (where supported), or the USR1 signal. Setting the POSIXLY_CORRECT evnironment variable to any value (including an empty value) will cause the USR1 signal to be ignored. ") ) .arg( @@ -1395,31 +1231,32 @@ Printing performance stats is also triggered by the INFO signal (where supported .help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file-flags. Conversion options and file flags may be intermixed. Conversion options: -\t- One of {ascii, ebcdic, ibm} will perform an encoding conversion. -\t\t- 'ascii' converts from EBCDIC to ASCII. This is the inverse of the 'ebcdic' option. -\t\t- 'ebcdic' converts from ASCII to EBCDIC. This is the inverse of the 'ascii' option. -\t\t- 'ibm' converts from ASCII to EBCDIC, appling the conventions for '[', ']' and '~' specified in POSIX. +\t One of {ascii, ebcdic, ibm} will perform an encoding conversion. +\t\t 'ascii' converts from EBCDIC to ASCII. This is the inverse of the 'ebcdic' option. +\t\t 'ebcdic' converts from ASCII to EBCDIC. This is the inverse of the 'ascii' option. +\t\t 'ibm' converts from ASCII to EBCDIC, applying the conventions for '[', ']' and '~' specified in POSIX. -\t- One of {ucase, lcase} will perform a case conversion. Works in conjuction with option {ascii, ebcdic, ibm} to infer input encoding. If no other conversion option is specified, input is assumed to be ascii. -\t\t- 'ucase' converts from lower-case to upper-case -\t\t- 'lcase' converts from upper-case to lower-case. +\t One of {ucase, lcase} will perform a case conversion. Works in conjunction with option {ascii, ebcdic, ibm} to infer input encoding. If no other conversion option is specified, input is assumed to be ascii. +\t\t 'ucase' converts from lower-case to upper-case +\t\t 'lcase' converts from upper-case to lower-case. -\t- One of {block, unblock}. Convert between lines terminated by newline characters, and fixed-width lines padded by spaces (without any newlines). Both the 'block' and 'unblock' options require cbs=BYTES be specified. -\t\t- 'block' for each newline less than the size indicated by cbs=BYTES, remove the newline and pad with spaces up to cbs. Lines longer than cbs are truncated. -\t\t- 'unblock' for each block of input of the size indicated by cbs=BYTES, remove right-trailing spaces and replace with a newline character. +\t One of {block, unblock}. Convert between lines terminated by newline characters, and fixed-width lines padded by spaces (without any newlines). Both the 'block' and 'unblock' options require cbs=BYTES be specified. +\t\t 'block' for each newline less than the size indicated by cbs=BYTES, remove the newline and pad with spaces up to cbs. Lines longer than cbs are truncated. +\t\t 'unblock' for each block of input of the size indicated by cbs=BYTES, remove right-trailing spaces and replace with a newline character. \t 'sparse' attempts to seek the output when an obs-sized block consists of only zeros. \t 'swab' swaps each adjacent pair of bytes. If an odd number of bytes is present, the final byte is omitted. \t 'sync' pad each ibs-sided block with zeros. If 'block' or 'unblock' is specified, pad with spaces instead. Flags: -\t- One of {excl, nocreat} -\t\t- 'excl' the output file must be created. Fail if the output file is already present. -\t\t- 'nocreat' the output file will not be created. Fail if the output file in not already present. -\t- 'notrunc' the output file will not be truncated. If this option is not present, output will be truncated when opened. -\t- 'noerror' all read errors will be ignored. If this option is not present, dd will only ignore Error::Interrupted. -\t- 'fdatasync' data will be written before finishing. -\t- 'fsync' data and metadata will be written before finishing. +\t One of {excl, nocreat} +\t\t 'excl' the output file must be created. Fail if the output file is already present. +\t\t 'nocreat' the output file will not be created. Fail if the output file in not already present. +\t 'notrunc' the output file will not be truncated. If this option is not present, output will be truncated when opened. +\t 'noerror' all read errors will be ignored. If this option is not present, dd will only ignore Error::Interrupted. +\t 'fdatasync' data will be written before finishing. +\t 'fsync' data and metadata will be written before finishing. + ") ) .arg( @@ -1429,24 +1266,25 @@ Flags: .help("iflag=FLAG[,FLAG] (alternatively --iflag FLAG[,FLAG]) a comma separated list of input flags which specify how the input source is treated. FLAG may be any of the input-flags or general-flags specified below. Input-Flags -\t- 'count_bytes' a value to count=N will be interpreted as bytes. -\t- 'skip_bytes' a value to skip=N will be interpreted as bytes. -\t- 'fullblock' wait for ibs bytes from each read. zero-length reads are still considered EOF. +\t 'count_bytes' a value to count=N will be interpreted as bytes. +\t 'skip_bytes' a value to skip=N will be interpreted as bytes. +\t 'fullblock' wait for ibs bytes from each read. zero-length reads are still considered EOF. General-Flags -\t- 'direct' -\t- 'directory' -\t- 'dsync' -\t- 'sync' -\t- 'nonblock' -\t- 'noatime' -\t- 'nocache' -\t- 'noctty' -\t- 'nofollow' +\t 'direct' use direct I/O for data. +\t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. +\t 'dsync' use syncronized I/O for data. +\t 'sync' use syncronized I/O for data and metadata. +\t 'nonblock' use non-blocking I/O. +\t 'noatime' do not update access time. +\t 'nocache' request that OS drop cache. +\t 'noctty' do not assign a controlling tty. +\t 'nofollow' do not follow system links. Output-Flags -\t- 'append' open file in append mode. Consider setting conv=notrunc as well. -\t- 'seek_bytes' a value to seek=N will be interpreted as bytes. +\t 'append' open file in append mode. Consider setting conv=notrunc as well. +\t 'seek_bytes' a value to seek=N will be interpreted as bytes. + ") ) .arg( @@ -1456,19 +1294,20 @@ Output-Flags .help("oflag=FLAG[,FLAG] (alternatively --oflag FLAG[,FLAG]) a comma separated list of output flags which specify how the output source is treated. FLAG may be any of the output-flags or general-flags specified below. Output-Flags -\t- 'append' open file in append mode. Consider setting conv=notrunc as well. -\t- 'seek_bytes' a value to seek=N will be interpreted as bytes. +\t 'append' open file in append mode. Consider setting conv=notrunc as well. +\t 'seek_bytes' a value to seek=N will be interpreted as bytes. General-Flags -\t- 'direct' -\t- 'directory' -\t- 'dsync' -\t- 'sync' -\t- 'nonblock' -\t- 'noatime' -\t- 'nocache' -\t- 'noctty' -\t- 'nofollow' +\t 'direct' use direct I/O for data. +\t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. +\t 'dsync' use syncronized I/O for data. +\t 'sync' use syncronized I/O for data and metadata. +\t 'nonblock' use non-blocking I/O. +\t 'noatime' do not update access time. +\t 'nocache' request that OS drop cache. +\t 'noctty' do not assign a controlling tty. +\t 'nofollow' do not follow system links. + ") ) }; diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index eba49f9d0..f52d68fd0 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -81,176 +81,176 @@ macro_rules! make_unblock_test ( ); #[test] -fn block_test_no_nl() -{ +fn block_test_no_nl() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - ]); + assert_eq!(res, vec![vec![0u8, 1u8, 2u8, 3u8],]); } #[test] -fn block_test_no_nl_short_record() -{ +fn block_test_no_nl_short_record() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8]; let res = block(buf, 8, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - ]); + assert_eq!( + res, + vec![vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE],] + ); } #[test] -fn block_test_no_nl_trunc() -{ +fn block_test_no_nl_trunc() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8/*, 4u8*/], - ]); + assert_eq!(res, vec![vec![0u8, 1u8, 2u8, 3u8 /*, 4u8*/],]); assert_eq!(rs.records_truncated, 1); } #[test] -fn block_test_nl_gt_cbs_trunc() -{ +fn block_test_nl_gt_cbs_trunc() { let mut rs = rs!(); - let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8]; + let buf = vec![ + 0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8, + ]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - // vec![4u8, SPACE, SPACE, SPACE], - vec![0u8, 1u8, 2u8, 3u8], - // vec![4u8, SPACE, SPACE, SPACE], - vec![5u8, 6u8, 7u8, 8u8], - ]); + assert_eq!( + res, + vec![ + vec![0u8, 1u8, 2u8, 3u8], + // vec![4u8, SPACE, SPACE, SPACE], + vec![0u8, 1u8, 2u8, 3u8], + // vec![4u8, SPACE, SPACE, SPACE], + vec![5u8, 6u8, 7u8, 8u8], + ] + ); assert_eq!(rs.records_truncated, 2); } #[test] -fn block_test_surrounded_nl() -{ +fn block_test_surrounded_nl() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; let res = block(buf, 8, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8, 8u8, SPACE, SPACE, SPACE], - ]); + assert_eq!( + res, + vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, 8u8, SPACE, SPACE, SPACE], + ] + ); } #[test] -fn block_test_multiple_nl_same_cbs_block() -{ +fn block_test_multiple_nl_same_cbs_block() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8]; let res = block(buf, 8, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![4u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - vec![5u8, 6u8, 7u8, 8u8, 9u8, SPACE, SPACE, SPACE], - ]); + assert_eq!( + res, + vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![5u8, 6u8, 7u8, 8u8, 9u8, SPACE, SPACE, SPACE], + ] + ); } #[test] -fn block_test_multiple_nl_diff_cbs_block() -{ +fn block_test_multiple_nl_diff_cbs_block() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8]; let res = block(buf, 8, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], - vec![8u8, 9u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - ]); + assert_eq!( + res, + vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], + vec![8u8, 9u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + ] + ); } #[test] -fn block_test_end_nl_diff_cbs_block() -{ +fn block_test_end_nl_diff_cbs_block() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - ]); + assert_eq!(res, vec![vec![0u8, 1u8, 2u8, 3u8],]); } #[test] -fn block_test_end_nl_same_cbs_block() -{ +fn block_test_end_nl_same_cbs_block() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, NL]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, SPACE] - ]); + assert_eq!(res, vec![vec![0u8, 1u8, 2u8, SPACE]]); } #[test] -fn block_test_double_end_nl() -{ +fn block_test_double_end_nl() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, NL, NL]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, SPACE], - vec![SPACE, SPACE, SPACE, SPACE], - ]); + assert_eq!( + res, + vec![vec![0u8, 1u8, 2u8, SPACE], vec![SPACE, SPACE, SPACE, SPACE],] + ); } #[test] -fn block_test_start_nl() -{ +fn block_test_start_nl() { let mut rs = rs!(); let buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![SPACE, SPACE, SPACE, SPACE], - vec![0u8, 1u8, 2u8, 3u8], - ]); + assert_eq!( + res, + vec![vec![SPACE, SPACE, SPACE, SPACE], vec![0u8, 1u8, 2u8, 3u8],] + ); } #[test] -fn block_test_double_surrounded_nl_no_trunc() -{ +fn block_test_double_surrounded_nl_no_trunc() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; let res = block(buf, 8, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], - ]); + assert_eq!( + res, + vec![ + vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], + vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8, SPACE, SPACE, SPACE, SPACE], + ] + ); } #[test] -fn block_test_double_surrounded_nl_double_trunc() -{ +fn block_test_double_surrounded_nl_double_trunc() { let mut rs = rs!(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; let res = block(buf, 4, &mut rs); - assert_eq!(res, vec![ - vec![0u8, 1u8, 2u8, 3u8], - vec![SPACE, SPACE, SPACE, SPACE], - vec![4u8, 5u8, 6u8, 7u8/*, 8u8*/], - ]); + assert_eq!( + res, + vec![ + vec![0u8, 1u8, 2u8, 3u8], + vec![SPACE, SPACE, SPACE, SPACE], + vec![4u8, 5u8, 6u8, 7u8 /*, 8u8*/], + ] + ); assert_eq!(rs.records_truncated, 1); } @@ -279,58 +279,51 @@ make_block_test!( ); #[test] -fn unblock_test_full_cbs() -{ +fn unblock_test_full_cbs() { let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8]; let res = unblock(buf, 8); - assert_eq!(res, - vec![0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, NL], - ); + assert_eq!(res, vec![0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, NL],); } #[test] -fn unblock_test_all_space() -{ +fn unblock_test_all_space() { let buf = vec![SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE]; let res = unblock(buf, 8); - assert_eq!(res, - vec![NL], - ); + assert_eq!(res, vec![NL],); } #[test] -fn unblock_test_decoy_spaces() -{ +fn unblock_test_decoy_spaces() { let buf = vec![0u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 7u8]; let res = unblock(buf, 8); - assert_eq!(res, - vec![0u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 7u8, NL], + assert_eq!( + res, + vec![0u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 7u8, NL], ); } #[test] -fn unblock_test_strip_single_cbs() -{ +fn unblock_test_strip_single_cbs() { let buf = vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE]; let res = unblock(buf, 8); - assert_eq!(res, - vec![0u8, 1u8, 2u8, 3u8, NL], - ); + assert_eq!(res, vec![0u8, 1u8, 2u8, 3u8, NL],); } #[test] -fn unblock_test_strip_multi_cbs() -{ +fn unblock_test_strip_multi_cbs() { let buf = vec![ vec![0u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], vec![0u8, 1u8, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE], vec![0u8, 1u8, 2u8, SPACE, SPACE, SPACE, SPACE, SPACE], vec![0u8, 1u8, 2u8, 3u8, SPACE, SPACE, SPACE, SPACE], - ].into_iter().flatten().collect::>(); + ] + .into_iter() + .flatten() + .collect::>(); let res = unblock(buf, 8); @@ -339,7 +332,10 @@ fn unblock_test_strip_multi_cbs() vec![0u8, 1u8, NL], vec![0u8, 1u8, 2u8, NL], vec![0u8, 1u8, 2u8, 3u8, NL], - ].into_iter().flatten().collect::>(); + ] + .into_iter() + .flatten() + .collect::>(); assert_eq!(res, exp); } diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 2195b1b84..177aee1a5 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -99,7 +99,9 @@ make_sync_test!( make_sync_test!( deadbeef_16_delayed, "deadbeef-16-delayed", - LazyReader { src: File::open("./test-resources/deadbeef-16.test").unwrap() }, + LazyReader { + src: File::open("./test-resources/deadbeef-16.test").unwrap() + }, Some(0u8), 16, 32, diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index a1a7ad892..e9f17e8d4 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -129,14 +129,16 @@ make_conv_test!( ); #[test] -fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() -{ +fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { // ASCII->EBCDIC let test_name = "all-valid-ascii-to-ebcdic"; let tmp_fname_ae = format!("./test-resources/FAILED-{}.test", test_name); let i = Input { - src: File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(), + src: File::open( + "./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test", + ) + .unwrap(), non_ascii: false, ibs: 128, xfer_stats: None, @@ -152,7 +154,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() oflags: DEFAULT_OFLAGS, }; - dd_fileout(i,o).unwrap(); + dd_fileout(i, o).unwrap(); // EBCDIC->ASCII let test_name = "all-valid-ebcdic-to-ascii"; @@ -175,13 +177,18 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() oflags: DEFAULT_OFLAGS, }; - dd_fileout(i,o).unwrap(); + dd_fileout(i, o).unwrap(); // Final Comparison let res = File::open(&tmp_fname_ea).unwrap(); - let spec = File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test").unwrap(); + let spec = + File::open("./test-resources/all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test") + .unwrap(); - assert_eq!(res.metadata().unwrap().len(), spec.metadata().unwrap().len()); + assert_eq!( + res.metadata().unwrap().len(), + spec.metadata().unwrap().len() + ); let res = BufReader::new(res); let spec = BufReader::new(spec); @@ -189,10 +196,8 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() let res = BufReader::new(res); // Check all bytes match - for (b_res, b_spec) in res.bytes().zip(spec.bytes()) - { - assert_eq!(b_res.unwrap(), - b_spec.unwrap()); + for (b_res, b_spec) in res.bytes().zip(spec.bytes()) { + assert_eq!(b_res.unwrap(), b_spec.unwrap()); } fs::remove_file(&tmp_fname_ae).unwrap(); diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index cb514e33d..a1780204c 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -1,13 +1,13 @@ use super::*; -mod sanity_tests; -mod conversion_tests; mod block_unblock_tests; mod conv_sync_tests; +mod conversion_tests; +mod sanity_tests; +use std::fs; use std::io::prelude::*; use std::io::BufReader; -use std::fs; const DEFAULT_CFO: OConvFlags = OConvFlags { sparse: false, @@ -55,15 +55,12 @@ const DEFAULT_OFLAGS: OFlags = OFlags { seek_bytes: false, }; -struct LazyReader -{ +struct LazyReader { src: R, } -impl Read for LazyReader -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result - { +impl Read for LazyReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { let reduced = cmp::max(buf.len() / 2, 1); self.src.read(&mut buf[..reduced]) } @@ -143,4 +140,3 @@ macro_rules! make_spec_test ( } }; ); - diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index f423af212..2024ea12e 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -115,7 +115,7 @@ make_io_test!( non_ascii: false, ibs: 531, xfer_stats: None, - count: Some(CountType::Bytes(32*1024)), + count: Some(CountType::Bytes(32 * 1024)), cflags: icf!(), iflags: DEFAULT_IFLAGS, }, @@ -199,7 +199,7 @@ make_io_test!( non_ascii: false, ibs: 521, xfer_stats: None, - count: Some(CountType::Bytes(32*1024)), + count: Some(CountType::Bytes(32 * 1024)), cflags: icf!(), iflags: DEFAULT_IFLAGS, }, @@ -217,7 +217,8 @@ make_io_test!( "random-73k-test-lazy-fullblock", Input { src: LazyReader { - src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() + src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test") + .unwrap() }, non_ascii: false, ibs: 521, @@ -254,53 +255,48 @@ make_io_test!( // Test internal buffer size fn #[test] -fn bsize_test_primes() -{ - let (n,m) = (7901, 7919); +fn bsize_test_primes() { + let (n, m) = (7901, 7919); let res = calc_bsize(n, m); assert!(res % n == 0); assert!(res % m == 0); - assert_eq!(res, n*m); + assert_eq!(res, n * m); } #[test] -fn bsize_test_rel_prime_obs_greater() -{ - let (n,m) = (7*5119, 13*5119); +fn bsize_test_rel_prime_obs_greater() { + let (n, m) = (7 * 5119, 13 * 5119); let res = calc_bsize(n, m); assert!(res % n == 0); assert!(res % m == 0); - assert_eq!(res, 7*13*5119); + assert_eq!(res, 7 * 13 * 5119); } #[test] -fn bsize_test_rel_prime_ibs_greater() -{ - let (n,m) = (13*5119, 7*5119); +fn bsize_test_rel_prime_ibs_greater() { + let (n, m) = (13 * 5119, 7 * 5119); let res = calc_bsize(n, m); assert!(res % n == 0); assert!(res % m == 0); - assert_eq!(res, 7*13*5119); + assert_eq!(res, 7 * 13 * 5119); } #[test] -fn bsize_test_3fac_rel_prime() -{ - let (n,m) = (11*13*5119, 7*11*5119); +fn bsize_test_3fac_rel_prime() { + let (n, m) = (11 * 13 * 5119, 7 * 11 * 5119); let res = calc_bsize(n, m); assert!(res % n == 0); assert!(res % m == 0); - assert_eq!(res, 7*11*13*5119); + assert_eq!(res, 7 * 11 * 13 * 5119); } #[test] -fn bsize_test_ibs_greater() -{ - let (n,m) = (512*1024, 256*1024); +fn bsize_test_ibs_greater() { + let (n, m) = (512 * 1024, 256 * 1024); let res = calc_bsize(n, m); assert!(res % n == 0); assert!(res % m == 0); @@ -309,9 +305,8 @@ fn bsize_test_ibs_greater() } #[test] -fn bsize_test_obs_greater() -{ - let (n,m) = (256*1024, 512*1024); +fn bsize_test_obs_greater() { + let (n, m) = (256 * 1024, 512 * 1024); let res = calc_bsize(n, m); assert!(res % n == 0); assert!(res % m == 0); @@ -320,9 +315,8 @@ fn bsize_test_obs_greater() } #[test] -fn bsize_test_bs_eq() -{ - let (n,m) = (1024, 1024); +fn bsize_test_bs_eq() { + let (n, m) = (1024, 1024); let res = calc_bsize(n, m); assert!(res % n == 0); assert!(res % m == 0); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index f134886b2..15bb8d481 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -8,8 +8,7 @@ pub type Matches = clap::ArgMatches<'static>; /// Parser Errors describe errors with parser input #[derive(Debug, PartialEq)] -pub enum ParseError -{ +pub enum ParseError { MultipleFmtTable, MultipleUCaseLCase, MultipleBlockUnblock, @@ -24,59 +23,52 @@ pub enum ParseError Unimplemented(String), } -impl std::fmt::Display for ParseError -{ +impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self - { - Self::MultipleFmtTable => - { - write!(f, "Only one of conv=ascii conv=ebcdic or conv=ibm may be specified") - }, - Self::MultipleUCaseLCase => - { + match self { + Self::MultipleFmtTable => { + write!( + f, + "Only one of conv=ascii conv=ebcdic or conv=ibm may be specified" + ) + } + Self::MultipleUCaseLCase => { write!(f, "Only one of conv=lcase or conv=ucase may be specified") - }, - Self::MultipleBlockUnblock => - { + } + Self::MultipleBlockUnblock => { write!(f, "Only one of conv=block or conv=unblock may be specified") - }, - Self::MultipleExclNoCreat => - { + } + Self::MultipleExclNoCreat => { write!(f, "Only one ov conv=excl or conv=nocreat may be specified") - }, - Self::FlagNoMatch(arg) => - { + } + Self::FlagNoMatch(arg) => { write!(f, "Unrecognized iflag=FLAG or oflag=FLAG -> {}", arg) - }, - Self::ConvFlagNoMatch(arg) => - { + } + Self::ConvFlagNoMatch(arg) => { write!(f, "Unrecognized conv=CONV -> {}", arg) - }, - Self::NoMatchingMultiplier(arg) => - { + } + Self::NoMatchingMultiplier(arg) => { write!(f, "Unrecognized byte multiplier -> {}", arg) - }, - Self::ByteStringContainsNoValue(arg) => - { + } + Self::ByteStringContainsNoValue(arg) => { write!(f, "Unrecognized byte value -> {}", arg) - }, - Self::MultiplierStringWouldOverflow(arg) => - { - write!(f, "Multiplier string would overflow on current system -> {}", arg) - }, - Self::BlockUnblockWithoutCBS => - { + } + Self::MultiplierStringWouldOverflow(arg) => { + write!( + f, + "Multiplier string would overflow on current system -> {}", + arg + ) + } + Self::BlockUnblockWithoutCBS => { write!(f, "conv=block or conv=unblock specified without cbs=N") - }, - Self::StatusLevelNotRecognized(arg) => - { + } + Self::StatusLevelNotRecognized(arg) => { write!(f, "status=LEVEL not recognized -> {}", arg) - }, - Self::Unimplemented(arg) => - { + } + Self::Unimplemented(arg) => { write!(f, "feature not implemented on this system -> {}", arg) - }, + } } } } @@ -86,8 +78,7 @@ impl Error for ParseError {} /// Some flags specified as part of a conv=CONV[,CONV]... block /// relate to the input file, others to the output file. #[derive(Debug, PartialEq)] -enum ConvFlag -{ +enum ConvFlag { // Input FmtAtoE, FmtEtoA, @@ -108,56 +99,35 @@ enum ConvFlag FSync, } -impl std::str::FromStr for ConvFlag -{ +impl std::str::FromStr for ConvFlag { type Err = ParseError; - fn from_str(s: &str) -> Result - { - match s - { + fn from_str(s: &str) -> Result { + match s { // Input - "ascii" => - Ok(Self::FmtEtoA), - "ebcdic" => - Ok(Self::FmtAtoE), - "ibm" => - Ok(Self::FmtAtoI), - "lcase" => - Ok(Self::LCase), - "ucase" => - Ok(Self::UCase), - "block" => - Ok(Self::Block), - "unblock" => - Ok(Self::Unblock), - "swab" => - Ok(Self::Swab), - "sync" => - Ok(Self::Sync), - "noerror" => - Ok(Self::NoError), + "ascii" => Ok(Self::FmtEtoA), + "ebcdic" => Ok(Self::FmtAtoE), + "ibm" => Ok(Self::FmtAtoI), + "lcase" => Ok(Self::LCase), + "ucase" => Ok(Self::UCase), + "block" => Ok(Self::Block), + "unblock" => Ok(Self::Unblock), + "swab" => Ok(Self::Swab), + "sync" => Ok(Self::Sync), + "noerror" => Ok(Self::NoError), // Output - "sparse" => - Ok(Self::Sparse), - "excl" => - Ok(Self::Excl), - "nocreat" => - Ok(Self::NoCreat), - "notrunc" => - Ok(Self::NoTrunc), - "fdatasync" => - Ok(Self::FDataSync), - "fsync" => - Ok(Self::FSync), - _ => - Err(ParseError::ConvFlagNoMatch(String::from(s))) - } + "sparse" => Ok(Self::Sparse), + "excl" => Ok(Self::Excl), + "nocreat" => Ok(Self::NoCreat), + "notrunc" => Ok(Self::NoTrunc), + "fdatasync" => Ok(Self::FDataSync), + "fsync" => Ok(Self::FSync), + _ => Err(ParseError::ConvFlagNoMatch(String::from(s))), + } } } -enum Flag -{ +enum Flag { // Input only FullBlock, CountBytes, @@ -181,353 +151,271 @@ enum Flag SeekBytes, } -impl std::str::FromStr for Flag -{ +impl std::str::FromStr for Flag { type Err = ParseError; - fn from_str(s: &str) -> Result - { - match s - { + fn from_str(s: &str) -> Result { + match s { // Input only - "fullblock" => - Ok(Self::FullBlock), - "count_bytes" => - Ok(Self::CountBytes), - "skip_bytes" => - Ok(Self::SkipBytes), + "fullblock" => Ok(Self::FullBlock), + "count_bytes" => Ok(Self::CountBytes), + "skip_bytes" => Ok(Self::SkipBytes), // Either "cio" => - // Ok(Self::Cio), - Err(ParseError::Unimplemented(s.to_string())), + // Ok(Self::Cio), + { + Err(ParseError::Unimplemented(s.to_string())) + } "direct" => - // Ok(Self::Direct), - if cfg!(unix) - { + // Ok(Self::Direct), + { + if cfg!(unix) { Ok(Self::Direct) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "directory" => - // Ok(Self::Directory), - if cfg!(unix) - { + // Ok(Self::Directory), + { + if cfg!(unix) { Ok(Self::Directory) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "dsync" => - // Ok(Self::Dsync), - if cfg!(unix) - { + // Ok(Self::Dsync), + { + if cfg!(unix) { Ok(Self::Dsync) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "sync" => - // Ok(Self::Sync), - if cfg!(unix) - { + // Ok(Self::Sync), + { + if cfg!(unix) { Ok(Self::Sync) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "nocache" => - // Ok(Self::NoCache), - Err(ParseError::Unimplemented(s.to_string())), + // Ok(Self::NoCache), + { + Err(ParseError::Unimplemented(s.to_string())) + } "nonblock" => - // Ok(Self::NonBlock), - if cfg!(unix) - { + // Ok(Self::NonBlock), + { + if cfg!(unix) { Ok(Self::NonBlock) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "noatime" => - // Ok(Self::NoATime), - if cfg!(unix) - { + // Ok(Self::NoATime), + { + if cfg!(unix) { Ok(Self::NoATime) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "noctty" => - // Ok(Self::NoCtty), - if cfg!(unix) - { + // Ok(Self::NoCtty), + { + if cfg!(unix) { Ok(Self::NoCtty) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "nofollow" => - // Ok(Self::NoFollow), - if cfg!(unix) - { + // Ok(Self::NoFollow), + { + if cfg!(unix) { Ok(Self::NoFollow) - } - else - { + } else { Err(ParseError::Unimplemented(s.to_string())) - }, + } + } "nolinks" => - // Ok(Self::NoLinks), - Err(ParseError::Unimplemented(s.to_string())), + // Ok(Self::NoLinks), + { + Err(ParseError::Unimplemented(s.to_string())) + } "binary" => - // Ok(Self::Binary), - Err(ParseError::Unimplemented(s.to_string())), + // Ok(Self::Binary), + { + Err(ParseError::Unimplemented(s.to_string())) + } "text" => - // Ok(Self::Text), - Err(ParseError::Unimplemented(s.to_string())), + // Ok(Self::Text), + { + Err(ParseError::Unimplemented(s.to_string())) + } // Output only - "append" => - Ok(Self::Append), - "seek_bytes" => - Ok(Self::SeekBytes), - _ => - Err(ParseError::FlagNoMatch(String::from(s))), + "append" => Ok(Self::Append), + "seek_bytes" => Ok(Self::SeekBytes), + _ => Err(ParseError::FlagNoMatch(String::from(s))), } } } -impl std::str::FromStr for StatusLevel -{ +impl std::str::FromStr for StatusLevel { type Err = ParseError; - fn from_str(s: &str) -> Result - { - match s - { - "none" => - Ok(StatusLevel::None), - "noxfer" => - Ok(StatusLevel::Noxfer), - "progress" => - Ok(StatusLevel::Progress), - _ => - Err(ParseError::StatusLevelNotRecognized(s.to_string())), + fn from_str(s: &str) -> Result { + 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 -{ - match s - { - "c" => - Ok(1), - "w" => - Ok(2), - "b" => - Ok(512), - "kB" => - Ok(1000), - "K" | "KiB" => - Ok(1024), - "MB" => - Ok(1000*1000), - "M" | "MiB" => - Ok(1024*1024), - "GB" => - Ok(1000*1000*1000), - "G" | "GiB" => - Ok(1024*1024*1024), - "TB" => - Ok(1000*1000*1000*1000), - "T" | "TiB" => - Ok(1024*1024*1024*1024), - "PB" => - Ok(1000*1000*1000*1000*1000), - "P" | "PiB" => - Ok(1024*1024*1024*1024*1024), - "EB" => - Ok(1000*1000*1000*1000*1000*1000), - "E" | "EiB" => - Ok(1024*1024*1024*1024*1024*1024), -// The following would overflow on my x64 system -// "ZB" => -// Ok(1000*1000*1000*1000*1000*1000*1000), -// "Z" | "ZiB" => -// Ok(1024*1024*1024*1024*1024*1024*1024), -// "YB" => -// Ok(1000*1000*1000*1000*1000*1000*1000*1000), -// "Y" | "YiB" => -// Ok(1024*1024*1024*1024*1024*1024*1024*1024), - _ => - Err(ParseError::NoMatchingMultiplier(s.to_string())), +fn parse_multiplier<'a>(s: &'a str) -> Result { + match s { + "c" => Ok(1), + "w" => Ok(2), + "b" => Ok(512), + "kB" => Ok(1000), + "K" | "KiB" => Ok(1024), + "MB" => Ok(1000 * 1000), + "M" | "MiB" => Ok(1024 * 1024), + "GB" => Ok(1000 * 1000 * 1000), + "G" | "GiB" => Ok(1024 * 1024 * 1024), + "TB" => Ok(1000 * 1000 * 1000 * 1000), + "T" | "TiB" => Ok(1024 * 1024 * 1024 * 1024), + "PB" => Ok(1000 * 1000 * 1000 * 1000 * 1000), + "P" | "PiB" => Ok(1024 * 1024 * 1024 * 1024 * 1024), + "EB" => Ok(1000 * 1000 * 1000 * 1000 * 1000 * 1000), + "E" | "EiB" => Ok(1024 * 1024 * 1024 * 1024 * 1024 * 1024), + // The following would overflow on my x64 system + // "ZB" => + // Ok(1000*1000*1000*1000*1000*1000*1000), + // "Z" | "ZiB" => + // Ok(1024*1024*1024*1024*1024*1024*1024), + // "YB" => + // Ok(1000*1000*1000*1000*1000*1000*1000*1000), + // "Y" | "YiB" => + // Ok(1024*1024*1024*1024*1024*1024*1024*1024), + _ => Err(ParseError::NoMatchingMultiplier(s.to_string())), } } -fn parse_bytes_only(s: &str) -> Result -{ - match s.parse() - { - Ok(bytes) => - Ok(bytes), - Err(_) => - Err(ParseError::ByteStringContainsNoValue(s.to_string())), +fn parse_bytes_only(s: &str) -> Result { + match s.parse() { + Ok(bytes) => Ok(bytes), + Err(_) => Err(ParseError::ByteStringContainsNoValue(s.to_string())), } } -fn parse_bytes_with_opt_multiplier(s: &str) -> Result -{ - match s.find(char::is_alphabetic) - { - Some(idx) => - { +fn parse_bytes_with_opt_multiplier(s: &str) -> Result { + match s.find(char::is_alphabetic) { + Some(idx) => { let base = parse_bytes_only(&s[..idx])?; let mult = parse_multiplier(&s[idx..])?; - if let Some(bytes) = base.checked_mul(mult) - { + if let Some(bytes) = base.checked_mul(mult) { Ok(bytes) - } - else - { + } else { Err(ParseError::MultiplierStringWouldOverflow(s.to_string())) } } - _ => - parse_bytes_only(&s), + _ => parse_bytes_only(&s), } } -pub fn parse_ibs(matches: &Matches) -> Result -{ - if let Some(mixed_str) = matches.value_of("bs") - { +pub fn parse_ibs(matches: &Matches) -> Result { + if let Some(mixed_str) = matches.value_of("bs") { parse_bytes_with_opt_multiplier(mixed_str) - } - else if let Some(mixed_str) = matches.value_of("ibs") - { + } else if let Some(mixed_str) = matches.value_of("ibs") { parse_bytes_with_opt_multiplier(mixed_str) - } - else - { + } else { Ok(512) } } -fn parse_cbs(matches: &Matches) -> Result, ParseError> -{ - if let Some(s) = matches.value_of("cbs") - { +fn parse_cbs(matches: &Matches) -> Result, ParseError> { + if let Some(s) = matches.value_of("cbs") { let bytes = parse_bytes_with_opt_multiplier(s)?; Ok(Some(bytes)) - } - else - { + } else { Ok(None) } } -pub fn parse_status_level(matches: &Matches) -> Result, ParseError> -{ - match matches.value_of("status") - { - Some(s) => - { +pub fn parse_status_level(matches: &Matches) -> Result, ParseError> { + match matches.value_of("status") { + Some(s) => { let st = s.parse()?; Ok(Some(st)) - }, - None => - Ok(None), + } + None => Ok(None), } } -pub fn parse_obs(matches: &Matches) -> Result -{ - if let Some(mixed_str) = matches.value_of("bs") - { +pub fn parse_obs(matches: &Matches) -> Result { + if let Some(mixed_str) = matches.value_of("bs") { parse_bytes_with_opt_multiplier(mixed_str) - } - else if let Some(mixed_str) = matches.value_of("obs") - { + } else if let Some(mixed_str) = matches.value_of("obs") { parse_bytes_with_opt_multiplier(mixed_str) - } - else - { + } else { Ok(512) } } -fn parse_ctable(fmt: Option, case: Option) -> 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_ctable(fmt: Option, case: Option) -> 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, + 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) - { + match (fmt, case) { // Both [ascii | ebcdic | ibm] and [lcase | ucase] specified - (Some(fmt), Some(case)) => - parse_conv_and_case_table(&fmt, &case), + (Some(fmt), Some(case)) => parse_conv_and_case_table(&fmt, &case), // Only [ascii | ebcdic | ibm] specified - (Some(fmt), None) => - parse_conv_table_only(&fmt), + (Some(fmt), 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), + (None, Some(ConvFlag::UCase)) => Some(&ASCII_LCASE_TO_UCASE), + (None, Some(ConvFlag::LCase)) => Some(&ASCII_UCASE_TO_LCASE), // ST else... - (_, _) => - None, - } + (_, _) => None, + } } -fn parse_flag_list>(tag: &str, matches: &Matches) -> Result, ParseError> -{ +fn parse_flag_list>( + tag: &str, + matches: &Matches, +) -> Result, ParseError> { let mut flags = Vec::new(); - if let Some(comma_str) = matches.value_of(tag) - { - for s in comma_str.split(",") - { + if let Some(comma_str) = matches.value_of(tag) { + for s in comma_str.split(",") { let flag = s.parse()?; flags.push(flag); } @@ -538,8 +426,7 @@ fn parse_flag_list>(tag: &str, matches: & /// Parse Conversion Options (Input Variety) /// Construct and validate a IConvFlags -pub fn parse_conv_flag_input(matches: &Matches) -> Result -{ +pub fn parse_conv_flag_input(matches: &Matches) -> Result { let flags = parse_flag_list("conv", matches)?; let cbs = parse_cbs(matches)?; @@ -551,96 +438,66 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result - if let Some(_) = fmt - { + for flag in flags { + match flag { + ConvFlag::FmtEtoA => { + if let Some(_) = fmt { return Err(ParseError::MultipleFmtTable); - } - else - { + } else { fmt = Some(flag); - }, - ConvFlag::FmtAtoE => - if let Some(_) = fmt - { + } + } + ConvFlag::FmtAtoE => { + if let Some(_) = fmt { return Err(ParseError::MultipleFmtTable); - } - else - { + } else { fmt = Some(flag); - }, - ConvFlag::FmtAtoI => - if let Some(_) = fmt - { + } + } + ConvFlag::FmtAtoI => { + if let Some(_) = fmt { return Err(ParseError::MultipleFmtTable); - } - else - { + } else { fmt = Some(flag); - }, - ConvFlag::UCase => - if let Some(_) = case - { + } + } + ConvFlag::UCase => { + if let Some(_) = case { return Err(ParseError::MultipleUCaseLCase); - } - else - { + } else { case = Some(flag) - }, - ConvFlag::LCase => - if let Some(_) = case - { + } + } + ConvFlag::LCase => { + if let Some(_) = case { return Err(ParseError::MultipleUCaseLCase); - } - else - { + } else { case = Some(flag) - }, - 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, block) - { - (Some(cbs), None) => - unblock = Some(cbs), - (None, _) => - return Err(ParseError::BlockUnblockWithoutCBS), - (_, Some(_)) => - return Err(ParseError::MultipleBlockUnblock), - }, - ConvFlag::Swab => - swab = true, - ConvFlag::Sync => - sync = true, - ConvFlag::NoError => - noerror = true, - _ => {}, + } + } + 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, block) { + (Some(cbs), None) => unblock = Some(cbs), + (None, _) => return Err(ParseError::BlockUnblockWithoutCBS), + (_, Some(_)) => return Err(ParseError::MultipleBlockUnblock), + }, + ConvFlag::Swab => swab = true, + ConvFlag::Sync => sync = true, + ConvFlag::NoError => noerror = true, + _ => {} } } let ctable = parse_ctable(fmt, case); - let sync = if sync && (block.is_some() || unblock.is_some()) - { + let sync = if sync && (block.is_some() || unblock.is_some()) { Some(' ' as u8) - } - else if sync - { + } else if sync { Some(0u8) - } - else - { + } else { None }; @@ -656,8 +513,7 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result Result -{ +pub fn parse_conv_flag_output(matches: &Matches) -> Result { let flags = parse_flag_list("conv", matches)?; let mut sparse = false; @@ -667,38 +523,28 @@ pub fn parse_conv_flag_output(matches: &Matches) -> Result - sparse = true, - ConvFlag::Excl => - if !nocreat - { + for flag in flags { + match flag { + ConvFlag::Sparse => sparse = true, + ConvFlag::Excl => { + if !nocreat { excl = true; - } - else - { + } else { return Err(ParseError::MultipleExclNoCreat); - }, - ConvFlag::NoCreat => - if !excl - { + } + } + ConvFlag::NoCreat => { + if !excl { nocreat = true; - } - else - { + } else { return Err(ParseError::MultipleExclNoCreat); - }, - ConvFlag::NoTrunc => - notrunc = true, - ConvFlag::FDataSync => - fdatasync = true, - ConvFlag::FSync => - fsync = true, - _ => {}, - } + } + } + ConvFlag::NoTrunc => notrunc = true, + ConvFlag::FDataSync => fdatasync = true, + ConvFlag::FSync => fsync = true, + _ => {} + } } Ok(OConvFlags { @@ -712,8 +558,7 @@ pub fn parse_conv_flag_output(matches: &Matches) -> Result Result -{ +pub fn parse_iflags(matches: &Matches) -> Result { let mut cio = false; let mut direct = false; let mut directory = false; @@ -733,47 +578,29 @@ pub fn parse_iflags(matches: &Matches) -> Result 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::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, - _ => {}, + 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::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{ + Ok(IFlags { cio, direct, directory, @@ -794,8 +621,7 @@ pub fn parse_iflags(matches: &Matches) -> Result } /// Parse OFlags struct from CL-input -pub fn parse_oflags(matches: &Matches) -> Result -{ +pub fn parse_oflags(matches: &Matches) -> Result { let mut append = false; let mut cio = false; let mut direct = false; @@ -814,41 +640,24 @@ pub fn parse_oflags(matches: &Matches) -> Result 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::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, - _ => {}, + 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::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, + _ => {} } } @@ -872,79 +681,62 @@ pub fn parse_oflags(matches: &Matches) -> Result } /// Parse the amount of the input file to skip. -pub fn parse_skip_amt(ibs: &usize, iflags: &IFlags, matches: &Matches) -> Result, ParseError> -{ - if let Some(amt) = matches.value_of("skip") - { - if iflags.skip_bytes - { +pub fn parse_skip_amt( + ibs: &usize, + iflags: &IFlags, + matches: &Matches, +) -> Result, ParseError> { + if let Some(amt) = matches.value_of("skip") { + if iflags.skip_bytes { let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(n)) - } - else - { + } else { let n = parse_bytes_with_opt_multiplier(amt)?; - Ok(Some(ibs*n)) + Ok(Some(ibs * n)) } - } - else - { + } else { Ok(None) } } /// Parse the amount of the output file to seek. -pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &Matches) -> Result, ParseError> -{ - if let Some(amt) = matches.value_of("seek") - { - if oflags.seek_bytes - { +pub fn parse_seek_amt( + obs: &usize, + oflags: &OFlags, + matches: &Matches, +) -> Result, ParseError> { + if let Some(amt) = matches.value_of("seek") { + if oflags.seek_bytes { let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(n)) - } - else - { + } else { let n = parse_bytes_with_opt_multiplier(amt)?; - Ok(Some(obs*n)) + Ok(Some(obs * n)) } - } - else - { + } else { Ok(None) } } /// Parse the value of count=N and the type of N implied by iflags -pub fn parse_count(iflags: &IFlags, matches: &Matches) -> Result, ParseError> -{ - if let Some(amt) = matches.value_of("count") - { +pub fn parse_count(iflags: &IFlags, matches: &Matches) -> Result, ParseError> { + if let Some(amt) = matches.value_of("count") { let n = parse_bytes_with_opt_multiplier(amt)?; - if iflags.count_bytes - { + if iflags.count_bytes { Ok(Some(CountType::Bytes(n))) - } - else - { + } else { Ok(Some(CountType::Reads(n))) } - } - else - { + } else { Ok(None) } } /// Parse whether the args indicate the input is not ascii -pub fn parse_input_non_ascii(matches: &Matches) -> Result -{ - if let Some(conv_opts) = matches.value_of("conv") - { +pub fn parse_input_non_ascii(matches: &Matches) -> Result { + if let Some(conv_opts) = matches.value_of("conv") { Ok(conv_opts.contains("ascii")) - } - else - { + } else { Ok(false) } } diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 05d9176d0..2c8954cec 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -1,19 +1,23 @@ use super::*; -use crate::{ - build_dd_app, - StatusLevel, -}; +use crate::{build_dd_app, StatusLevel}; #[cfg(not(unix))] #[test] -fn unimplemented_flags_should_error_non_unix() -{ +fn unimplemented_flags_should_error_non_unix() { let mut unfailed = Vec::new(); // The following flags are only implemented in unix - for flag in vec!["direct", "directory", "dsync", "sync", "nonblock", "noatime", "noctty", "nofollow"] - { + for flag in vec![ + "direct", + "directory", + "dsync", + "sync", + "nonblock", + "noatime", + "noctty", + "nofollow", + ] { let args = vec![ String::from("dd"), format!("--iflag={}", flag), @@ -21,36 +25,30 @@ fn unimplemented_flags_should_error_non_unix() ]; let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); - match parse_iflags(&matches) - { - Ok(_) => - unfailed.push(format!("iflag={}", flag)), - Err(_) => - {/* expected behaviour :-) */}, + match parse_iflags(&matches) { + Ok(_) => unfailed.push(format!("iflag={}", flag)), + Err(_) => { /* expected behaviour :-) */ } } - match parse_oflags(&matches) - { - Ok(_) => - unfailed.push(format!("oflag={}", flag)), - Err(_) => - {/* expected behaviour :-) */}, + match parse_oflags(&matches) { + Ok(_) => unfailed.push(format!("oflag={}", flag)), + Err(_) => { /* expected behaviour :-) */ } } } - if !unfailed.is_empty() - { - panic!("The following flags did not panic as expected: {:?}", unfailed); + if !unfailed.is_empty() { + panic!( + "The following flags did not panic as expected: {:?}", + unfailed + ); } } #[test] -fn unimplemented_flags_should_error() -{ +fn unimplemented_flags_should_error() { let mut unfailed = Vec::new(); // The following flags are not implemented - for flag in vec!["cio", "nocache", "nolinks", "text", "binary"] - { + for flag in vec!["cio", "nocache", "nolinks", "text", "binary"] { let args = vec![ String::from("dd"), format!("--iflag={}", flag), @@ -58,31 +56,26 @@ fn unimplemented_flags_should_error() ]; let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); - match parse_iflags(&matches) - { - Ok(_) => - unfailed.push(format!("iflag={}", flag)), - Err(_) => - {/* expected behaviour :-) */}, + match parse_iflags(&matches) { + Ok(_) => unfailed.push(format!("iflag={}", flag)), + Err(_) => { /* expected behaviour :-) */ } } - match parse_oflags(&matches) - { - Ok(_) => - unfailed.push(format!("oflag={}", flag)), - Err(_) => - {/* expected behaviour :-) */}, + match parse_oflags(&matches) { + Ok(_) => unfailed.push(format!("oflag={}", flag)), + Err(_) => { /* expected behaviour :-) */ } } } - if !unfailed.is_empty() - { - panic!("The following flags did not panic as expected: {:?}", unfailed); + if !unfailed.is_empty() { + panic!( + "The following flags did not panic as expected: {:?}", + unfailed + ); } } #[test] -fn test_status_level_absent() -{ +fn test_status_level_absent() { let args = vec![ String::from("dd"), String::from("--if=foo.file"), @@ -96,8 +89,7 @@ fn test_status_level_absent() } #[test] -fn test_status_level_none() -{ +fn test_status_level_none() { let args = vec![ String::from("dd"), String::from("--status=none"), @@ -112,8 +104,7 @@ fn test_status_level_none() } #[test] -fn test_status_level_progress() -{ +fn test_status_level_progress() { let args = vec![ String::from("dd"), String::from("--if=foo.file"), @@ -128,8 +119,7 @@ fn test_status_level_progress() } #[test] -fn test_status_level_noxfer() -{ +fn test_status_level_noxfer() { let args = vec![ String::from("dd"), String::from("--if=foo.file"), @@ -147,12 +137,8 @@ fn test_status_level_noxfer() #[test] #[should_panic] -fn icf_ctable_error() -{ - let args = vec![ - String::from("dd"), - String::from("--conv=ascii,ebcdic,ibm"), - ]; +fn icf_ctable_error() { + let args = vec![String::from("dd"), String::from("--conv=ascii,ebcdic,ibm")]; let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); @@ -161,12 +147,8 @@ fn icf_ctable_error() #[test] #[should_panic] -fn icf_case_error() -{ - let args = vec![ - String::from("dd"), - String::from("--conv=ucase,lcase"), - ]; +fn icf_case_error() { + let args = vec![String::from("dd"), String::from("--conv=ucase,lcase")]; let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); @@ -175,12 +157,8 @@ fn icf_case_error() #[test] #[should_panic] -fn icf_block_error() -{ - let args = vec![ - String::from("dd"), - String::from("--conv=block,unblock"), - ]; +fn icf_block_error() { + let args = vec![String::from("dd"), String::from("--conv=block,unblock")]; let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); @@ -189,12 +167,8 @@ fn icf_block_error() #[test] #[should_panic] -fn icf_creat_error() -{ - let args = vec![ - String::from("dd"), - String::from("--conv=excl,nocreat"), - ]; +fn icf_creat_error() { + let args = vec![String::from("dd"), String::from("--conv=excl,nocreat")]; let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); @@ -202,35 +176,23 @@ fn icf_creat_error() } #[test] -fn parse_icf_token_ibm() -{ - let exp = vec![ - ConvFlag::FmtAtoI, - ]; +fn parse_icf_token_ibm() { + let exp = vec![ConvFlag::FmtAtoI]; - let args = vec![ - String::from("dd"), - String::from("--conv=ibm"), - ]; + let args = vec![String::from("dd"), String::from("--conv=ibm")]; let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); - for cf in &exp - { + for cf in &exp { assert!(exp.contains(&cf)); } } #[test] -fn parse_icf_tokens_elu() -{ - let exp = vec![ - ConvFlag::FmtEtoA, - ConvFlag::LCase, - ConvFlag::Unblock, - ]; +fn parse_icf_tokens_elu() { + let exp = vec![ConvFlag::FmtEtoA, ConvFlag::LCase, ConvFlag::Unblock]; let args = vec![ String::from("dd"), @@ -240,15 +202,13 @@ fn parse_icf_tokens_elu() let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); - for cf in &exp - { + for cf in &exp { assert!(exp.contains(&cf)); } } #[test] -fn parse_icf_tokens_remaining() -{ +fn parse_icf_tokens_remaining() { let exp = vec![ ConvFlag::FmtAtoE, ConvFlag::UCase, @@ -274,8 +234,7 @@ fn parse_icf_tokens_remaining() let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); - for cf in &exp - { + for cf in &exp { assert!(exp.contains(&cf)); } } @@ -294,130 +253,53 @@ macro_rules! test_byte_parser ( } ); -test_byte_parser!( - test_bytes_n, - "765", - 765 -); -test_byte_parser!( - test_bytes_c, - "13c", - 13 -); +test_byte_parser!(test_bytes_n, "765", 765); +test_byte_parser!(test_bytes_c, "13c", 13); -test_byte_parser!( - test_bytes_w, - "1w", - 2 -); +test_byte_parser!(test_bytes_w, "1w", 2); -test_byte_parser!( - test_bytes_b, - "1b", - 512 -); +test_byte_parser!(test_bytes_b, "1b", 512); -test_byte_parser!( - test_bytes_k, - "1kB", - 1000 -); -test_byte_parser!( - test_bytes_K, - "1K", - 1024 -); -test_byte_parser!( - test_bytes_Ki, - "1KiB", - 1024 -); +test_byte_parser!(test_bytes_k, "1kB", 1000); +test_byte_parser!(test_bytes_K, "1K", 1024); +test_byte_parser!(test_bytes_Ki, "1KiB", 1024); -test_byte_parser!( - test_bytes_MB, - "2MB", - 2*1000*1000 -); -test_byte_parser!( - test_bytes_M, - "2M", - 2*1024*1024 -); -test_byte_parser!( - test_bytes_Mi, - "2MiB", - 2*1024*1024 -); +test_byte_parser!(test_bytes_MB, "2MB", 2 * 1000 * 1000); +test_byte_parser!(test_bytes_M, "2M", 2 * 1024 * 1024); +test_byte_parser!(test_bytes_Mi, "2MiB", 2 * 1024 * 1024); -test_byte_parser!( - test_bytes_GB, - "3GB", - 3*1000*1000*1000 -); -test_byte_parser!( - test_bytes_G, - "3G", - 3*1024*1024*1024 -); -test_byte_parser!( - test_bytes_Gi, - "3GiB", - 3*1024*1024*1024 -); +test_byte_parser!(test_bytes_GB, "3GB", 3 * 1000 * 1000 * 1000); +test_byte_parser!(test_bytes_G, "3G", 3 * 1024 * 1024 * 1024); +test_byte_parser!(test_bytes_Gi, "3GiB", 3 * 1024 * 1024 * 1024); -test_byte_parser!( - test_bytes_TB, - "4TB", - 4*1000*1000*1000*1000 -); -test_byte_parser!( - test_bytes_T, - "4T", - 4*1024*1024*1024*1024 -); -test_byte_parser!( - test_bytes_Ti, - "4TiB", - 4*1024*1024*1024*1024 -); +test_byte_parser!(test_bytes_TB, "4TB", 4 * 1000 * 1000 * 1000 * 1000); +test_byte_parser!(test_bytes_T, "4T", 4 * 1024 * 1024 * 1024 * 1024); +test_byte_parser!(test_bytes_Ti, "4TiB", 4 * 1024 * 1024 * 1024 * 1024); -test_byte_parser!( - test_bytes_PB, - "5PB", - 5*1000*1000*1000*1000*1000 -); -test_byte_parser!( - test_bytes_P, - "5P", - 5*1024*1024*1024*1024*1024 -); -test_byte_parser!( - test_bytes_Pi, - "5PiB", - 5*1024*1024*1024*1024*1024 -); +test_byte_parser!(test_bytes_PB, "5PB", 5 * 1000 * 1000 * 1000 * 1000 * 1000); +test_byte_parser!(test_bytes_P, "5P", 5 * 1024 * 1024 * 1024 * 1024 * 1024); +test_byte_parser!(test_bytes_Pi, "5PiB", 5 * 1024 * 1024 * 1024 * 1024 * 1024); test_byte_parser!( test_bytes_EB, "6EB", - 6*1000*1000*1000*1000*1000*1000 + 6 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 ); test_byte_parser!( test_bytes_E, "6E", - 6*1024*1024*1024*1024*1024*1024 + 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 ); test_byte_parser!( test_bytes_Ei, "6EiB", - 6*1024*1024*1024*1024*1024*1024 + 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 ); #[test] #[should_panic] #[allow(non_snake_case)] -fn test_KB_multiplier_error() -{ +fn test_KB_multiplier_error() { // KB is not valid (kB, K, and KiB are) let bs_str = "2000KB"; @@ -426,8 +308,7 @@ fn test_KB_multiplier_error() #[test] #[should_panic] -fn test_overflow_panic() -{ +fn test_overflow_panic() { let bs_str = format!("{}KiB", usize::MAX); parse_bytes_with_opt_multiplier(&bs_str).unwrap(); @@ -435,8 +316,7 @@ fn test_overflow_panic() #[test] #[should_panic] -fn test_neg_panic() -{ +fn test_neg_panic() { let bs_str = format!("{}KiB", -1); parse_bytes_with_opt_multiplier(&bs_str).unwrap(); From 860cbc6311f7973a165684b7f4106a08212b65a1 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 2 Jul 2021 15:30:46 -0700 Subject: [PATCH 42/66] Removes or ignores all compiler warnings --- src/uu/dd/src/datastructures.rs | 26 ++++++++++++++ src/uu/dd/src/dd.rs | 6 +--- .../src/dd_unit_tests/block_unblock_tests.rs | 2 -- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 1 - .../dd/src/dd_unit_tests/conversion_tests.rs | 4 --- src/uu/dd/src/dd_unit_tests/mod.rs | 35 +++++++++---------- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 10 ------ src/uu/dd/src/parseargs.rs | 13 +++++++ 8 files changed, 57 insertions(+), 40 deletions(-) diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index 42026c5b3..af4a68c98 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -68,18 +68,31 @@ pub struct OConvFlags { /// Stores all Flags that apply to the input pub struct IFlags { + #[allow(dead_code)] pub cio: bool, + #[allow(dead_code)] pub direct: bool, + #[allow(dead_code)] pub directory: bool, + #[allow(dead_code)] pub dsync: bool, + #[allow(dead_code)] pub sync: bool, + #[allow(dead_code)] pub nocache: bool, + #[allow(dead_code)] pub nonblock: bool, + #[allow(dead_code)] pub noatime: bool, + #[allow(dead_code)] pub noctty: bool, + #[allow(dead_code)] pub nofollow: bool, + #[allow(dead_code)] pub nolinks: bool, + #[allow(dead_code)] pub binary: bool, + #[allow(dead_code)] pub text: bool, pub fullblock: bool, pub count_bytes: bool, @@ -89,18 +102,31 @@ pub struct IFlags { /// Stores all Flags that apply to the output pub struct OFlags { pub append: bool, + #[allow(dead_code)] pub cio: bool, + #[allow(dead_code)] pub direct: bool, + #[allow(dead_code)] pub directory: bool, + #[allow(dead_code)] pub dsync: bool, + #[allow(dead_code)] pub sync: bool, + #[allow(dead_code)] pub nocache: bool, + #[allow(dead_code)] pub nonblock: bool, + #[allow(dead_code)] pub noatime: bool, + #[allow(dead_code)] pub noctty: bool, + #[allow(dead_code)] pub nofollow: bool, + #[allow(dead_code)] pub nolinks: bool, + #[allow(dead_code)] pub binary: bool, + #[allow(dead_code)] pub text: bool, pub seek_bytes: bool, } diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 64bedc828..ee4dc27ff 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -42,7 +42,7 @@ use std::sync::{atomic::AtomicUsize, atomic::Ordering, mpsc, Arc}; use std::thread; use std::time; -const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; +// const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; const ABOUT: &str = "copy, and optionally convert, a file system resource"; const BUF_INIT_BYTE: u8 = 0xDD; const RTN_SUCCESS: i32 = 0; @@ -294,14 +294,12 @@ struct Output { dst: W, obs: usize, cflags: OConvFlags, - oflags: OFlags, } impl Output { fn new(matches: &Matches) -> Result> { let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; - let oflags = parseargs::parse_oflags(matches)?; let dst = io::stdout(); @@ -309,7 +307,6 @@ impl Output { dst, obs, cflags, - oflags, }) } @@ -396,7 +393,6 @@ impl Output { dst, obs, cflags, - oflags, }) } else { // The following error should only occur if someone diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index f52d68fd0..f475754a5 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -39,7 +39,6 @@ macro_rules! make_block_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -72,7 +71,6 @@ macro_rules! make_unblock_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 177aee1a5..93ea8e34d 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -25,7 +25,6 @@ macro_rules! make_sync_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: $obs, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index e9f17e8d4..96e35445e 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -18,7 +18,6 @@ macro_rules! make_conv_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -44,7 +43,6 @@ macro_rules! make_icf_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -151,7 +149,6 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { dst: File::create(&tmp_fname_ae).unwrap(), obs: 1024, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }; dd_fileout(i, o).unwrap(); @@ -174,7 +171,6 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { dst: File::create(&tmp_fname_ea).unwrap(), obs: 1024, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }; dd_fileout(i, o).unwrap(); diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index a1780204c..ee28d4fd9 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -37,23 +37,23 @@ const DEFAULT_IFLAGS: IFlags = IFlags { 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, -}; +// 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, +// }; struct LazyReader { src: R, @@ -109,7 +109,6 @@ macro_rules! make_spec_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index 2024ea12e..e38870d4a 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -12,7 +12,6 @@ macro_rules! make_io_test ( dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: $o.obs, cflags: $o.cflags, - oflags: $o.oflags, }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -60,7 +59,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1031, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); @@ -81,7 +79,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 521, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); @@ -102,7 +99,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1024, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); @@ -123,7 +119,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1031, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); @@ -144,7 +139,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1031, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/gnudd-deadbeef-first-16k.spec").unwrap() ); @@ -165,7 +159,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1031, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/gnudd-deadbeef-first-12345.spec").unwrap() ); @@ -186,7 +179,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1024, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); @@ -207,7 +199,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1031, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); @@ -248,7 +239,6 @@ make_io_test!( dst: DST_PLACEHOLDER, obs: 1031, cflags: DEFAULT_CFO, - oflags: DEFAULT_OFLAGS, }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 15bb8d481..cc7a7447b 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -133,18 +133,31 @@ enum Flag { CountBytes, SkipBytes, // Either + #[allow(unused)] Cio, + #[allow(unused)] Direct, + #[allow(unused)] Directory, + #[allow(unused)] Dsync, + #[allow(unused)] Sync, + #[allow(unused)] NoCache, + #[allow(unused)] NonBlock, + #[allow(unused)] NoATime, + #[allow(unused)] NoCtty, + #[allow(unused)] NoFollow, + #[allow(unused)] NoLinks, + #[allow(unused)] Binary, + #[allow(unused)] Text, // Output only Append, From 418ecbe61a1ebffc63ccae0e1f3d2f8806ea65df Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 5 Jul 2021 11:27:17 -0700 Subject: [PATCH 43/66] Makes multiplier parsing system dependant - multipler is now created as u128, then returned as usize after conversion. Errors due to overflow now depend on the system on which the code is run. --- src/uu/dd/src/parseargs.rs | 54 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index cc7a7447b..8a28339e0 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -293,33 +293,33 @@ impl std::str::FromStr for StatusLevel { } fn parse_multiplier<'a>(s: &'a str) -> Result { - match s { - "c" => Ok(1), - "w" => Ok(2), - "b" => Ok(512), - "kB" => Ok(1000), - "K" | "KiB" => Ok(1024), - "MB" => Ok(1000 * 1000), - "M" | "MiB" => Ok(1024 * 1024), - "GB" => Ok(1000 * 1000 * 1000), - "G" | "GiB" => Ok(1024 * 1024 * 1024), - "TB" => Ok(1000 * 1000 * 1000 * 1000), - "T" | "TiB" => Ok(1024 * 1024 * 1024 * 1024), - "PB" => Ok(1000 * 1000 * 1000 * 1000 * 1000), - "P" | "PiB" => Ok(1024 * 1024 * 1024 * 1024 * 1024), - "EB" => Ok(1000 * 1000 * 1000 * 1000 * 1000 * 1000), - "E" | "EiB" => Ok(1024 * 1024 * 1024 * 1024 * 1024 * 1024), - // The following would overflow on my x64 system - // "ZB" => - // Ok(1000*1000*1000*1000*1000*1000*1000), - // "Z" | "ZiB" => - // Ok(1024*1024*1024*1024*1024*1024*1024), - // "YB" => - // Ok(1000*1000*1000*1000*1000*1000*1000*1000), - // "Y" | "YiB" => - // Ok(1024*1024*1024*1024*1024*1024*1024*1024), - _ => Err(ParseError::NoMatchingMultiplier(s.to_string())), - } + let mult: u128 = match s { + "c" => 1, + "w" => 2, + "b" => 512, + "kB" => 1000, + "K" | "KiB" => 1024, + "MB" => 1000 * 1000, + "M" | "MiB" => 1024 * 1024, + "GB" => 1000 * 1000 * 1000, + "G" | "GiB" => 1024 * 1024 * 1024, + "TB" => 1000 * 1000 * 1000 * 1000, + "T" | "TiB" => 1024 * 1024 * 1024 * 1024, + "PB" => 1000 * 1000 * 1000 * 1000 * 1000, + "P" | "PiB" => 1024 * 1024 * 1024 * 1024 * 1024, + "EB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000, + "E" | "EiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024, + "ZB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000, + "Z" | "ZiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, + "YB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000, + "Y" | "YiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, + _ => return Err(ParseError::NoMatchingMultiplier(s.to_string())), + }; + + mult.try_into() + .map_err(|_e| { + ParseError::MultiplierStringWouldOverflow(s.to_string()) + }) } fn parse_bytes_only(s: &str) -> Result { From 0b981d9dc38f58397b99efc5022ef09cbb037263 Mon Sep 17 00:00:00 2001 From: backwaterred <36117575+backwaterred@users.noreply.github.com> Date: Mon, 5 Jul 2021 11:34:22 -0700 Subject: [PATCH 44/66] Update src/uu/dd/src/datastructures.rs Adds project header to conversion_tables.rs Co-authored-by: Sylvestre Ledru --- src/uu/dd/src/datastructures.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index 42026c5b3..637bba3be 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -1,3 +1,10 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// spell-checker:ignore (ToDO) ctable noerror + use crate::conversion_tables::*; use std::error::Error; From 1ad89c5e897607d3533626d031874b727e186a67 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 6 Jul 2021 11:52:48 -0700 Subject: [PATCH 45/66] Addresses issues raised in PR#2474 - runs rustfmt on test_dd.rs - eliminates compiler warnings - adds many words to spellchecker ignore list - adds sanity test for vexing conv=nocreat issue. Still WIP. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/uu/dd/Cargo.toml | 6 +- src/uu/dd/src/datastructures.rs | 7 +- src/uu/dd/src/dd.rs | 241 +++++++++-- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 13 + src/uu/dd/src/parseargs.rs | 7 +- src/uu/dd/src/parseargs/unit_tests.rs | 104 ++++- tests/by-util/test_dd.rs | 433 +++++++------------- 9 files changed, 455 insertions(+), 360 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a0c5b510..5366d9024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1911,7 +1911,7 @@ dependencies = [ [[package]] name = "uu_dd" -version = "0.0.4" +version = "0.0.6" dependencies = [ "byte-unit", "clap", diff --git a/Cargo.toml b/Cargo.toml index 185a1beb1..b05db2c98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -248,7 +248,7 @@ cp = { optional=true, version="0.0.6", package="uu_cp", path="src/uu/cp" } csplit = { optional=true, version="0.0.6", package="uu_csplit", path="src/uu/csplit" } cut = { optional=true, version="0.0.6", package="uu_cut", path="src/uu/cut" } date = { optional=true, version="0.0.6", package="uu_date", path="src/uu/date" } -dd = { optional=true, version="0.0.4", package="uu_dd", path="src/uu/dd" } +dd = { optional=true, version="0.0.6", package="uu_dd", path="src/uu/dd" } df = { optional=true, version="0.0.6", package="uu_df", path="src/uu/df" } dircolors= { optional=true, version="0.0.6", package="uu_dircolors", path="src/uu/dircolors" } dirname = { optional=true, version="0.0.6", package="uu_dirname", path="src/uu/dirname" } diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 1a7905293..dc5d03d00 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dd" -version = "0.0.4" +version = "0.0.6" authors = ["uutils developers"] license = "MIT" description = "dd ~ (uutils) copy and convert files" @@ -16,12 +16,12 @@ path = "src/dd.rs" [dependencies] byte-unit = "4.0" -clap = "2.33.3" +clap = { version = "2.33", features = [ "wrap_help" ] } debug_print = "1.0" gcd = "2.0" libc = "0.2" signal-hook = "0.3.9" -uucore = { version=">=0.0.7", package="uucore", path="../../uucore" } +uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index af4a68c98..269f87402 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -162,10 +162,9 @@ impl std::fmt::Display for InternalError { Self::WrongInputType | Self::WrongOutputType => { write!(f, "Internal dd error: Wrong Input/Output data type") } - Self::InvalidConvBlockUnblockCase => write!( - f, - "Internal dd error: Invalid Conversion, Block, or Unblock data" - ), + Self::InvalidConvBlockUnblockCase => { + write!(f, "Invalid Conversion, Block, or Unblock data") + } } } } diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index ee4dc27ff..fcd1637c5 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -5,7 +5,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (T0DO) +// spell-checker:ignore (parseargs xfer cflags iflags parseargs parseargs xfer cflags iflags iflags iflags xfer cflags oflags oflags oflags oflags dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK xfer cflags fname fname tlen rlen fullblock rlen tlen tlen noerror rlen rlen remaing plen plen plen plen oflag dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK fname notrunc nocreat fname wlen wlen wlen wlen rstat rstat curr curr curr curr rposition rstat ctable ctable rstat ctable ctable btotal btotal btotal btotal SIGUSR SIGUSR sigval SIGINFO SIGINFO sigval SIGINFO SIGINFO SIGUSR sigval SIGUSR permenantly sigval itegral itegral wstat rmax rmax rmax rsofar rremain rmax rsofar rremain bmax bmax bmax bremain bmax wstat bremain wstat wstat opertaions Noxfer opertaions fileout Noxfer INFILE OUTFILE fileout fileout INFILE INFILE OUTFILE OUTFILE iflag oflag iflag noxfer ucase lcase ucase lcase nocreat nocreat notrunc noerror IFLAG IFLAG iflag iflag fullblock oflag dsync syncronized syncronized nonblock noatime nocache noctty nofollow notrunc OFLAG OFLAG oflag notrunc dsync syncronized syncronized nonblock noatime nocache noctty nofollow T0DO) #[macro_use] extern crate uucore; @@ -38,6 +38,7 @@ use std::fs::{File, OpenOptions}; use std::io::{self, Read, Seek, Write}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; +use std::path::Path; use std::sync::{atomic::AtomicUsize, atomic::Ordering, mpsc, Arc}; use std::thread; use std::time; @@ -303,11 +304,7 @@ impl Output { let dst = io::stdout(); - Ok(Output { - dst, - obs, - cflags, - }) + Ok(Output { dst, obs, cflags }) } fn fsync(&mut self) -> io::Result<()> { @@ -360,40 +357,42 @@ fn make_unix_oflags(oflags: &OFlags) -> Option { impl Output { fn new(matches: &Matches) -> Result> { + fn open_dst( + path: &Path, + cflags: &OConvFlags, + oflags: &OFlags, + ) -> Result> { + let mut opts = OpenOptions::new(); + opts.write(true) + .create(!cflags.nocreat) + .truncate(!cflags.notrunc) + .create_new(cflags.excl) + .append(oflags.append); + + if cfg!(unix) { + if let Some(libc_flags) = make_unix_oflags(oflags) { + opts.custom_flags(libc_flags); + } + } + + let dst = opts.open(path)?; + + Ok(dst) + } let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; let oflags = parseargs::parse_oflags(matches)?; let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; if let Some(fname) = matches.value_of("of") { - let mut dst = { - let mut opts = OpenOptions::new(); - opts.write(true) - .create(true) - .truncate(!cflags.notrunc) - .append(oflags.append) - // 'create_new' overrides 'create' - .create_new(cflags.excl && !cflags.nocreat); - - if cfg!(unix) { - if let Some(libc_flags) = make_unix_oflags(&oflags) { - opts.custom_flags(libc_flags); - } - } - - opts.open(fname)? - }; + let mut dst = open_dst(Path::new(&fname), &cflags, &oflags)?; if let Some(amt) = seek { let amt: u64 = amt.try_into()?; dst.seek(io::SeekFrom::Start(amt))?; } - Ok(Output { - dst, - obs, - cflags, - }) + Ok(Output { dst, obs, cflags }) } else { // The following error should only occur if someone // mistakenly calls Output::::new() without checking @@ -1083,7 +1082,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .iter() .fold(Vec::new(), append_dashes_if_not_present); - let matches = build_dd_app!() + let matches = uu_app() // TODO: usage, after_help //.usage(...) //.after_help(...) @@ -1134,14 +1133,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } pub fn uu_app() -> clap::App<'static, 'static> { - build_dd_app!() -} - -#[macro_export] -macro_rules! build_dd_app ( - () => - { - clap::App::new(executable!()) + clap::App::new(executable!()) .version(crate_version!()) .about(ABOUT) .arg( @@ -1306,5 +1298,176 @@ General-Flags ") ) - }; -); +} + +// #[macro_export] +// macro_rules! build_dd_app ( +// () => +// { +// clap::App::new(executable!()) +// .version(crate_version!()) +// .about(ABOUT) +// .arg( +// clap::Arg::with_name(options::INFILE) +// .long(options::INFILE) +// .takes_value(true) +// .help("if=FILE (alternatively --if FILE) specifies the file used for input. When not specified, stdin is used instead") +// ) +// .arg( +// clap::Arg::with_name(options::OUTFILE) +// .long(options::OUTFILE) +// .takes_value(true) +// .help("of=FILE (alternatively --of FILE) specifies the file used for output. When not specified, stdout is used instead") +// ) +// .arg( +// clap::Arg::with_name(options::IBS) +// .long(options::IBS) +// .takes_value(true) +// .help("ibs=N (alternatively --ibs N) specifies the size of buffer used for reads (default: 512). Multiplier strings permitted.") +// ) +// .arg( +// clap::Arg::with_name(options::OBS) +// .long(options::OBS) +// .takes_value(true) +// .help("obs=N (alternatively --obs N) specifies the size of buffer used for writes (default: 512). Multiplier strings permitted.") +// ) +// .arg( +// clap::Arg::with_name(options::BS) +// .long(options::BS) +// .takes_value(true) +// .help("bs=N (alternatively --bs N) specifies ibs=N and obs=N (default: 512). If ibs or obs are also specified, bs=N takes precedence. Multiplier strings permitted.") +// ) +// .arg( +// clap::Arg::with_name(options::CBS) +// .long(options::CBS) +// .takes_value(true) +// .help("cbs=BYTES (alternatively --cbs BYTES) specifies the 'conversion block size' in bytes. Applies to the conv=block, and conv=unblock operations. Multiplier strings permitted.") +// ) +// .arg( +// clap::Arg::with_name(options::SKIP) +// .long(options::SKIP) +// .takes_value(true) +// .help("skip=N (alternatively --skip N) causes N ibs-sized records of input to be skipped before beginning copy/convert operations. See iflag=count_bytes if skipping N bytes is preferred. Multiplier strings permitted.") +// ) +// .arg( +// clap::Arg::with_name(options::SEEK) +// .long(options::SEEK) +// .takes_value(true) +// .help("seek=N (alternatively --seek N) seeks N obs-sized records into output before beginning copy/convert operations. See oflag=seek_bytes if seeking N bytes is preferred. Multiplier strings permitted.") +// ) +// .arg( +// clap::Arg::with_name(options::COUNT) +// .long(options::COUNT) +// .takes_value(true) +// .help("count=N (alternatively --count N) stop reading input after N ibs-sized read operations rather than proceeding until EOF. See iflag=count_bytes if stopping after N bytes is preferred. Multiplier strings permitted.") +// ) +// .arg( +// clap::Arg::with_name(options::STATUS) +// .long(options::STATUS) +// .takes_value(true) +// .help("status=LEVEL (alternatively --status LEVEL) controls whether volume and performance stats are written to stderr. +// +// When unspecified, dd will print stats upon completion. An example is below. +// \t6+0 records in +// \t16+0 records out +// \t8192 bytes (8.2 kB, 8.0 KiB) copied, 0.00057009 s, 14.4 MB/s +// The first two lines are the 'volume' stats and the final line is the 'performance' stats. +// The volume stats indicate the number of complete and partial ibs-sized reads, or obs-sized writes that took place during the copy. The format of the volume stats is +. If records have been truncated (see conv=block), the volume stats will contain the number of truncated records. +// +// Permissible LEVEL values are: +// \t progress: Print periodic performance stats as the copy proceeds. +// \t noxfer: Print final volume stats, but not performance stats. +// \t none: Do not print any stats. +// +// Printing performance stats is also triggered by the INFO signal (where supported), or the USR1 signal. Setting the POSIXLY_CORRECT environment variable to any value (including an empty value) will cause the USR1 signal to be ignored. +// +// ") +// ) +// .arg( +// clap::Arg::with_name(options::CONV) +// .long(options::CONV) +// .takes_value(true) +// .help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file-flags. Conversion options and file flags may be intermixed. +// +// Conversion options: +// \t One of {ascii, ebcdic, ibm} will perform an encoding conversion. +// \t\t 'ascii' converts from EBCDIC to ASCII. This is the inverse of the 'ebcdic' option. +// \t\t 'ebcdic' converts from ASCII to EBCDIC. This is the inverse of the 'ascii' option. +// \t\t 'ibm' converts from ASCII to EBCDIC, applying the conventions for '[', ']' and '~' specified in POSIX. +// +// \t One of {ucase, lcase} will perform a case conversion. Works in conjunction with option {ascii, ebcdic, ibm} to infer input encoding. If no other conversion option is specified, input is assumed to be ascii. +// \t\t 'ucase' converts from lower-case to upper-case +// \t\t 'lcase' converts from upper-case to lower-case. +// +// \t One of {block, unblock}. Convert between lines terminated by newline characters, and fixed-width lines padded by spaces (without any newlines). Both the 'block' and 'unblock' options require cbs=BYTES be specified. +// \t\t 'block' for each newline less than the size indicated by cbs=BYTES, remove the newline and pad with spaces up to cbs. Lines longer than cbs are truncated. +// \t\t 'unblock' for each block of input of the size indicated by cbs=BYTES, remove right-trailing spaces and replace with a newline character. +// +// \t 'sparse' attempts to seek the output when an obs-sized block consists of only zeros. +// \t 'swab' swaps each adjacent pair of bytes. If an odd number of bytes is present, the final byte is omitted. +// \t 'sync' pad each ibs-sided block with zeros. If 'block' or 'unblock' is specified, pad with spaces instead. +// +// Flags: +// \t One of {excl, nocreat} +// \t\t 'excl' the output file must be created. Fail if the output file is already present. +// \t\t 'nocreat' the output file will not be created. Fail if the output file in not already present. +// \t 'notrunc' the output file will not be truncated. If this option is not present, output will be truncated when opened. +// \t 'noerror' all read errors will be ignored. If this option is not present, dd will only ignore Error::Interrupted. +// \t 'fdatasync' data will be written before finishing. +// \t 'fsync' data and metadata will be written before finishing. +// +// ") +// ) +// .arg( +// clap::Arg::with_name(options::IFLAG) +// .long(options::IFLAG) +// .takes_value(true) +// .help("iflag=FLAG[,FLAG] (alternatively --iflag FLAG[,FLAG]) a comma separated list of input flags which specify how the input source is treated. FLAG may be any of the input-flags or general-flags specified below. +// +// Input-Flags +// \t 'count_bytes' a value to count=N will be interpreted as bytes. +// \t 'skip_bytes' a value to skip=N will be interpreted as bytes. +// \t 'fullblock' wait for ibs bytes from each read. zero-length reads are still considered EOF. +// +// General-Flags +// \t 'direct' use direct I/O for data. +// \t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. +// \t 'dsync' use syncronized I/O for data. +// \t 'sync' use syncronized I/O for data and metadata. +// \t 'nonblock' use non-blocking I/O. +// \t 'noatime' do not update access time. +// \t 'nocache' request that OS drop cache. +// \t 'noctty' do not assign a controlling tty. +// \t 'nofollow' do not follow system links. +// +// Output-Flags +// \t 'append' open file in append mode. Consider setting conv=notrunc as well. +// \t 'seek_bytes' a value to seek=N will be interpreted as bytes. +// +// ") +// ) +// .arg( +// clap::Arg::with_name(options::OFLAG) +// .long(options::OFLAG) +// .takes_value(true) +// .help("oflag=FLAG[,FLAG] (alternatively --oflag FLAG[,FLAG]) a comma separated list of output flags which specify how the output source is treated. FLAG may be any of the output-flags or general-flags specified below. +// +// Output-Flags +// \t 'append' open file in append mode. Consider setting conv=notrunc as well. +// \t 'seek_bytes' a value to seek=N will be interpreted as bytes. +// +// General-Flags +// \t 'direct' use direct I/O for data. +// \t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. +// \t 'dsync' use syncronized I/O for data. +// \t 'sync' use syncronized I/O for data and metadata. +// \t 'nonblock' use non-blocking I/O. +// \t 'noatime' do not update access time. +// \t 'nocache' request that OS drop cache. +// \t 'noctty' do not assign a controlling tty. +// \t 'nofollow' do not follow system links. +// +// ") +// ) +// }; +// ); diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index e38870d4a..c5ed825bd 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -313,3 +313,16 @@ fn bsize_test_bs_eq() { assert_eq!(res, m); } + +#[test] +#[should_panic] +fn test_nocreate_causes_failure_when_ofile_doesnt_exist() { + let args = vec![ + String::from("dd"), + String::from("--conv=nocreat"), + String::from("--of=not-a-real.file"), + ]; + + let matches = uu_app().get_matches_from_safe(args).unwrap(); + let _ = Output::::new(&matches).unwrap(); +} diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 8a28339e0..e72d5cccc 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore (parseargs xfer cflags iflags parseargs parseargs xfer cflags iflags iflags iflags xfer cflags oflags oflags oflags oflags dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK xfer cflags fname fname tlen rlen fullblock rlen tlen tlen noerror rlen rlen remaing plen plen plen plen oflag dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK fname notrunc nocreat fname wlen wlen wlen wlen rstat rstat curr curr curr curr rposition rstat ctable ctable rstat ctable ctable btotal btotal btotal btotal SIGUSR SIGUSR sigval SIGINFO SIGINFO sigval SIGINFO SIGINFO SIGUSR sigval SIGUSR permenantly sigval itegral itegral wstat rmax rmax rmax rsofar rremain rmax rsofar rremain bmax bmax bmax bremain bmax wstat bremain wstat wstat opertaions Noxfer opertaions fileout Noxfer INFILE OUTFILE fileout fileout INFILE INFILE OUTFILE OUTFILE iflag oflag iflag noxfer ucase lcase ucase lcase nocreat nocreat notrunc noerror IFLAG IFLAG iflag iflag fullblock oflag dsync syncronized syncronized nonblock noatime nocache noctty nofollow notrunc OFLAG OFLAG oflag notrunc dsync syncronized syncronized nonblock noatime nocache noctty nofollow T0DO) + #[cfg(test)] mod unit_tests; @@ -127,6 +129,7 @@ impl std::str::FromStr for ConvFlag { } } +#[derive(Debug, PartialEq)] enum Flag { // Input only FullBlock, @@ -317,9 +320,7 @@ fn parse_multiplier<'a>(s: &'a str) -> Result { }; mult.try_into() - .map_err(|_e| { - ParseError::MultiplierStringWouldOverflow(s.to_string()) - }) + .map_err(|_e| ParseError::MultiplierStringWouldOverflow(s.to_string())) } fn parse_bytes_only(s: &str) -> Result { diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 2c8954cec..96025c064 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -1,6 +1,6 @@ use super::*; -use crate::{build_dd_app, StatusLevel}; +use crate::StatusLevel; #[cfg(not(unix))] #[test] @@ -23,7 +23,7 @@ fn unimplemented_flags_should_error_non_unix() { format!("--iflag={}", flag), format!("--oflag={}", flag), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); match parse_iflags(&matches) { Ok(_) => unfailed.push(format!("iflag={}", flag)), @@ -54,7 +54,7 @@ fn unimplemented_flags_should_error() { format!("--iflag={}", flag), format!("--oflag={}", flag), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); match parse_iflags(&matches) { Ok(_) => unfailed.push(format!("iflag={}", flag)), @@ -82,7 +82,7 @@ fn test_status_level_absent() { String::from("--of=bar.file"), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap(); assert_eq!(st, None); @@ -97,7 +97,7 @@ fn test_status_level_none() { String::from("--of=bar.file"), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap().unwrap(); assert_eq!(st, StatusLevel::None); @@ -112,7 +112,7 @@ fn test_status_level_progress() { String::from("--status=progress"), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap().unwrap(); assert_eq!(st, StatusLevel::Progress); @@ -127,7 +127,7 @@ fn test_status_level_noxfer() { String::from("--of=bar.file"), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let st = parse_status_level(&matches).unwrap().unwrap(); assert_eq!(st, StatusLevel::Noxfer); @@ -140,7 +140,7 @@ fn test_status_level_noxfer() { fn icf_ctable_error() { let args = vec![String::from("dd"), String::from("--conv=ascii,ebcdic,ibm")]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let _ = parse_conv_flag_input(&matches).unwrap(); } @@ -150,7 +150,7 @@ fn icf_ctable_error() { fn icf_case_error() { let args = vec![String::from("dd"), String::from("--conv=ucase,lcase")]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let _ = parse_conv_flag_input(&matches).unwrap(); } @@ -160,7 +160,7 @@ fn icf_case_error() { fn icf_block_error() { let args = vec![String::from("dd"), String::from("--conv=block,unblock")]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let _ = parse_conv_flag_input(&matches).unwrap(); } @@ -170,7 +170,7 @@ fn icf_block_error() { fn icf_creat_error() { let args = vec![String::from("dd"), String::from("--conv=excl,nocreat")]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let _ = parse_conv_flag_output(&matches).unwrap(); } @@ -180,7 +180,7 @@ fn parse_icf_token_ibm() { let exp = vec![ConvFlag::FmtAtoI]; let args = vec![String::from("dd"), String::from("--conv=ibm")]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let act = parse_flag_list::("conv", &matches).unwrap(); @@ -198,7 +198,7 @@ fn parse_icf_tokens_elu() { String::from("dd"), String::from("--conv=ebcdic,lcase,unblock"), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let act = parse_flag_list::("conv", &matches).unwrap(); assert_eq!(exp.len(), act.len()); @@ -229,7 +229,7 @@ fn parse_icf_tokens_remaining() { String::from("dd"), String::from("--conv=ascii,ucase,block,sparse,swab,sync,noerror,excl,nocreat,notrunc,noerror,fdatasync,fsync"), ]; - let matches = build_dd_app!().get_matches_from_safe(args).unwrap(); + let matches = uu_app().get_matches_from_safe(args).unwrap(); let act = parse_flag_list::("conv", &matches).unwrap(); @@ -239,6 +239,82 @@ fn parse_icf_tokens_remaining() { } } +#[test] +fn parse_iflag_tokens() { + let exp = vec![ + Flag::FullBlock, + Flag::CountBytes, + Flag::SkipBytes, + // Flag::Cio, + Flag::Direct, + Flag::Directory, + Flag::Dsync, + Flag::Sync, + // Flag::NoCache, + Flag::NonBlock, + Flag::NoATime, + Flag::NoCtty, + Flag::NoFollow, + // Flag::NoLinks, + // Flag::Binary, + // Flag::Text, + Flag::Append, + Flag::SeekBytes, + ]; + + let args = vec![ + String::from("dd"), + String::from("--iflag=fullblock,count_bytes,skip_bytes,direct,directory,dsync,sync,nonblock,noatime,noctty,nofollow,append,seek_bytes"), + // String::from("--iflag=fullblock,count_bytes,skip_bytes,cio,direct,directory,dsync,sync,nocache,nonblock,noatime,noctty,nofollow,nolinks,binary,text,append,seek_bytes"), + ]; + let matches = uu_app().get_matches_from_safe(args).unwrap(); + + let act = parse_flag_list::("iflag", &matches).unwrap(); + + assert_eq!(exp.len(), act.len()); + for cf in &exp { + assert!(exp.contains(&cf)); + } +} + +#[test] +fn parse_oflag_tokens() { + let exp = vec![ + Flag::FullBlock, + Flag::CountBytes, + Flag::SkipBytes, + // Flag::Cio, + Flag::Direct, + Flag::Directory, + Flag::Dsync, + Flag::Sync, + // Flag::NoCache, + Flag::NonBlock, + Flag::NoATime, + Flag::NoCtty, + Flag::NoFollow, + // Flag::NoLinks, + // Flag::Binary, + // Flag::Text, + Flag::Append, + Flag::SeekBytes, + ]; + + let args = vec![ + String::from("dd"), + String::from("--oflag=fullblock,count_bytes,skip_bytes,direct,directory,dsync,sync,nonblock,noatime,noctty,nofollow,append,seek_bytes"), + // String::from("--oflag=fullblock,count_bytes,skip_bytes,cio,direct,directory,dsync,sync,nocache,nonblock,noatime,noctty,nofollow,nolinks,binary,text,append,seek_bytes"), + ]; + let matches = uu_app().get_matches_from_safe(args).unwrap(); + + let act = parse_flag_list::("oflag", &matches).unwrap(); + + assert_eq!(exp.len(), act.len()); + for cf in &exp { + assert!(exp.contains(&cf)); + } +} + // ----- Multiplier Strings etc. ----- macro_rules! test_byte_parser ( ( $test_name:ident, $bs_str:expr, $bs:expr ) => diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index de085bf11..b48069588 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,53 +1,41 @@ use crate::common::util::*; +use std::fs::{File, OpenOptions}; use std::io::{BufReader, Read, Write}; -use std::path::{PathBuf}; -use std::fs::{OpenOptions, File}; +use std::path::PathBuf; use tempfile::tempfile; -macro_rules! inf -{ - ($fname:expr) => - {{ +macro_rules! inf { + ($fname:expr) => {{ &format!("if={}", $fname) }}; } -macro_rules! of -{ - ($fname:expr) => - {{ +macro_rules! of { + ($fname:expr) => {{ &format!("of={}", $fname) }}; } -macro_rules! fixture_path -{ - ($fname:expr) => - {{ +macro_rules! fixture_path { + ($fname:expr) => {{ PathBuf::from(format!("./tests/fixtures/dd/{}", $fname)) }}; } -macro_rules! assert_fixture_exists -{ - ($fname:expr) => - {{ +macro_rules! assert_fixture_exists { + ($fname:expr) => {{ let fpath = fixture_path!($fname); - if !fpath.exists() - { + if !fpath.exists() { panic!("Fixture missing: {:?}", fpath); } }}; } -macro_rules! assert_fixture_not_exists -{ - ($fname:expr) => - {{ +macro_rules! assert_fixture_not_exists { + ($fname:expr) => {{ let fpath = PathBuf::from(format!("./fixtures/dd/{}", $fname)); - if fpath.exists() - { + if fpath.exists() { panic!("Fixture present: {:?}", fpath); } }}; @@ -84,34 +72,23 @@ macro_rules! cmp_file ( }; ); -fn build_ascii_block(n: usize) -> Vec -{ - (0..=127) - .cycle() - .take(n) - .collect() +fn build_ascii_block(n: usize) -> Vec { + (0..=127).cycle().take(n).collect() } // Sanity Tests #[test] -fn version() -{ - new_ucmd!() - .args(&["--version"]) - .succeeds(); +fn version() { + new_ucmd!().args(&["--version"]).succeeds(); } #[test] -fn help() -{ - new_ucmd!() - .args(&["--help"]) - .succeeds(); +fn help() { + new_ucmd!().args(&["--help"]).succeeds(); } #[test] -fn test_stdin_stdout() -{ +fn test_stdin_stdout() { let input = build_ascii_block(521); let output = String::from_utf8(input.clone()).unwrap(); new_ucmd!() @@ -125,17 +102,12 @@ fn test_stdin_stdout() // Top-Level Items // count=N, skip=N, status=LEVEL, conv=FLAG, *flag=FLAG #[test] -fn test_stdin_stdout_count() -{ +fn test_stdin_stdout_count() { let input = build_ascii_block(521); let mut output = String::from_utf8(input.clone()).unwrap(); output.truncate(256); new_ucmd!() - .args(&[ - "status=none", - "count=2", - "ibs=128", - ]) + .args(&["status=none", "count=2", "ibs=128"]) .pipe_in(input) .run() .no_stderr() @@ -143,17 +115,12 @@ fn test_stdin_stdout_count() } #[test] -fn test_stdin_stdout_count_bytes() -{ +fn test_stdin_stdout_count_bytes() { let input = build_ascii_block(521); let mut output = String::from_utf8(input.clone()).unwrap(); output.truncate(256); new_ucmd!() - .args(&[ - "status=none", - "count=256", - "iflag=count_bytes", - ]) + .args(&["status=none", "count=256", "iflag=count_bytes"]) .pipe_in(input) .run() .no_stderr() @@ -161,17 +128,12 @@ fn test_stdin_stdout_count_bytes() } #[test] -fn test_stdin_stdout_skip() -{ +fn test_stdin_stdout_skip() { let input = build_ascii_block(521); let mut output = String::from_utf8(input.clone()).unwrap(); let _ = output.drain(..256); new_ucmd!() - .args(&[ - "status=none", - "skip=2", - "ibs=128", - ]) + .args(&["status=none", "skip=2", "ibs=128"]) .pipe_in(input) .run() .no_stderr() @@ -179,18 +141,12 @@ fn test_stdin_stdout_skip() } #[test] -fn test_stdin_stdout_skip_bytes() -{ +fn test_stdin_stdout_skip_bytes() { let input = build_ascii_block(521); let mut output = String::from_utf8(input.clone()).unwrap(); let _ = output.drain(..256); new_ucmd!() - .args(&[ - "status=none", - "skip=256", - "ibs=128", - "iflag=skip_bytes", - ]) + .args(&["status=none", "skip=256", "ibs=128", "iflag=skip_bytes"]) .pipe_in(input) .run() .no_stderr() @@ -198,16 +154,11 @@ fn test_stdin_stdout_skip_bytes() } #[test] -fn test_stdin_stdout_skip_w_multiplier() -{ - let input = build_ascii_block(10*1024); - let output = String::from_utf8(input[5*1024..].to_vec()).unwrap(); +fn test_stdin_stdout_skip_w_multiplier() { + let input = build_ascii_block(10 * 1024); + let output = String::from_utf8(input[5 * 1024..].to_vec()).unwrap(); new_ucmd!() - .args(&[ - "status=none", - "skip=5K", - "iflag=skip_bytes" - ]) + .args(&["status=none", "skip=5K", "iflag=skip_bytes"]) .pipe_in(input) .run() .no_stderr() @@ -216,16 +167,11 @@ fn test_stdin_stdout_skip_w_multiplier() } #[test] -fn test_stdin_stdout_count_w_multiplier() -{ - let input = build_ascii_block(5*1024); - let output = String::from_utf8(input[..2*1024].to_vec()).unwrap(); +fn test_stdin_stdout_count_w_multiplier() { + let input = build_ascii_block(5 * 1024); + let output = String::from_utf8(input[..2 * 1024].to_vec()).unwrap(); new_ucmd!() - .args(&[ - "status=none", - "count=2KiB", - "iflag=count_bytes", - ]) + .args(&["status=none", "count=2KiB", "iflag=count_bytes"]) .pipe_in(input) .run() .no_stderr() @@ -234,133 +180,93 @@ fn test_stdin_stdout_count_w_multiplier() } #[test] -fn test_final_stats_noxfer() -{ +fn test_final_stats_noxfer() { new_ucmd!() - .args(&[ - "status=noxfer", - ]) + .args(&["status=noxfer"]) .succeeds() .stderr_only(""); } #[test] -fn test_final_stats_unspec() -{ +fn test_final_stats_unspec() { let output = vec![ "0+0 records in", "0+0 records out", "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s", ]; - let output = output.into_iter() - .fold(String::new(), | mut acc, s | { - acc.push_str(s); - acc.push('\n'); - acc - }); - new_ucmd!() - .run() - .stderr_only(&output) - .success(); + let output = output.into_iter().fold(String::new(), |mut acc, s| { + acc.push_str(s); + acc.push('\n'); + acc + }); + new_ucmd!().run().stderr_only(&output).success(); } #[test] -fn test_excl_causes_failure_when_present() -{ +fn test_excl_causes_failure_when_present() { let fname = "this-file-exists-excl.txt"; assert_fixture_exists!(&fname); let (_fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "of=this-file-exists-excl.txt", - "conv=excl", - ]) + ucmd.args(&["of=this-file-exists-excl.txt", "conv=excl"]) .fails(); } #[test] -fn test_atime_updated() -{ +fn test_atime_updated() { let fname = "this-file-exists-no-noatime.txt"; assert_fixture_exists!(&fname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - inf!(fname), - ]); + ucmd.args(&["status=none", inf!(fname)]); let pre_atime = fix.metadata(&fname).accessed().unwrap(); - ucmd.pipe_in("") - .run() - .no_stderr() - .success(); + ucmd.pipe_in("").run().no_stderr().success(); let post_atime = fix.metadata(&fname).accessed().unwrap(); assert!(pre_atime != post_atime); } #[test] -fn test_noatime_does_not_update_infile_atime() -{ +fn test_noatime_does_not_update_infile_atime() { let fname = "this-ifile-exists-noatime.txt"; assert_fixture_exists!(&fname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - "iflag=noatime", - inf!(fname), - ]); + ucmd.args(&["status=none", "iflag=noatime", inf!(fname)]); let pre_atime = fix.metadata(&fname).accessed().unwrap(); - ucmd.run() - .no_stderr() - .success(); + ucmd.run().no_stderr().success(); let post_atime = fix.metadata(&fname).accessed().unwrap(); assert_eq!(pre_atime, post_atime); } #[test] -fn test_noatime_does_not_update_ofile_atime() -{ +fn test_noatime_does_not_update_ofile_atime() { let fname = "this-ofile-exists-noatime.txt"; assert_fixture_exists!(&fname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - "oflag=noatime", - of!(fname), - ]); + ucmd.args(&["status=none", "oflag=noatime", of!(fname)]); let pre_atime = fix.metadata(&fname).accessed().unwrap(); - ucmd.pipe_in("") - .run() - .no_stderr() - .success(); + ucmd.pipe_in("").run().no_stderr().success(); let post_atime = fix.metadata(&fname).accessed().unwrap(); assert_eq!(pre_atime, post_atime); } #[test] -fn test_nocreat_causes_failure_when_outfile_not_present() -{ +fn test_nocreat_causes_failure_when_outfile_not_present() { let fname = "this-file-does-not-exist.txt"; assert_fixture_not_exists!(&fname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "conv=nocreat", - of!(&fname), - ]) - .pipe_in("") - .run(); + ucmd.args(&["conv=nocreat", of!(&fname)]).pipe_in("").run(); assert!(!fix.file_exists(&fname)); @@ -368,25 +274,17 @@ fn test_nocreat_causes_failure_when_outfile_not_present() } #[test] -fn test_notrunc_does_not_truncate() -{ +fn test_notrunc_does_not_truncate() { // Set up test if needed (eg. after failure) let fname = "this-file-exists-notrunc.txt"; let fpath = fixture_path!(fname); - match fpath.metadata() - { - Ok(m) if m.len() == 256 => {}, - _ => - build_test_file!(&fpath, &build_ascii_block(256)), + match fpath.metadata() { + Ok(m) if m.len() == 256 => {} + _ => build_test_file!(&fpath, &build_ascii_block(256)), } let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - "conv=notrunc", - of!(&fname), - "if=null.txt", - ]) + ucmd.args(&["status=none", "conv=notrunc", of!(&fname), "if=null.txt"]) .run() .no_stdout() .no_stderr() @@ -396,24 +294,17 @@ fn test_notrunc_does_not_truncate() } #[test] -fn test_existing_file_truncated() -{ +fn test_existing_file_truncated() { // Set up test if needed (eg. after failure) let fname = "this-file-exists-truncated.txt"; let fpath = fixture_path!(fname); - match fpath.metadata() - { - Ok(m) if m.len() == 256 => {}, - _ => - build_test_file!(&fpath, &vec![0; 256]), + match fpath.metadata() { + Ok(m) if m.len() == 256 => {} + _ => build_test_file!(&fpath, &vec![0; 256]), } let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - "if=null.txt", - of!(fname), - ]) + ucmd.args(&["status=none", "if=null.txt", of!(fname)]) .run() .no_stdout() .no_stderr() @@ -423,37 +314,28 @@ fn test_existing_file_truncated() } #[test] -fn test_null_stats() -{ +fn test_null_stats() { let stats = vec![ "0+0 records in\n", "0+0 records out\n", "0 bytes (0 B, 0 B) copied, 0.0 s, 0 B/s\n", ]; - let stats = stats.into_iter() - .fold(String::new(), | mut acc, s | { - acc.push_str(s); - acc - }); + let stats = stats.into_iter().fold(String::new(), |mut acc, s| { + acc.push_str(s); + acc + }); new_ucmd!() - .args(&[ - "if=null.txt", - ]) + .args(&["if=null.txt"]) .run() .stderr_only(stats) .success(); } #[test] -fn test_null_fullblock() -{ +fn test_null_fullblock() { new_ucmd!() - .args(&[ - "if=null.txt", - "status=none", - "iflag=fullblock", - ]) + .args(&["if=null.txt", "status=none", "iflag=fullblock"]) .run() .no_stdout() .no_stderr() @@ -463,8 +345,7 @@ fn test_null_fullblock() #[cfg(unix)] // #[ignore] // See note below before running this test! #[test] -fn test_fullblock() -{ +fn test_fullblock() { let tname = "fullblock-from-urand"; let tmp_fn = format!("TESTFILE-{}.tmp", &tname); let exp_stats = vec![ @@ -472,11 +353,10 @@ fn test_fullblock() "1+0 records out\n", "134217728 bytes (134 MB, 128 MiB) copied,", ]; - let exp_stats = exp_stats.into_iter() - .fold(Vec::new(), | mut acc, s | { - acc.extend(s.bytes()); - acc - }); + let exp_stats = exp_stats.into_iter().fold(Vec::new(), |mut acc, s| { + acc.extend(s.bytes()); + acc + }); let ucmd = new_ucmd!() .args(&[ @@ -491,7 +371,8 @@ fn test_fullblock() // a reasonable value for testing most systems. "count=1", "iflag=fullblock", - ]).run(); + ]) + .run(); ucmd.success(); let run_stats = &ucmd.stderr()[..exp_stats.len()]; @@ -500,20 +381,12 @@ fn test_fullblock() // Fileio #[test] -fn test_ys_to_stdout() -{ - let output: Vec<_> = String::from("y\n") - .bytes() - .cycle() - .take(1024) - .collect(); +fn test_ys_to_stdout() { + let output: Vec<_> = String::from("y\n").bytes().cycle().take(1024).collect(); let output = String::from_utf8(output).unwrap(); new_ucmd!() - .args(&[ - "status=none", - "if=y-nl-1k.txt", - ]) + .args(&["status=none", "if=y-nl-1k.txt"]) .run() .no_stderr() .stdout_is(output) @@ -521,15 +394,11 @@ fn test_ys_to_stdout() } #[test] -fn test_zeros_to_stdout() -{ - let output = vec![0; 256*1024]; +fn test_zeros_to_stdout() { + let output = vec![0; 256 * 1024]; let output = String::from_utf8(output).unwrap(); new_ucmd!() - .args(&[ - "status=none", - "if=zero-256k.txt", - ]) + .args(&["status=none", "if=zero-256k.txt"]) .run() .no_stderr() .stdout_is(output) @@ -537,22 +406,12 @@ fn test_zeros_to_stdout() } #[test] -fn test_to_stdout_with_ibs_obs() -{ - let output: Vec<_> = String::from("y\n") - .bytes() - .cycle() - .take(1024) - .collect(); +fn test_to_stdout_with_ibs_obs() { + let output: Vec<_> = String::from("y\n").bytes().cycle().take(1024).collect(); let output = String::from_utf8(output).unwrap(); new_ucmd!() - .args(&[ - "status=none", - "if=y-nl-1k.txt", - "ibs=521", - "obs=1031", - ]) + .args(&["status=none", "if=y-nl-1k.txt", "ibs=521", "obs=1031"]) .run() .no_stderr() .stdout_is(output) @@ -560,17 +419,13 @@ fn test_to_stdout_with_ibs_obs() } #[test] -fn test_ascii_10k_to_stdout() -{ - let output = build_ascii_block(1024*1024); +fn test_ascii_10k_to_stdout() { + let output = build_ascii_block(1024 * 1024); // build_test_file!("ascii-10k.txt", &output); let output = String::from_utf8(output).unwrap(); new_ucmd!() - .args(&[ - "status=none", - "if=ascii-10k.txt", - ]) + .args(&["status=none", "if=ascii-10k.txt"]) .run() .no_stderr() .stdout_is(output) @@ -578,30 +433,26 @@ fn test_ascii_10k_to_stdout() } #[test] -fn test_zeros_to_file() -{ +fn test_zeros_to_file() { let tname = "zero-256k"; let test_fn = format!("{}.txt", tname); let tmp_fn = format!("TESTFILE-{}.tmp", &tname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - inf!(test_fn), - of!(tmp_fn), - ]) - .run() - .no_stderr() - .no_stdout() - .success(); + ucmd.args(&["status=none", inf!(test_fn), of!(tmp_fn)]) + .run() + .no_stderr() + .no_stdout() + .success(); - cmp_file!(File::open(fixture_path!(&test_fn)).unwrap(), - fix.open(&tmp_fn)); + cmp_file!( + File::open(fixture_path!(&test_fn)).unwrap(), + fix.open(&tmp_fn) + ); } #[test] -fn test_to_file_with_ibs_obs() -{ +fn test_to_file_with_ibs_obs() { let tname = "zero-256k"; let test_fn = format!("{}.txt", tname); let tmp_fn = format!("TESTFILE-{}.tmp", &tname); @@ -614,46 +465,47 @@ fn test_to_file_with_ibs_obs() "ibs=222", "obs=111", ]) - .run() - .no_stderr() - .no_stdout() - .success(); + .run() + .no_stderr() + .no_stdout() + .success(); - cmp_file!(File::open(fixture_path!(&test_fn)).unwrap(), - fix.open(&tmp_fn)); + cmp_file!( + File::open(fixture_path!(&test_fn)).unwrap(), + fix.open(&tmp_fn) + ); } #[test] -fn test_ascii_521k_to_file() -{ +fn test_ascii_521k_to_file() { let tname = "ascii-521k"; - let input = build_ascii_block(512*1024); + let input = build_ascii_block(512 * 1024); let tmp_fn = format!("TESTFILE-{}.tmp", &tname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - of!(tmp_fn), - ]) + ucmd.args(&["status=none", of!(tmp_fn)]) .pipe_in(input.clone()) .run() .no_stderr() .no_stdout() .success(); - assert_eq!(512*1024, fix.metadata(&tmp_fn).len()); + assert_eq!(512 * 1024, fix.metadata(&tmp_fn).len()); - cmp_file!({ let mut input_f = tempfile().unwrap(); - input_f.write(&input).unwrap(); - input_f }, - fix.open(&tmp_fn)); + cmp_file!( + { + let mut input_f = tempfile().unwrap(); + input_f.write(&input).unwrap(); + input_f + }, + fix.open(&tmp_fn) + ); } #[ignore] #[cfg(unix)] #[test] -fn test_ascii_5_gibi_to_file() -{ +fn test_ascii_5_gibi_to_file() { let tname = "ascii-5G"; let tmp_fn = format!("TESTFILE-{}.tmp", &tname); @@ -665,37 +517,28 @@ fn test_ascii_5_gibi_to_file() "if=/dev/zero", of!(tmp_fn), ]) - .run() - .no_stderr() - .no_stdout() - .success(); + .run() + .no_stderr() + .no_stdout() + .success(); - assert_eq!(5*1024*1024*1024, fix.metadata(&tmp_fn).len()); + assert_eq!(5 * 1024 * 1024 * 1024, fix.metadata(&tmp_fn).len()); } #[test] -fn test_self_transfer() -{ +fn test_self_transfer() { let fname = "self-transfer-256k.txt"; let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - "conv=notrunc", - inf!(fname), - of!(fname), - ]); + ucmd.args(&["status=none", "conv=notrunc", inf!(fname), of!(fname)]); assert!(fix.file_exists(fname)); - assert_eq!(256*1024, fix.metadata(fname).len()); + assert_eq!(256 * 1024, fix.metadata(fname).len()); - ucmd.run() - .no_stdout() - .no_stderr() - .success(); + ucmd.run().no_stdout().no_stderr().success(); assert!(fix.file_exists(fname)); - assert_eq!(256*1024, fix.metadata(fname).len()); + assert_eq!(256 * 1024, fix.metadata(fname).len()); } // conv=[ascii,ebcdic,ibm], conv=[ucase,lcase], conv=[block,unblock], conv=sync From d2cebad931e6e5e2a33c56d377f0d48a84420d08 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 6 Jul 2021 15:35:48 -0700 Subject: [PATCH 46/66] addresses a few more issues from PR#2474 - adds project header to multiple files - updates spell check skip words - removes linux only flags direct,noatime from mac_os build - applies rustfmt to test_dd --- src/uu/dd/src/conversion_tables.rs | 12 +++++++- src/uu/dd/src/datastructures.rs | 28 +++++++++---------- src/uu/dd/src/dd.rs | 13 ++++----- .../src/dd_unit_tests/block_unblock_tests.rs | 2 ++ .../dd/src/dd_unit_tests/conv_sync_tests.rs | 2 ++ .../dd/src/dd_unit_tests/conversion_tests.rs | 2 ++ src/uu/dd/src/dd_unit_tests/mod.rs | 2 ++ src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 2 ++ src/uu/dd/src/parseargs/unit_tests.rs | 2 +- tests/by-util/test_dd.rs | 2 ++ 10 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs index a3b9a81c0..9258e0515 100644 --- a/src/uu/dd/src/conversion_tables.rs +++ b/src/uu/dd/src/conversion_tables.rs @@ -1,6 +1,16 @@ -// Conversion tables are just lookup tables. +// This file is part of the uutils coreutils package. +// +// (c) Tyler Steele +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// spell-checker:ignore (UCASE LCASE) + +// Note: Conversion tables are just lookup tables. // eg. The ASCII->EBCDIC table stores the EBCDIC code at the index // obtained by treating the ASCII representation as a number. + pub type ConversionTable = [u8; 256]; pub const ASCII_UCASE_TO_LCASE: ConversionTable = [ diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index d8459e193..c7696c160 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) ctable noerror +// spell-checker:ignore (TODO ctable noerror) use crate::conversion_tables::*; @@ -179,17 +179,17 @@ impl std::fmt::Display for InternalError { impl Error for InternalError {} pub mod options { - pub const INFILE: &'static str = "if"; - pub const OUTFILE: &'static str = "of"; - pub const IBS: &'static str = "ibs"; - pub const OBS: &'static str = "obs"; - pub const BS: &'static str = "bs"; - pub const CBS: &'static str = "cbs"; - pub const COUNT: &'static str = "count"; - pub const SKIP: &'static str = "skip"; - pub const SEEK: &'static str = "seek"; - pub const STATUS: &'static str = "status"; - pub const CONV: &'static str = "conv"; - pub const IFLAG: &'static str = "iflag"; - pub const OFLAG: &'static str = "oflag"; + pub const INFILE: &str = "if"; + pub const OUTFILE: &str = "of"; + pub const IBS: &str = "ibs"; + pub const OBS: &str = "obs"; + pub const BS: &str = "bs"; + pub const CBS: &str = "cbs"; + pub const COUNT: &str = "count"; + pub const SKIP: &str = "skip"; + pub const SEEK: &str = "seek"; + pub const STATUS: &str = "status"; + pub const CONV: &str = "conv"; + pub const IFLAG: &str = "iflag"; + pub const OFLAG: &str = "oflag"; } diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index fcd1637c5..6a9b58186 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -5,7 +5,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (parseargs xfer cflags iflags parseargs parseargs xfer cflags iflags iflags iflags xfer cflags oflags oflags oflags oflags dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK xfer cflags fname fname tlen rlen fullblock rlen tlen tlen noerror rlen rlen remaing plen plen plen plen oflag dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK fname notrunc nocreat fname wlen wlen wlen wlen rstat rstat curr curr curr curr rposition rstat ctable ctable rstat ctable ctable btotal btotal btotal btotal SIGUSR SIGUSR sigval SIGINFO SIGINFO sigval SIGINFO SIGINFO SIGUSR sigval SIGUSR permenantly sigval itegral itegral wstat rmax rmax rmax rsofar rremain rmax rsofar rremain bmax bmax bmax bremain bmax wstat bremain wstat wstat opertaions Noxfer opertaions fileout Noxfer INFILE OUTFILE fileout fileout INFILE INFILE OUTFILE OUTFILE iflag oflag iflag noxfer ucase lcase ucase lcase nocreat nocreat notrunc noerror IFLAG IFLAG iflag iflag fullblock oflag dsync syncronized syncronized nonblock noatime nocache noctty nofollow notrunc OFLAG OFLAG oflag notrunc dsync syncronized syncronized nonblock noatime nocache noctty nofollow T0DO) +// spell-checker:ignore (bmax bremain btotal cflags ctable curr dsync DSYNC fileout fname fullblock iflags INFILE noatime NOATIME nocreat noctty NOCTTY noerror nofollow NOFOLLOW nonblock NONBLOCK notrunc Noxfer oflag oflags OUTFILE parseargs plen rlen rmax rposition rremain rsofar rstat SIGINFO SIGUSR sigval tlen wlen wstat xfer) #[macro_use] extern crate uucore; @@ -27,8 +27,6 @@ use byte_unit::Byte; use clap::{self, crate_version}; use debug_print::debug_println; use gcd::Gcd; -#[cfg(unix)] -use libc; use signal_hook::consts::signal; use std::cmp; use std::convert::TryInto; @@ -139,10 +137,9 @@ impl Input { let mut opts = OpenOptions::new(); opts.read(true); - if cfg!(unix) { - if let Some(libc_flags) = make_unix_iflags(&iflags) { - opts.custom_flags(libc_flags); - } + #[cfg(target_os = "linux")] + if let Some(libc_flags) = make_unix_iflags(&iflags) { + opts.custom_flags(libc_flags); } opts.open(fname)? @@ -783,7 +780,7 @@ fn print_xfer_stats(update: &ProgUpdate) { fn gen_prog_updater( rx: mpsc::Receiver, xfer_stats: Option, -) -> impl Fn() -> () { +) -> impl Fn() { // -------------------------------------------------------------- fn posixly_correct() -> bool { env::var("POSIXLY_CORRECT").is_ok() diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index f475754a5..036f87130 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) + use super::*; const NL: u8 = '\n' as u8; diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 93ea8e34d..b2b45843c 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) + use super::*; macro_rules! make_sync_test ( diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 96e35445e..6fb7d1e26 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) + use super::*; macro_rules! make_conv_test ( diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index ee28d4fd9..d177319a7 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) + use super::*; mod block_unblock_tests; diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index c5ed825bd..2e91fb2cb 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) + use super::*; const DST_PLACEHOLDER: Vec = Vec::new(); diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 96025c064..f2019c3cd 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -2,7 +2,7 @@ use super::*; use crate::StatusLevel; -#[cfg(not(unix))] +#[cfg(not(target_os = "linux"))] #[test] fn unimplemented_flags_should_error_non_unix() { let mut unfailed = Vec::new(); diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index b48069588..ae23e963e 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) + use crate::common::util::*; use std::fs::{File, OpenOptions}; From 74e0071cfef99fa84c7ad26c4b94423e257cb88a Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 6 Jul 2021 17:47:26 -0700 Subject: [PATCH 47/66] fixes conv=nocreat test failure --- src/uu/dd/src/dd.rs | 13 ++++--------- src/uu/dd/src/parseargs/unit_tests.rs | 2 +- tests/by-util/test_dd.rs | 3 +-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 6a9b58186..f93a2b309 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -366,14 +366,12 @@ impl Output { .create_new(cflags.excl) .append(oflags.append); - if cfg!(unix) { - if let Some(libc_flags) = make_unix_oflags(oflags) { - opts.custom_flags(libc_flags); - } + #[cfg(target_os = "linux")] + if let Some(libc_flags) = make_unix_oflags(oflags) { + opts.custom_flags(libc_flags); } let dst = opts.open(path)?; - Ok(dst) } let obs = parseargs::parse_obs(matches)?; @@ -777,10 +775,7 @@ fn print_xfer_stats(update: &ProgUpdate) { } /// Generate a progress updater that tracks progress, receives updates, and responds to signals. -fn gen_prog_updater( - rx: mpsc::Receiver, - xfer_stats: Option, -) -> impl Fn() { +fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option) -> impl Fn() { // -------------------------------------------------------------- fn posixly_correct() -> bool { env::var("POSIXLY_CORRECT").is_ok() diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index f2019c3cd..79a04482b 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -7,7 +7,7 @@ use crate::StatusLevel; fn unimplemented_flags_should_error_non_unix() { let mut unfailed = Vec::new(); - // The following flags are only implemented in unix + // The following flags are only implemented in linux for flag in vec![ "direct", "directory", diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index ae23e963e..a3876e2d8 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -268,11 +268,10 @@ fn test_nocreat_causes_failure_when_outfile_not_present() { assert_fixture_not_exists!(&fname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&["conv=nocreat", of!(&fname)]).pipe_in("").run(); + ucmd.args(&["conv=nocreat", of!(&fname)]).pipe_in("").fails().stderr_is("dd Error: No such file or directory (os error 2)"); assert!(!fix.file_exists(&fname)); - ucmd.fails(); } #[test] From 2e9e984b3a3a1337d6df3c9d62f8fbacaae5a16c Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 6 Jul 2021 18:17:30 -0700 Subject: [PATCH 48/66] Adds test with unicode filename Filenames should be the only spot where unicode can appear in dd's cli. --- tests/by-util/test_dd.rs | 27 +++++++++++++++++++++++++++ tests/fixtures/dd/😎💚🦊.txt | 0 2 files changed, 27 insertions(+) create mode 100644 tests/fixtures/dd/😎💚🦊.txt diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index a3876e2d8..0c78d13af 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -438,6 +438,7 @@ fn test_zeros_to_file() { let tname = "zero-256k"; let test_fn = format!("{}.txt", tname); let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + assert_fixture_exists!(test_fn); let (fix, mut ucmd) = at_and_ucmd!(); ucmd.args(&["status=none", inf!(test_fn), of!(tmp_fn)]) @@ -457,6 +458,7 @@ fn test_to_file_with_ibs_obs() { let tname = "zero-256k"; let test_fn = format!("{}.txt", tname); let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + assert_fixture_exists!(test_fn); let (fix, mut ucmd) = at_and_ucmd!(); ucmd.args(&[ @@ -529,6 +531,7 @@ fn test_ascii_5_gibi_to_file() { #[test] fn test_self_transfer() { let fname = "self-transfer-256k.txt"; + assert_fixture_exists!(fname); let (fix, mut ucmd) = at_and_ucmd!(); ucmd.args(&["status=none", "conv=notrunc", inf!(fname), of!(fname)]); @@ -542,5 +545,29 @@ fn test_self_transfer() { assert_eq!(256 * 1024, fix.metadata(fname).len()); } +#[test] +fn test_unicode_filenames() { + let tname = "😎💚🦊"; + let test_fn = format!("{}.txt", tname); + let tmp_fn = format!("TESTFILE-{}.tmp", &tname); + assert_fixture_exists!(test_fn); + + let (fix, mut ucmd) = at_and_ucmd!(); + ucmd.args(&[ + "status=none", + inf!(test_fn), + of!(tmp_fn), + ]) + .run() + .no_stderr() + .no_stdout() + .success(); + + cmp_file!( + File::open(fixture_path!(&test_fn)).unwrap(), + fix.open(&tmp_fn) + ); +} + // conv=[ascii,ebcdic,ibm], conv=[ucase,lcase], conv=[block,unblock], conv=sync // TODO: Move conv tests from unit test module diff --git a/tests/fixtures/dd/😎💚🦊.txt b/tests/fixtures/dd/😎💚🦊.txt new file mode 100644 index 000000000..e69de29bb From 4f7dda4be96e5128837aa60569b9744ab29e21d8 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 7 Jul 2021 17:27:47 -0700 Subject: [PATCH 49/66] Completes final? issues barring PR - Adds cspell.json. Hopefully this will make you happy, spellchecker. - Removes non-functional spellchecker-ignore tags - Adds a sleep call to the no noatime test. Some systems were did not notice a changed atime without the option present. - Adds a test with a unicode filename. - Addresses clippy lints and rustfmt issues. --- src/uu/dd/cspell.json | 44 +++++ src/uu/dd/src/conversion_tables.rs | 2 +- src/uu/dd/src/datastructures.rs | 4 +- src/uu/dd/src/dd.rs | 257 +++++------------------------ src/uu/dd/src/parseargs.rs | 31 ++-- tests/by-util/test_dd.rs | 8 +- 6 files changed, 112 insertions(+), 234 deletions(-) create mode 100644 src/uu/dd/cspell.json diff --git a/src/uu/dd/cspell.json b/src/uu/dd/cspell.json new file mode 100644 index 000000000..91b041d12 --- /dev/null +++ b/src/uu/dd/cspell.json @@ -0,0 +1,44 @@ +{ + "version": "0.1", + "language": "en-CA", + "words": [ + "ucase", + "lcase", + "nocreat", + "noerror", + "notrunc", + "nocache", + "dsync", + "sync", + "nonblock", + "noctty", + "nofollow", + "nolinks", + "fullblock", + "noxfer", + "iflag", + "oflag", + "infile", + "outfile", + "fileio", + "urand", + "xfer", + "cflags", + "ctable", + "gnudd", + "atoe", + "etoa", + "atoibm", + "bmax", + "datastructures", + "creat", + "mult", + "unfailed", + "behaviour", + "ctty", + ], + "ignorePaths": [ + "test-fixtures/*.test", + "test-fixtures/*.spec" + ] +} diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs index 9258e0515..1130a7b5b 100644 --- a/src/uu/dd/src/conversion_tables.rs +++ b/src/uu/dd/src/conversion_tables.rs @@ -4,7 +4,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. - +// // spell-checker:ignore (UCASE LCASE) // Note: Conversion tables are just lookup tables. diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index c7696c160..f299291d3 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -1,10 +1,10 @@ // This file is part of the uutils coreutils package. // +// (c) Tyler Steele +// // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (TODO ctable noerror) - use crate::conversion_tables::*; use std::error::Error; diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index f93a2b309..934045df2 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -5,8 +5,6 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (bmax bremain btotal cflags ctable curr dsync DSYNC fileout fname fullblock iflags INFILE noatime NOATIME nocreat noctty NOCTTY noerror nofollow NOFOLLOW nonblock NONBLOCK notrunc Noxfer oflag oflags OUTFILE parseargs plen rlen rmax rposition rremain rsofar rstat SIGINFO SIGUSR sigval tlen wlen wstat xfer) - #[macro_use] extern crate uucore; use uucore::InvalidEncodingHandling; @@ -34,7 +32,7 @@ use std::env; use std::error::Error; use std::fs::{File, OpenOptions}; use std::io::{self, Read, Seek, Write}; -#[cfg(unix)] +#[cfg(target_os = "linux")] use std::os::unix::fs::OpenOptionsExt; use std::path::Path; use std::sync::{atomic::AtomicUsize, atomic::Ordering, mpsc, Arc}; @@ -87,7 +85,8 @@ impl Input { } } -fn make_unix_iflags(oflags: &IFlags) -> Option { +#[cfg(target_os = "linux")] +fn make_linux_iflags(oflags: &IFlags) -> Option { let mut flag = 0; if oflags.direct { @@ -138,7 +137,7 @@ impl Input { opts.read(true); #[cfg(target_os = "linux")] - if let Some(libc_flags) = make_unix_iflags(&iflags) { + if let Some(libc_flags) = make_linux_iflags(&iflags) { opts.custom_flags(libc_flags); } @@ -225,7 +224,7 @@ impl Input { /// Fills a given buffer. /// Reads in increments of 'self.ibs'. - /// The start of each ibs-sized read is aligned to multiples of ibs; remaing space is filled with the 'pad' byte. + /// The start of each ibs-sized read is aligned to multiples of ibs; remaining space is filled with the 'pad' byte. fn fill_blocks(&mut self, buf: &mut Vec, pad: u8) -> Result> { let mut reads_complete = 0; let mut reads_partial = 0; @@ -313,7 +312,8 @@ impl Output { } } -fn make_unix_oflags(oflags: &OFlags) -> Option { +#[cfg(target_os = "linux")] +fn make_linux_oflags(oflags: &OFlags) -> Option { let mut flag = 0; // oflag=FLAG @@ -367,7 +367,7 @@ impl Output { .append(oflags.append); #[cfg(target_os = "linux")] - if let Some(libc_flags) = make_unix_oflags(oflags) { + if let Some(libc_flags) = make_linux_oflags(oflags) { opts.custom_flags(libc_flags); } @@ -512,20 +512,20 @@ impl Output { /// Appends padding as specified by conv=block and cbs=N fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> { let mut blocks = buf - .split(|&e| e == '\n' as u8) + .split(|&e| e == b'\n') .fold(Vec::new(), |mut blocks, split| { let mut split = split.to_vec(); if split.len() > cbs { rstat.records_truncated += 1; } - split.resize(cbs, ' ' as u8); + split.resize(cbs, b' '); blocks.push(split); blocks }); if let Some(last) = blocks.last() { - if last.iter().all(|&e| e == ' ' as u8) { + if last.iter().all(|&e| e == b' ') { blocks.pop(); } } @@ -559,13 +559,13 @@ fn unblock(buf: Vec, cbs: usize) -> Vec { build_blocks(buf, cbs) .into_iter() .fold(Vec::new(), |mut unblocks, mut block| { - let block = if let Some(last_char_idx) = block.iter().rposition(|&e| e != ' ' as u8) { + let block = if let Some(last_char_idx) = block.iter().rposition(|&e| e != b' ') { block.truncate(last_char_idx + 1); - block.push('\n' as u8); + block.push(b'\n'); block - } else if let Some(32u8 /* ' ' as u8 */) = block.get(0) { - vec!['\n' as u8] + } else if let Some(b' ') = block.get(0) { + vec![b'\n'] } else { block }; @@ -612,13 +612,13 @@ fn conv_block_unblock_helper( } } // -------------------------------------------------------------------- - if conv_only(&i) { + if conv_only(i) { // no block/unblock let ct = i.cflags.ctable.unwrap(); - apply_ct(&mut buf, &ct); + apply_ct(&mut buf, ct); Ok(buf) - } else if should_block_then_conv(&i) { + } else if should_block_then_conv(i) { // ascii input so perform the block first let cbs = i.cflags.block.unwrap(); @@ -626,41 +626,41 @@ fn conv_block_unblock_helper( if let Some(ct) = i.cflags.ctable { for buf in blocks.iter_mut() { - apply_ct(buf, &ct); + apply_ct(buf, ct); } } let blocks = blocks.into_iter().flatten().collect(); Ok(blocks) - } else if should_conv_then_block(&i) { + } else if should_conv_then_block(i) { // Non-ascii so perform the conversion first let cbs = i.cflags.block.unwrap(); if let Some(ct) = i.cflags.ctable { - apply_ct(&mut buf, &ct); + apply_ct(&mut buf, ct); } let blocks = block(buf, cbs, rstat).into_iter().flatten().collect(); Ok(blocks) - } else if should_unblock_then_conv(&i) { + } else if should_unblock_then_conv(i) { // ascii input so perform the unblock first let cbs = i.cflags.unblock.unwrap(); let mut buf = unblock(buf, cbs); if let Some(ct) = i.cflags.ctable { - apply_ct(&mut buf, &ct); + apply_ct(&mut buf, ct); } Ok(buf) - } else if should_conv_then_unblock(&i) { + } else if should_conv_then_unblock(i) { // Non-ascii input so perform the conversion first let cbs = i.cflags.unblock.unwrap(); if let Some(ct) = i.cflags.ctable { - apply_ct(&mut buf, &ct); + apply_ct(&mut buf, ct); } let buf = unblock(buf, cbs); @@ -720,7 +720,7 @@ fn read_helper( if i.cflags.swab { perform_swab(&mut buf); } - if is_conv(&i) || is_block(&i) || is_unblock(&i) { + if is_conv(i) || is_block(i) || is_unblock(i) { let buf = conv_block_unblock_helper(buf, i, &mut rstat)?; Ok((rstat, buf)) } else { @@ -761,7 +761,6 @@ fn make_prog_line(update: &ProgUpdate) -> String { update.duration.as_secs_f64(), xfer_rate ) - .to_string() } fn reprint_prog_line(update: &ProgUpdate) { eprint!("\r{}", make_prog_line(update)); @@ -815,16 +814,18 @@ fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option update, (Err(_), _) => - // recv only fails permenantly + // recv only fails permanently { break } }; // Handle signals + #[allow(clippy::single_match)] match sigval.load(Ordering::Relaxed) { SIGUSR1_USIZE => { print_xfer_stats(&update); } + // SIGINFO_USIZE => ... _ => { /* no signals recv'd */ } }; } @@ -833,15 +834,15 @@ fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option usize { let gcd = Gcd::gcd(ibs, obs); - let lcm = (ibs / gcd) * obs; - - lcm + // calculate the lcm from gcd + (ibs / gcd) * obs } /// Calculate the buffer size appropriate for this loop iteration, respecting @@ -887,7 +888,7 @@ fn below_count_limit(count: &Option, rstat: &ReadStat, wstat: &WriteS } } -/// Perform the copy/convert opertaions. Stdout version +/// Perform the copy/convert operations. Stdout 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<(), Box> { @@ -962,7 +963,7 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), Ok(()) } -/// Perform the copy/convert opertaions. File backed output version +/// Perform the copy/convert operations. 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<(), Box> { @@ -1037,13 +1038,13 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box, s: &String) -> Vec { - if Some("--") == s.get(0..=1) { - acc - } else { + if Some("--") != s.get(0..=1) { acc.push(format!("--{}", s)); - acc } + acc } macro_rules! unpack_or_rtn ( @@ -1253,8 +1254,8 @@ Input-Flags General-Flags \t 'direct' use direct I/O for data. \t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. -\t 'dsync' use syncronized I/O for data. -\t 'sync' use syncronized I/O for data and metadata. +\t 'dsync' use synchronized I/O for data. +\t 'sync' use synchronized I/O for data and metadata. \t 'nonblock' use non-blocking I/O. \t 'noatime' do not update access time. \t 'nocache' request that OS drop cache. @@ -1280,8 +1281,8 @@ Output-Flags General-Flags \t 'direct' use direct I/O for data. \t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. -\t 'dsync' use syncronized I/O for data. -\t 'sync' use syncronized I/O for data and metadata. +\t 'dsync' use synchronized I/O for data. +\t 'sync' use synchronized I/O for data and metadata. \t 'nonblock' use non-blocking I/O. \t 'noatime' do not update access time. \t 'nocache' request that OS drop cache. @@ -1291,175 +1292,3 @@ General-Flags ") ) } - -// #[macro_export] -// macro_rules! build_dd_app ( -// () => -// { -// clap::App::new(executable!()) -// .version(crate_version!()) -// .about(ABOUT) -// .arg( -// clap::Arg::with_name(options::INFILE) -// .long(options::INFILE) -// .takes_value(true) -// .help("if=FILE (alternatively --if FILE) specifies the file used for input. When not specified, stdin is used instead") -// ) -// .arg( -// clap::Arg::with_name(options::OUTFILE) -// .long(options::OUTFILE) -// .takes_value(true) -// .help("of=FILE (alternatively --of FILE) specifies the file used for output. When not specified, stdout is used instead") -// ) -// .arg( -// clap::Arg::with_name(options::IBS) -// .long(options::IBS) -// .takes_value(true) -// .help("ibs=N (alternatively --ibs N) specifies the size of buffer used for reads (default: 512). Multiplier strings permitted.") -// ) -// .arg( -// clap::Arg::with_name(options::OBS) -// .long(options::OBS) -// .takes_value(true) -// .help("obs=N (alternatively --obs N) specifies the size of buffer used for writes (default: 512). Multiplier strings permitted.") -// ) -// .arg( -// clap::Arg::with_name(options::BS) -// .long(options::BS) -// .takes_value(true) -// .help("bs=N (alternatively --bs N) specifies ibs=N and obs=N (default: 512). If ibs or obs are also specified, bs=N takes precedence. Multiplier strings permitted.") -// ) -// .arg( -// clap::Arg::with_name(options::CBS) -// .long(options::CBS) -// .takes_value(true) -// .help("cbs=BYTES (alternatively --cbs BYTES) specifies the 'conversion block size' in bytes. Applies to the conv=block, and conv=unblock operations. Multiplier strings permitted.") -// ) -// .arg( -// clap::Arg::with_name(options::SKIP) -// .long(options::SKIP) -// .takes_value(true) -// .help("skip=N (alternatively --skip N) causes N ibs-sized records of input to be skipped before beginning copy/convert operations. See iflag=count_bytes if skipping N bytes is preferred. Multiplier strings permitted.") -// ) -// .arg( -// clap::Arg::with_name(options::SEEK) -// .long(options::SEEK) -// .takes_value(true) -// .help("seek=N (alternatively --seek N) seeks N obs-sized records into output before beginning copy/convert operations. See oflag=seek_bytes if seeking N bytes is preferred. Multiplier strings permitted.") -// ) -// .arg( -// clap::Arg::with_name(options::COUNT) -// .long(options::COUNT) -// .takes_value(true) -// .help("count=N (alternatively --count N) stop reading input after N ibs-sized read operations rather than proceeding until EOF. See iflag=count_bytes if stopping after N bytes is preferred. Multiplier strings permitted.") -// ) -// .arg( -// clap::Arg::with_name(options::STATUS) -// .long(options::STATUS) -// .takes_value(true) -// .help("status=LEVEL (alternatively --status LEVEL) controls whether volume and performance stats are written to stderr. -// -// When unspecified, dd will print stats upon completion. An example is below. -// \t6+0 records in -// \t16+0 records out -// \t8192 bytes (8.2 kB, 8.0 KiB) copied, 0.00057009 s, 14.4 MB/s -// The first two lines are the 'volume' stats and the final line is the 'performance' stats. -// The volume stats indicate the number of complete and partial ibs-sized reads, or obs-sized writes that took place during the copy. The format of the volume stats is +. If records have been truncated (see conv=block), the volume stats will contain the number of truncated records. -// -// Permissible LEVEL values are: -// \t progress: Print periodic performance stats as the copy proceeds. -// \t noxfer: Print final volume stats, but not performance stats. -// \t none: Do not print any stats. -// -// Printing performance stats is also triggered by the INFO signal (where supported), or the USR1 signal. Setting the POSIXLY_CORRECT environment variable to any value (including an empty value) will cause the USR1 signal to be ignored. -// -// ") -// ) -// .arg( -// clap::Arg::with_name(options::CONV) -// .long(options::CONV) -// .takes_value(true) -// .help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file-flags. Conversion options and file flags may be intermixed. -// -// Conversion options: -// \t One of {ascii, ebcdic, ibm} will perform an encoding conversion. -// \t\t 'ascii' converts from EBCDIC to ASCII. This is the inverse of the 'ebcdic' option. -// \t\t 'ebcdic' converts from ASCII to EBCDIC. This is the inverse of the 'ascii' option. -// \t\t 'ibm' converts from ASCII to EBCDIC, applying the conventions for '[', ']' and '~' specified in POSIX. -// -// \t One of {ucase, lcase} will perform a case conversion. Works in conjunction with option {ascii, ebcdic, ibm} to infer input encoding. If no other conversion option is specified, input is assumed to be ascii. -// \t\t 'ucase' converts from lower-case to upper-case -// \t\t 'lcase' converts from upper-case to lower-case. -// -// \t One of {block, unblock}. Convert between lines terminated by newline characters, and fixed-width lines padded by spaces (without any newlines). Both the 'block' and 'unblock' options require cbs=BYTES be specified. -// \t\t 'block' for each newline less than the size indicated by cbs=BYTES, remove the newline and pad with spaces up to cbs. Lines longer than cbs are truncated. -// \t\t 'unblock' for each block of input of the size indicated by cbs=BYTES, remove right-trailing spaces and replace with a newline character. -// -// \t 'sparse' attempts to seek the output when an obs-sized block consists of only zeros. -// \t 'swab' swaps each adjacent pair of bytes. If an odd number of bytes is present, the final byte is omitted. -// \t 'sync' pad each ibs-sided block with zeros. If 'block' or 'unblock' is specified, pad with spaces instead. -// -// Flags: -// \t One of {excl, nocreat} -// \t\t 'excl' the output file must be created. Fail if the output file is already present. -// \t\t 'nocreat' the output file will not be created. Fail if the output file in not already present. -// \t 'notrunc' the output file will not be truncated. If this option is not present, output will be truncated when opened. -// \t 'noerror' all read errors will be ignored. If this option is not present, dd will only ignore Error::Interrupted. -// \t 'fdatasync' data will be written before finishing. -// \t 'fsync' data and metadata will be written before finishing. -// -// ") -// ) -// .arg( -// clap::Arg::with_name(options::IFLAG) -// .long(options::IFLAG) -// .takes_value(true) -// .help("iflag=FLAG[,FLAG] (alternatively --iflag FLAG[,FLAG]) a comma separated list of input flags which specify how the input source is treated. FLAG may be any of the input-flags or general-flags specified below. -// -// Input-Flags -// \t 'count_bytes' a value to count=N will be interpreted as bytes. -// \t 'skip_bytes' a value to skip=N will be interpreted as bytes. -// \t 'fullblock' wait for ibs bytes from each read. zero-length reads are still considered EOF. -// -// General-Flags -// \t 'direct' use direct I/O for data. -// \t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. -// \t 'dsync' use syncronized I/O for data. -// \t 'sync' use syncronized I/O for data and metadata. -// \t 'nonblock' use non-blocking I/O. -// \t 'noatime' do not update access time. -// \t 'nocache' request that OS drop cache. -// \t 'noctty' do not assign a controlling tty. -// \t 'nofollow' do not follow system links. -// -// Output-Flags -// \t 'append' open file in append mode. Consider setting conv=notrunc as well. -// \t 'seek_bytes' a value to seek=N will be interpreted as bytes. -// -// ") -// ) -// .arg( -// clap::Arg::with_name(options::OFLAG) -// .long(options::OFLAG) -// .takes_value(true) -// .help("oflag=FLAG[,FLAG] (alternatively --oflag FLAG[,FLAG]) a comma separated list of output flags which specify how the output source is treated. FLAG may be any of the output-flags or general-flags specified below. -// -// Output-Flags -// \t 'append' open file in append mode. Consider setting conv=notrunc as well. -// \t 'seek_bytes' a value to seek=N will be interpreted as bytes. -// -// General-Flags -// \t 'direct' use direct I/O for data. -// \t 'directory' fail unless the given input (if used as an iflag) or output (if used as an oflag) is a directory. -// \t 'dsync' use syncronized I/O for data. -// \t 'sync' use syncronized I/O for data and metadata. -// \t 'nonblock' use non-blocking I/O. -// \t 'noatime' do not update access time. -// \t 'nocache' request that OS drop cache. -// \t 'noctty' do not assign a controlling tty. -// \t 'nofollow' do not follow system links. -// -// ") -// ) -// }; -// ); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index e72d5cccc..d1667e963 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -1,4 +1,9 @@ -// spell-checker:ignore (parseargs xfer cflags iflags parseargs parseargs xfer cflags iflags iflags iflags xfer cflags oflags oflags oflags oflags dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK xfer cflags fname fname tlen rlen fullblock rlen tlen tlen noerror rlen rlen remaing plen plen plen plen oflag dsync DSYNC noatime NOATIME noctty NOCTTY nofollow NOFOLLOW nonblock NONBLOCK fname notrunc nocreat fname wlen wlen wlen wlen rstat rstat curr curr curr curr rposition rstat ctable ctable rstat ctable ctable btotal btotal btotal btotal SIGUSR SIGUSR sigval SIGINFO SIGINFO sigval SIGINFO SIGINFO SIGUSR sigval SIGUSR permenantly sigval itegral itegral wstat rmax rmax rmax rsofar rremain rmax rsofar rremain bmax bmax bmax bremain bmax wstat bremain wstat wstat opertaions Noxfer opertaions fileout Noxfer INFILE OUTFILE fileout fileout INFILE INFILE OUTFILE OUTFILE iflag oflag iflag noxfer ucase lcase ucase lcase nocreat nocreat notrunc noerror IFLAG IFLAG iflag iflag fullblock oflag dsync syncronized syncronized nonblock noatime nocache noctty nofollow notrunc OFLAG OFLAG oflag notrunc dsync syncronized syncronized nonblock noatime nocache noctty nofollow T0DO) +// This file is part of the uutils coreutils package. +// +// (c) Tyler Steele +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. #[cfg(test)] mod unit_tests; @@ -295,7 +300,7 @@ impl std::str::FromStr for StatusLevel { } } -fn parse_multiplier<'a>(s: &'a str) -> Result { +fn parse_multiplier(s: &'_ str) -> Result { let mult: u128 = match s { "c" => 1, "w" => 2, @@ -342,7 +347,7 @@ fn parse_bytes_with_opt_multiplier(s: &str) -> Result { Err(ParseError::MultiplierStringWouldOverflow(s.to_string())) } } - _ => parse_bytes_only(&s), + _ => parse_bytes_only(s), } } @@ -429,7 +434,7 @@ fn parse_flag_list>( let mut flags = Vec::new(); if let Some(comma_str) = matches.value_of(tag) { - for s in comma_str.split(",") { + for s in comma_str.split(',') { let flag = s.parse()?; flags.push(flag); } @@ -455,35 +460,35 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result { - if let Some(_) = fmt { + if fmt.is_some() { return Err(ParseError::MultipleFmtTable); } else { fmt = Some(flag); } } ConvFlag::FmtAtoE => { - if let Some(_) = fmt { + if fmt.is_some() { return Err(ParseError::MultipleFmtTable); } else { fmt = Some(flag); } } ConvFlag::FmtAtoI => { - if let Some(_) = fmt { + if fmt.is_some() { return Err(ParseError::MultipleFmtTable); } else { fmt = Some(flag); } } ConvFlag::UCase => { - if let Some(_) = case { + if case.is_some() { return Err(ParseError::MultipleUCaseLCase); } else { case = Some(flag) } } ConvFlag::LCase => { - if let Some(_) = case { + if case.is_some() { return Err(ParseError::MultipleUCaseLCase); } else { case = Some(flag) @@ -508,7 +513,7 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result Result, ParseError> { if let Some(amt) = matches.value_of("skip") { + let n = parse_bytes_with_opt_multiplier(amt)?; if iflags.skip_bytes { - let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(n)) } else { - let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(ibs * n)) } } else { @@ -720,11 +724,10 @@ pub fn parse_seek_amt( matches: &Matches, ) -> Result, ParseError> { if let Some(amt) = matches.value_of("seek") { + let n = parse_bytes_with_opt_multiplier(amt)?; if oflags.seek_bytes { - let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(n)) } else { - let n = parse_bytes_with_opt_multiplier(amt)?; Ok(Some(obs * n)) } } else { diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 0c78d13af..ca153c032 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,5 +1,3 @@ -// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) - use crate::common::util::*; use std::fs::{File, OpenOptions}; @@ -225,6 +223,7 @@ fn test_atime_updated() { let pre_atime = fix.metadata(&fname).accessed().unwrap(); ucmd.pipe_in("").run().no_stderr().success(); + std::thread::sleep(std::time::Duration::from_millis(10)); let post_atime = fix.metadata(&fname).accessed().unwrap(); assert!(pre_atime != post_atime); @@ -268,7 +267,10 @@ fn test_nocreat_causes_failure_when_outfile_not_present() { assert_fixture_not_exists!(&fname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&["conv=nocreat", of!(&fname)]).pipe_in("").fails().stderr_is("dd Error: No such file or directory (os error 2)"); + ucmd.args(&["conv=nocreat", of!(&fname)]) + .pipe_in("") + .fails() + .stderr_is("dd Error: No such file or directory (os error 2)"); assert!(!fix.file_exists(&fname)); From 47806ddd39d3206793cebcb1e84cd22d2b8c294a Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 8 Jul 2021 09:14:06 -0700 Subject: [PATCH 50/66] build issues - Address Rust fmt issue - ignores run-without-noatime test which fails on some build machines - adds cspell:disable to all project files. - adds .../dd/fixtures/cspell.json to ignore test fixtures. --- src/uu/dd/src/conversion_tables.rs | 4 +-- src/uu/dd/src/datastructures.rs | 2 ++ src/uu/dd/src/dd.rs | 2 ++ .../src/dd_unit_tests/block_unblock_tests.rs | 2 +- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 2 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 2 +- src/uu/dd/src/dd_unit_tests/mod.rs | 2 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 2 +- src/uu/dd/src/parseargs.rs | 2 ++ src/uu/dd/src/parseargs/unit_tests.rs | 2 ++ tests/by-util/test_dd.rs | 27 +++++++------------ 11 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs index 1130a7b5b..61c36bc04 100644 --- a/src/uu/dd/src/conversion_tables.rs +++ b/src/uu/dd/src/conversion_tables.rs @@ -4,8 +4,8 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// -// spell-checker:ignore (UCASE LCASE) + +/* cspell:disable */ // Note: Conversion tables are just lookup tables. // eg. The ASCII->EBCDIC table stores the EBCDIC code at the index diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index f299291d3..59e770249 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -5,6 +5,8 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +/* cspell:disable */ + use crate::conversion_tables::*; use std::error::Error; diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 934045df2..2b44a47d8 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -5,6 +5,8 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +/* cspell:disable */ + #[macro_use] extern crate uucore; use uucore::InvalidEncodingHandling; diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index 036f87130..83058948a 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -1,4 +1,4 @@ -// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) +/* cspell:disable */ use super::*; diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index b2b45843c..5d7dcd5e3 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -1,4 +1,4 @@ -// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) +/* cspell:disable */ use super::*; diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 6fb7d1e26..738ead5fd 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -1,4 +1,4 @@ -// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) +/* cspell:disable */ use super::*; diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index d177319a7..49edadd97 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -1,4 +1,4 @@ -// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) +/* cspell:disable */ use super::*; diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index 2e91fb2cb..02355964b 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -1,4 +1,4 @@ -// spell-checker:ignore (Fileio fname fpath fullblock gibi ifile iflag infile lcase noatime nocreat notrunc noxfer ofile oflag outfile specfile testfile TESTFILE tname ucase unspec urand) +/* cspell:disable */ use super::*; diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index d1667e963..72645205f 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -5,6 +5,8 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +/* cspell:disable */ + #[cfg(test)] mod unit_tests; diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 79a04482b..25a32eb68 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -1,3 +1,5 @@ +/* cspell:disable */ + use super::*; use crate::StatusLevel; diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index ca153c032..736570b56 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,3 +1,5 @@ +/* cspell:disable */ + use crate::common::util::*; use std::fs::{File, OpenOptions}; @@ -212,6 +214,7 @@ fn test_excl_causes_failure_when_present() { .fails(); } +#[ignore] #[test] fn test_atime_updated() { let fname = "this-file-exists-no-noatime.txt"; @@ -271,9 +274,7 @@ fn test_nocreat_causes_failure_when_outfile_not_present() { .pipe_in("") .fails() .stderr_is("dd Error: No such file or directory (os error 2)"); - assert!(!fix.file_exists(&fname)); - } #[test] @@ -515,13 +516,7 @@ fn test_ascii_5_gibi_to_file() { let tmp_fn = format!("TESTFILE-{}.tmp", &tname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - "count=5G", - "iflag=count_bytes", - "if=/dev/zero", - of!(tmp_fn), - ]) + ucmd.args(&["status=none", "count=5G", "iflag=count_bytes", "if=/dev/zero", of!(tmp_fn)]) .run() .no_stderr() .no_stdout() @@ -555,15 +550,11 @@ fn test_unicode_filenames() { assert_fixture_exists!(test_fn); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&[ - "status=none", - inf!(test_fn), - of!(tmp_fn), - ]) - .run() - .no_stderr() - .no_stdout() - .success(); + ucmd.args(&["status=none", inf!(test_fn), of!(tmp_fn)]) + .run() + .no_stderr() + .no_stdout() + .success(); cmp_file!( File::open(fixture_path!(&test_fn)).unwrap(), From 88363858d5ecba20fb79758809873885cdde19ce Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 12 Jul 2021 10:13:47 -0700 Subject: [PATCH 51/66] Minor changes. --- src/uu/dd/cspell.json | 6 +++--- tests/by-util/test_dd.rs | 8 +++++++- tests/fixtures/dd/cspell.json | 8 ++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/dd/cspell.json diff --git a/src/uu/dd/cspell.json b/src/uu/dd/cspell.json index 91b041d12..9f4bd4207 100644 --- a/src/uu/dd/cspell.json +++ b/src/uu/dd/cspell.json @@ -1,6 +1,6 @@ { "version": "0.1", - "language": "en-CA", + "language": "en", "words": [ "ucase", "lcase", @@ -38,7 +38,7 @@ "ctty", ], "ignorePaths": [ - "test-fixtures/*.test", - "test-fixtures/*.spec" + "**/test-fixtures/*.test", + "**/test-fixtures/*.spec" ] } diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 736570b56..5e9014d39 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -516,7 +516,13 @@ fn test_ascii_5_gibi_to_file() { let tmp_fn = format!("TESTFILE-{}.tmp", &tname); let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&["status=none", "count=5G", "iflag=count_bytes", "if=/dev/zero", of!(tmp_fn)]) + ucmd.args(&[ + "status=none", + "count=5G", + "iflag=count_bytes", + "if=/dev/zero", + of!(tmp_fn) + ]) .run() .no_stderr() .no_stdout() diff --git a/tests/fixtures/dd/cspell.json b/tests/fixtures/dd/cspell.json new file mode 100644 index 000000000..19986ad83 --- /dev/null +++ b/tests/fixtures/dd/cspell.json @@ -0,0 +1,8 @@ +{ + "version": "0.1", + "language": "en", + "ignorePaths": [ + "*.txt", + "*.spec" + ] +} From bb0d70965def296b20927f38f02b622b506006cd Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 12 Jul 2021 10:36:38 -0700 Subject: [PATCH 52/66] Commits changed Cargo.toml in dd --- Cargo.lock | 2 +- src/uu/dd/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03b64361e..dd1e697ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1911,7 +1911,7 @@ dependencies = [ [[package]] name = "uu_dd" -version = "0.0.6" +version = "0.0.7" dependencies = [ "byte-unit", "clap", diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index dc5d03d00..f65d0bf12 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dd" -version = "0.0.6" +version = "0.0.7" authors = ["uutils developers"] license = "MIT" description = "dd ~ (uutils) copy and convert files" From 94e0145d51dcd510df2e99944e94c23b033a536b Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 12 Jul 2021 12:54:39 -0700 Subject: [PATCH 53/66] fixes more minor issues --- src/uu/dd/cspell.json | 4 ++-- tests/by-util/test_dd.rs | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/uu/dd/cspell.json b/src/uu/dd/cspell.json index 9f4bd4207..fb29630b2 100644 --- a/src/uu/dd/cspell.json +++ b/src/uu/dd/cspell.json @@ -38,7 +38,7 @@ "ctty", ], "ignorePaths": [ - "**/test-fixtures/*.test", - "**/test-fixtures/*.spec" + "**/test-resources/*.test", + "**/test-resources/*.spec" ] } diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 5e9014d39..de6e510a9 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -223,12 +223,12 @@ fn test_atime_updated() { let (fix, mut ucmd) = at_and_ucmd!(); ucmd.args(&["status=none", inf!(fname)]); - let pre_atime = fix.metadata(&fname).accessed().unwrap(); + let pre_atime = fix.metadata(fname).accessed().unwrap(); ucmd.pipe_in("").run().no_stderr().success(); std::thread::sleep(std::time::Duration::from_millis(10)); - let post_atime = fix.metadata(&fname).accessed().unwrap(); + let post_atime = fix.metadata(fname).accessed().unwrap(); assert!(pre_atime != post_atime); } @@ -240,11 +240,11 @@ fn test_noatime_does_not_update_infile_atime() { let (fix, mut ucmd) = at_and_ucmd!(); ucmd.args(&["status=none", "iflag=noatime", inf!(fname)]); - let pre_atime = fix.metadata(&fname).accessed().unwrap(); + let pre_atime = fix.metadata(fname).accessed().unwrap(); ucmd.run().no_stderr().success(); - let post_atime = fix.metadata(&fname).accessed().unwrap(); + let post_atime = fix.metadata(fname).accessed().unwrap(); assert_eq!(pre_atime, post_atime); } @@ -256,11 +256,11 @@ fn test_noatime_does_not_update_ofile_atime() { let (fix, mut ucmd) = at_and_ucmd!(); ucmd.args(&["status=none", "oflag=noatime", of!(fname)]); - let pre_atime = fix.metadata(&fname).accessed().unwrap(); + let pre_atime = fix.metadata(fname).accessed().unwrap(); ucmd.pipe_in("").run().no_stderr().success(); - let post_atime = fix.metadata(&fname).accessed().unwrap(); + let post_atime = fix.metadata(fname).accessed().unwrap(); assert_eq!(pre_atime, post_atime); } @@ -274,7 +274,7 @@ fn test_nocreat_causes_failure_when_outfile_not_present() { .pipe_in("") .fails() .stderr_is("dd Error: No such file or directory (os error 2)"); - assert!(!fix.file_exists(&fname)); + assert!(!fix.file_exists(fname)); } #[test] @@ -294,7 +294,7 @@ fn test_notrunc_does_not_truncate() { .no_stderr() .success(); - assert_eq!(256, fix.metadata(&fname).len()); + assert_eq!(256, fix.metadata(fname).len()); } #[test] @@ -314,7 +314,7 @@ fn test_existing_file_truncated() { .no_stderr() .success(); - assert_eq!(0, fix.metadata(&fname).len()); + assert_eq!(0, fix.metadata(fname).len()); } #[test] @@ -501,7 +501,7 @@ fn test_ascii_521k_to_file() { cmp_file!( { let mut input_f = tempfile().unwrap(); - input_f.write(&input).unwrap(); + input_f.write_all(&input).unwrap(); input_f }, fix.open(&tmp_fn) @@ -521,7 +521,7 @@ fn test_ascii_5_gibi_to_file() { "count=5G", "iflag=count_bytes", "if=/dev/zero", - of!(tmp_fn) + of!(tmp_fn), ]) .run() .no_stderr() From c3f95575818037d9468b97c753311de32ce0216a Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 12 Jul 2021 13:53:31 -0700 Subject: [PATCH 54/66] Adds dd to feat_common_core --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index c76af8d84..19083c150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ feat_common_core = [ "df", "dircolors", "dirname", + "dd", "du", "echo", "env", From 9d9267e08b4bf97f3cf44c48dda39c2c0812e0b9 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 14 Jul 2021 13:59:11 -0700 Subject: [PATCH 55/66] Addresses code-quality issues from testsdiepraam and miDeb. - Removes dd from feat_require_unix (keeps it in feat_common_core) - Changes name of make_linux_iflags parameter from oflags to iflags. - Removes roughed out SIGINFO impl. - Renames plen -> target_len. - Removes internal fn def for build_blocks and replaces with a call to chunks from std. - Renames apply_ct to the more descriptive apply_conversion - Replaces manual swap steps in perform_swab with call to swap from std. - Replaces manual solution with chunks where appropriate (in Read, and Write impl). - Renames xfer -> transfer (in local variable names). - Improves documentation for dd_filout/dd_stdout issue. - Removes commented debug_assert statements. - Modifies ProdUpdate to contain ReadStat and WriteStat rather than copying their fields. - Addresses verbose return in `Output::new(...)` - Resoves compiler warning when built as release when signal handler fails to register. - Derives _Default_ trait on ReadStat. - Adds comments for truncated lines in block unblock tests - Removes `as u8` in block unblock tests. - Removes unecessary `#[inline]` - Delegates multiplier string parsing to uucore::parse_size. - Renames 'unfailed' -> 'succeeded' for clairity. - Removes #dead_code warnings. No clippy warnings on my local machine. - Reworks signal handler to better accomodate platform-specific signals. - Removes explicit references to "if" and "of" in dd.rs. - Removes explicit references to "bs", "ibs", "cbs" and "status" in parseargs.rs. - Removes `#[allow(deadcode)]` for OFlags, and IFlags. - Removes spellchecker ignore from all dd files. - Adds tests for 'traditional' and 'modern' CLI. --- Cargo.toml | 1 - src/uu/dd/src/conversion_tables.rs | 2 - src/uu/dd/src/datastructures.rs | 44 +-- src/uu/dd/src/dd.rs | 351 ++++++------------ .../src/dd_unit_tests/block_unblock_tests.rs | 50 +-- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 4 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 10 +- src/uu/dd/src/dd_unit_tests/mod.rs | 22 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 20 +- src/uu/dd/src/parseargs.rs | 85 ++--- src/uu/dd/src/parseargs/unit_tests.rs | 204 ++++++++-- tests/by-util/test_dd.rs | 2 - 12 files changed, 377 insertions(+), 418 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 19083c150..c3819560e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,7 +153,6 @@ feat_require_unix = [ "chmod", "chown", "chroot", - "dd", "groups", "hostid", "id", diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs index 61c36bc04..aca2ef9bc 100644 --- a/src/uu/dd/src/conversion_tables.rs +++ b/src/uu/dd/src/conversion_tables.rs @@ -5,8 +5,6 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -/* cspell:disable */ - // Note: Conversion tables are just lookup tables. // eg. The ASCII->EBCDIC table stores the EBCDIC code at the index // obtained by treating the ASCII representation as a number. diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs index 59e770249..6f2b16d67 100644 --- a/src/uu/dd/src/datastructures.rs +++ b/src/uu/dd/src/datastructures.rs @@ -5,23 +5,18 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -/* cspell:disable */ - use crate::conversion_tables::*; use std::error::Error; use std::time; pub struct ProgUpdate { - pub reads_complete: u64, - pub reads_partial: u64, - pub writes_complete: u64, - pub writes_partial: u64, - pub bytes_total: u128, - pub records_truncated: u32, + pub read_stat: ReadStat, + pub write_stat: WriteStat, pub duration: time::Duration, } +#[derive(Clone, Copy, Default)] pub struct ReadStat { pub reads_complete: u64, pub reads_partial: u64, @@ -37,6 +32,7 @@ impl std::ops::AddAssign for ReadStat { } } +#[derive(Clone, Copy)] pub struct WriteStat { pub writes_complete: u64, pub writes_partial: u64, @@ -55,6 +51,7 @@ impl std::ops::AddAssign for WriteStat { type Cbs = usize; /// Stores all Conv Flags that apply to the input +#[derive(Debug, Default, PartialEq)] pub struct IConvFlags { pub ctable: Option<&'static ConversionTable>, pub block: Option, @@ -65,7 +62,7 @@ pub struct IConvFlags { } /// Stores all Conv Flags that apply to the output -#[derive(Debug, PartialEq)] +#[derive(Debug, Default, PartialEq)] pub struct OConvFlags { pub sparse: bool, pub excl: bool, @@ -76,32 +73,20 @@ pub struct OConvFlags { } /// Stores all Flags that apply to the input +#[derive(Debug, Default, PartialEq)] pub struct IFlags { - #[allow(dead_code)] pub cio: bool, - #[allow(dead_code)] pub direct: bool, - #[allow(dead_code)] pub directory: bool, - #[allow(dead_code)] pub dsync: bool, - #[allow(dead_code)] pub sync: bool, - #[allow(dead_code)] pub nocache: bool, - #[allow(dead_code)] pub nonblock: bool, - #[allow(dead_code)] pub noatime: bool, - #[allow(dead_code)] pub noctty: bool, - #[allow(dead_code)] pub nofollow: bool, - #[allow(dead_code)] pub nolinks: bool, - #[allow(dead_code)] pub binary: bool, - #[allow(dead_code)] pub text: bool, pub fullblock: bool, pub count_bytes: bool, @@ -109,33 +94,21 @@ pub struct IFlags { } /// Stores all Flags that apply to the output +#[derive(Debug, Default, PartialEq)] pub struct OFlags { pub append: bool, - #[allow(dead_code)] pub cio: bool, - #[allow(dead_code)] pub direct: bool, - #[allow(dead_code)] pub directory: bool, - #[allow(dead_code)] pub dsync: bool, - #[allow(dead_code)] pub sync: bool, - #[allow(dead_code)] pub nocache: bool, - #[allow(dead_code)] pub nonblock: bool, - #[allow(dead_code)] pub noatime: bool, - #[allow(dead_code)] pub noctty: bool, - #[allow(dead_code)] pub nofollow: bool, - #[allow(dead_code)] pub nolinks: bool, - #[allow(dead_code)] pub binary: bool, - #[allow(dead_code)] pub text: bool, pub seek_bytes: bool, } @@ -153,6 +126,7 @@ pub enum StatusLevel { /// Defaults to Reads(N) /// if iflag=count_bytes /// then becomes Bytes(N) +#[derive(Debug, PartialEq)] pub enum CountType { Reads(usize), Bytes(usize), diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 2b44a47d8..712e10e1e 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -5,8 +5,6 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -/* cspell:disable */ - #[macro_use] extern crate uucore; use uucore::InvalidEncodingHandling; @@ -25,7 +23,6 @@ use conversion_tables::*; use byte_unit::Byte; use clap::{self, crate_version}; -use debug_print::debug_println; use gcd::Gcd; use signal_hook::consts::signal; use std::cmp; @@ -51,7 +48,7 @@ struct Input { src: R, non_ascii: bool, ibs: usize, - xfer_stats: Option, + print_level: Option, count: Option, cflags: IConvFlags, iflags: IFlags, @@ -61,7 +58,7 @@ impl Input { fn new(matches: &Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?; - let xfer_stats = parseargs::parse_status_level(matches)?; + let print_level = parseargs::parse_status_level(matches)?; let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; @@ -71,7 +68,7 @@ impl Input { src: io::stdin(), non_ascii, ibs, - xfer_stats, + print_level, count, cflags, iflags, @@ -88,31 +85,31 @@ impl Input { } #[cfg(target_os = "linux")] -fn make_linux_iflags(oflags: &IFlags) -> Option { +fn make_linux_iflags(iflags: &IFlags) -> Option { let mut flag = 0; - if oflags.direct { + if iflags.direct { flag |= libc::O_DIRECT; } - if oflags.directory { + if iflags.directory { flag |= libc::O_DIRECTORY; } - if oflags.dsync { + if iflags.dsync { flag |= libc::O_DSYNC; } - if oflags.noatime { + if iflags.noatime { flag |= libc::O_NOATIME; } - if oflags.noctty { + if iflags.noctty { flag |= libc::O_NOCTTY; } - if oflags.nofollow { + if iflags.nofollow { flag |= libc::O_NOFOLLOW; } - if oflags.nonblock { + if iflags.nonblock { flag |= libc::O_NONBLOCK; } - if oflags.sync { + if iflags.sync { flag |= libc::O_SYNC; } @@ -127,13 +124,13 @@ impl Input { fn new(matches: &Matches) -> Result> { let ibs = parseargs::parse_ibs(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?; - let xfer_stats = parseargs::parse_status_level(matches)?; + let print_level = parseargs::parse_status_level(matches)?; let cflags = parseargs::parse_conv_flag_input(matches)?; let iflags = parseargs::parse_iflags(matches)?; let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; let count = parseargs::parse_count(&iflags, matches)?; - if let Some(fname) = matches.value_of("if") { + if let Some(fname) = matches.value_of(options::INFILE) { let mut src = { let mut opts = OpenOptions::new(); opts.read(true); @@ -155,7 +152,7 @@ impl Input { src, non_ascii, ibs, - xfer_stats, + print_level, count, cflags, iflags, @@ -198,28 +195,27 @@ impl Input { fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> { let mut reads_complete = 0; let mut reads_partial = 0; - let mut base_idx = 0; + let mut bytes_total = 0; - while base_idx < buf.len() { - let next_blk = cmp::min(base_idx + self.ibs, buf.len()); - - match self.read(&mut buf[base_idx..next_blk])? { + for chunk in buf.chunks_mut(self.ibs) { + match self.read(chunk)? { rlen if rlen == self.ibs => { - base_idx += rlen; + bytes_total += rlen; reads_complete += 1; } rlen if rlen > 0 => { - base_idx += rlen; + bytes_total += rlen; reads_partial += 1; } _ => break, } } - buf.truncate(base_idx); + buf.truncate(bytes_total); Ok(ReadStat { reads_complete, reads_partial, + // Records are not truncated when filling. records_truncated: 0, }) } @@ -234,35 +230,19 @@ impl Input { while base_idx < buf.len() { let next_blk = cmp::min(base_idx + self.ibs, buf.len()); - let plen = next_blk - base_idx; + let target_len = next_blk - base_idx; match self.read(&mut buf[base_idx..next_blk])? { 0 => break, - rlen if rlen < plen => { + rlen if rlen < target_len => { reads_partial += 1; - let padding = vec![pad; plen - rlen]; + let padding = vec![pad; target_len - rlen]; buf.splice(base_idx + rlen..next_blk, padding.into_iter()); } _ => { reads_complete += 1; } } - // TODO: Why does this cause the conv=sync tests to hang? - // let rlen = self.read(&mut buf[base_idx..next_blk])?; - // if rlen < plen - // { - // reads_partial += 1; - // let padding = vec![pad; plen-rlen]; - // buf.splice(base_idx+rlen..next_blk, padding.into_iter()); - // } - // else - // { - // reads_complete += 1; - // } - // if rlen == 0 - // { - // break; - // } base_idx += self.ibs; } @@ -356,11 +336,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option { impl Output { fn new(matches: &Matches) -> Result> { - fn open_dst( - path: &Path, - cflags: &OConvFlags, - oflags: &OFlags, - ) -> Result> { + fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result { let mut opts = OpenOptions::new(); opts.write(true) .create(!cflags.nocreat) @@ -373,15 +349,14 @@ impl Output { opts.custom_flags(libc_flags); } - let dst = opts.open(path)?; - Ok(dst) + opts.open(path) } let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; let oflags = parseargs::parse_oflags(matches)?; let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; - if let Some(fname) = matches.value_of("of") { + if let Some(fname) = matches.value_of(options::OUTFILE) { let mut dst = open_dst(Path::new(&fname), &cflags, &oflags)?; if let Some(amt) = seek { @@ -418,7 +393,6 @@ impl Seek for Output { impl Write for Output { fn write(&mut self, buf: &[u8]) -> io::Result { - #[inline] fn is_sparse(buf: &[u8]) -> bool { buf.iter().all(|&e| e == 0u8) } @@ -454,20 +428,17 @@ impl Output { fn write_blocks(&mut self, buf: Vec) -> io::Result { let mut writes_complete = 0; let mut writes_partial = 0; - let mut base_idx = 0; + let mut bytes_total = 0; - while base_idx < buf.len() { - let next_blk = cmp::min(base_idx + self.obs, buf.len()); - let plen = next_blk - base_idx; - - match self.write(&buf[base_idx..next_blk])? { - wlen if wlen < plen => { + for chunk in buf.chunks(self.obs) { + match self.write(chunk)? { + wlen if wlen < chunk.len() => { writes_partial += 1; - base_idx += wlen; + bytes_total += wlen; } wlen => { writes_complete += 1; - base_idx += wlen; + bytes_total += wlen; } } } @@ -475,7 +446,7 @@ impl Output { Ok(WriteStat { writes_complete, writes_partial, - bytes_total: base_idx.try_into().unwrap_or(0u128), + bytes_total: bytes_total.try_into().unwrap_or(0u128), }) } } @@ -484,20 +455,17 @@ impl Output { fn write_blocks(&mut self, buf: Vec) -> io::Result { let mut writes_complete = 0; let mut writes_partial = 0; - let mut base_idx = 0; + let mut bytes_total = 0; - while base_idx < buf.len() { - let next_blk = cmp::min(base_idx + self.obs, buf.len()); - let plen = next_blk - base_idx; - - match self.write(&buf[base_idx..next_blk])? { - wlen if wlen < plen => { + for chunk in buf.chunks(self.obs) { + match self.write(chunk)? { + wlen if wlen < chunk.len() => { writes_partial += 1; - base_idx += wlen; + bytes_total += wlen; } wlen => { writes_complete += 1; - base_idx += wlen; + bytes_total += wlen; } } } @@ -505,7 +473,7 @@ impl Output { Ok(WriteStat { writes_complete, writes_partial, - bytes_total: base_idx.try_into().unwrap_or(0u128), + bytes_total: bytes_total.try_into().unwrap_or(0u128), }) } } @@ -538,47 +506,18 @@ fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> { /// Trims padding from each cbs-length partition of buf /// as specified by conv=unblock and cbs=N fn unblock(buf: Vec, cbs: usize) -> Vec { - // Local Helper Fns ---------------------------------------------------- - #[inline] - fn build_blocks(buf: Vec, cbs: usize) -> Vec> { - let mut blocks = Vec::new(); - let mut curr = buf; - let mut next; - let mut width; + buf.chunks(cbs).fold(Vec::new(), |mut acc, block| { + if let Some(last_char_idx) = block.iter().rposition(|&e| e != b' ') { + // Find last space + acc.extend(&block[..=last_char_idx]); + acc.push(b'\n'); + } else { + // The block is filled with only spaces + acc.push(b'\n'); + }; - while !curr.is_empty() { - width = cmp::min(cbs, curr.len()); - next = curr.split_off(width); - - blocks.push(curr); - - curr = next; - } - - blocks - } - // --------------------------------------------------------------------- - build_blocks(buf, cbs) - .into_iter() - .fold(Vec::new(), |mut unblocks, mut block| { - let block = if let Some(last_char_idx) = block.iter().rposition(|&e| e != b' ') { - block.truncate(last_char_idx + 1); - block.push(b'\n'); - - block - } else if let Some(b' ') = block.get(0) { - vec![b'\n'] - } else { - block - }; - - unblocks.push(block); - - unblocks - }) - .into_iter() - .flatten() - .collect() + acc + }) } fn conv_block_unblock_helper( @@ -587,19 +526,15 @@ fn conv_block_unblock_helper( rstat: &mut ReadStat, ) -> Result, Box> { // Local Predicate Fns ------------------------------------------------- - #[inline] fn should_block_then_conv(i: &Input) -> bool { !i.non_ascii && i.cflags.block.is_some() } - #[inline] fn should_conv_then_block(i: &Input) -> bool { i.non_ascii && i.cflags.block.is_some() } - #[inline] fn should_unblock_then_conv(i: &Input) -> bool { !i.non_ascii && i.cflags.unblock.is_some() } - #[inline] fn should_conv_then_unblock(i: &Input) -> bool { i.non_ascii && i.cflags.unblock.is_some() } @@ -607,8 +542,7 @@ fn conv_block_unblock_helper( i.cflags.ctable.is_some() && i.cflags.block.is_none() && i.cflags.unblock.is_none() } // Local Helper Fns ---------------------------------------------------- - #[inline] - fn apply_ct(buf: &mut [u8], ct: &ConversionTable) { + fn apply_conversion(buf: &mut [u8], ct: &ConversionTable) { for idx in 0..buf.len() { buf[idx] = ct[buf[idx] as usize]; } @@ -617,7 +551,7 @@ fn conv_block_unblock_helper( if conv_only(i) { // no block/unblock let ct = i.cflags.ctable.unwrap(); - apply_ct(&mut buf, ct); + apply_conversion(&mut buf, ct); Ok(buf) } else if should_block_then_conv(i) { @@ -628,7 +562,7 @@ fn conv_block_unblock_helper( if let Some(ct) = i.cflags.ctable { for buf in blocks.iter_mut() { - apply_ct(buf, ct); + apply_conversion(buf, ct); } } @@ -640,7 +574,7 @@ fn conv_block_unblock_helper( let cbs = i.cflags.block.unwrap(); if let Some(ct) = i.cflags.ctable { - apply_ct(&mut buf, ct); + apply_conversion(&mut buf, ct); } let blocks = block(buf, cbs, rstat).into_iter().flatten().collect(); @@ -653,7 +587,7 @@ fn conv_block_unblock_helper( let mut buf = unblock(buf, cbs); if let Some(ct) = i.cflags.ctable { - apply_ct(&mut buf, ct); + apply_conversion(&mut buf, ct); } Ok(buf) @@ -662,7 +596,7 @@ fn conv_block_unblock_helper( let cbs = i.cflags.unblock.unwrap(); if let Some(ct) = i.cflags.ctable { - apply_ct(&mut buf, ct); + apply_conversion(&mut buf, ct); } let buf = unblock(buf, cbs); @@ -683,27 +617,19 @@ fn read_helper( bsize: usize, ) -> Result<(ReadStat, Vec), Box> { // Local Predicate Fns ----------------------------------------------- - #[inline] fn is_conv(i: &Input) -> bool { i.cflags.ctable.is_some() } - #[inline] fn is_block(i: &Input) -> bool { i.cflags.block.is_some() } - #[inline] fn is_unblock(i: &Input) -> bool { i.cflags.unblock.is_some() } // Local Helper Fns ------------------------------------------------- - #[inline] fn perform_swab(buf: &mut [u8]) { - let mut tmp; - for base in (1..buf.len()).step_by(2) { - tmp = buf[base]; - buf[base] = buf[base - 1]; - buf[base - 1] = tmp; + buf.swap(base, base - 1); } } // ------------------------------------------------------------------ @@ -733,35 +659,35 @@ fn read_helper( fn print_io_lines(update: &ProgUpdate) { eprintln!( "{}+{} records in", - update.reads_complete, update.reads_partial + update.read_stat.reads_complete, update.read_stat.reads_partial ); - if update.records_truncated > 0 { - eprintln!("{} truncated records", update.records_truncated); + if update.read_stat.records_truncated > 0 { + eprintln!("{} truncated records", update.read_stat.records_truncated); } eprintln!( "{}+{} records out", - update.writes_complete, update.writes_partial + update.write_stat.writes_complete, update.write_stat.writes_partial ); } fn make_prog_line(update: &ProgUpdate) -> String { - let btotal_metric = Byte::from_bytes(update.bytes_total) + let btotal_metric = Byte::from_bytes(update.write_stat.bytes_total) .get_appropriate_unit(false) .format(0); - let btotal_bin = Byte::from_bytes(update.bytes_total) + let btotal_bin = Byte::from_bytes(update.write_stat.bytes_total) .get_appropriate_unit(true) .format(0); let safe_millis = cmp::max(1, update.duration.as_millis()); - let xfer_rate = Byte::from_bytes(1000 * (update.bytes_total / safe_millis)) + let transfer_rate = Byte::from_bytes(1000 * (update.write_stat.bytes_total / safe_millis)) .get_appropriate_unit(false) .format(1); format!( "{} bytes ({}, {}) copied, {:.1} s, {}/s", - update.bytes_total, + update.write_stat.bytes_total, btotal_metric, btotal_bin, update.duration.as_secs_f64(), - xfer_rate + transfer_rate ) } fn reprint_prog_line(update: &ProgUpdate) { @@ -770,65 +696,60 @@ fn reprint_prog_line(update: &ProgUpdate) { fn print_prog_line(update: &ProgUpdate) { eprintln!("{}", make_prog_line(update)); } -fn print_xfer_stats(update: &ProgUpdate) { +fn print_transfer_stats(update: &ProgUpdate) { print_io_lines(update); print_prog_line(update); } -/// Generate a progress updater that tracks progress, receives updates, and responds to signals. -fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option) -> impl Fn() { +/// Generate a progress updater that tracks progress, receives updates, and responds to progress update requests (signals). +fn gen_prog_updater(rx: mpsc::Receiver, print_level: Option) -> impl Fn() { + // -------------------------------------------------------------- + #[cfg(target_os = "linux")] + const SIGUSR1_USIZE: usize = signal::SIGUSR1 as usize; // -------------------------------------------------------------- fn posixly_correct() -> bool { env::var("POSIXLY_CORRECT").is_ok() } + fn register_signal_handlers(sigval: Arc) -> Result<(), Box> { + #[cfg(target_os = "linux")] + if !posixly_correct() { + signal_hook::flag::register_usize(signal::SIGUSR1, sigval, SIGUSR1_USIZE)?; + } + + Ok(()) + } // -------------------------------------------------------------- move || { - const SIGUSR1_USIZE: usize = signal::SIGUSR1 as usize; - let sigval = Arc::new(AtomicUsize::new(0)); - // TODO: SIGINFO seems to only exist for BSD (and therefore MACOS) - // I will probably want put this behind a feature-gate and may need to pass the value to handle as my own constant. - // This may involve some finagling with the signals library. - // see -> https://unix.stackexchange.com/questions/179481/siginfo-on-gnu-linux-arch-linux-missing - // if let Err(e) = signal_hook::flag::register_usize(signal::SIGINFO, sigval.clone(), signal::SIGINFO as usize) - // { - // debug_println!("Internal dd Warning: Unable to register SIGINFO handler \n\t{}", e); - // } - if !posixly_correct() { - if let Err(e) = - signal_hook::flag::register_usize(signal::SIGUSR1, sigval.clone(), SIGUSR1_USIZE) - { - debug_println!( - "Internal dd Warning: Unable to register SIGUSR1 handler \n\t{}", + register_signal_handlers(sigval.clone()).unwrap_or_else(|e| { + if Some(StatusLevel::None) != print_level { + eprintln!( + "Internal dd Warning: Unable to register signal handler \n\t{}", e ); } - } + }); loop { // Wait for update - let update = match (rx.recv(), xfer_stats) { + let update = match (rx.recv(), print_level) { (Ok(update), Some(StatusLevel::Progress)) => { reprint_prog_line(&update); update } (Ok(update), _) => update, - (Err(_), _) => - // recv only fails permanently - { - break + (Err(_), _) => { + // recv only fails permanently, so we break here to + // avoid recv'ing on a broken pipe + break; } }; // Handle signals - #[allow(clippy::single_match)] - match sigval.load(Ordering::Relaxed) { - SIGUSR1_USIZE => { - print_xfer_stats(&update); - } - // SIGINFO_USIZE => ... - _ => { /* no signals recv'd */ } + #[cfg(target_os = "linux")] + if let SIGUSR1_USIZE = sigval.load(Ordering::Relaxed) { + print_transfer_stats(&update); }; } } @@ -840,7 +761,6 @@ fn gen_prog_updater(rx: mpsc::Receiver, xfer_stats: Option usize { let gcd = Gcd::gcd(ibs, obs); // calculate the lcm from gcd @@ -878,12 +798,10 @@ fn below_count_limit(count: &Option, rstat: &ReadStat, wstat: &WriteS match count { Some(CountType::Reads(n)) => { let n = (*n).try_into().unwrap(); - // debug_assert!(rstat.reads_complete + rstat.reads_partial >= n); rstat.reads_complete + rstat.reads_partial <= n } Some(CountType::Bytes(n)) => { let n = (*n).try_into().unwrap(); - // debug_assert!(wstat.bytes_total >= n); wstat.bytes_total <= n } None => true, @@ -891,8 +809,8 @@ fn below_count_limit(count: &Option, rstat: &ReadStat, wstat: &WriteS } /// Perform the copy/convert operations. Stdout 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. +/// Note: The body of this function should be kept identical to dd_fileout. This is definitely a problem from a maintenance perspective +/// and should be addressed (TODO). The problem exists because some of dd's functionality depends on whether the output is a file or stdout. fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), Box> { let mut rstat = ReadStat { reads_complete: 0, @@ -909,7 +827,7 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), let prog_tx = { let (tx, rx) = mpsc::channel(); - thread::spawn(gen_prog_updater(rx, i.xfer_stats)); + thread::spawn(gen_prog_updater(rx, i.print_level)); tx }; @@ -934,12 +852,8 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), }; // Update Prog prog_tx.send(ProgUpdate { - reads_complete: rstat.reads_complete, - reads_partial: rstat.reads_partial, - writes_complete: wstat.writes_complete, - writes_partial: wstat.writes_partial, - bytes_total: wstat.bytes_total, - records_truncated: rstat.records_truncated, + read_stat: rstat, + write_stat: wstat, duration: start.elapsed(), })?; } @@ -950,15 +864,11 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), o.fdatasync()?; } - match i.xfer_stats { + match i.print_level { Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {} - _ => print_xfer_stats(&ProgUpdate { - reads_complete: rstat.reads_complete, - reads_partial: rstat.reads_partial, - writes_complete: wstat.writes_complete, - writes_partial: wstat.writes_partial, - bytes_total: wstat.bytes_total, - records_truncated: rstat.records_truncated, + _ => print_transfer_stats(&ProgUpdate { + read_stat: rstat, + write_stat: wstat, duration: start.elapsed(), }), } @@ -966,8 +876,8 @@ fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), } /// Perform the copy/convert operations. 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. +/// Note: The body of this function should be kept identical to dd_stdout. This is definitely a problem from a maintenance perspective +/// and should be addressed (TODO). The problem exists because some of dd's functionality depends on whether the output is a file or stdout. fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box> { let mut rstat = ReadStat { reads_complete: 0, @@ -984,7 +894,7 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box(mut i: Input, mut o: Output) -> Result<(), Box(mut i: Input, mut o: Output) -> Result<(), Box {} - _ => print_xfer_stats(&ProgUpdate { - reads_complete: rstat.reads_complete, - reads_partial: rstat.reads_partial, - writes_complete: wstat.writes_complete, - writes_partial: wstat.writes_partial, - bytes_total: wstat.bytes_total, - records_truncated: rstat.records_truncated, + _ => print_transfer_stats(&ProgUpdate { + read_stat: rstat, + write_stat: wstat, duration: start.elapsed(), }), } Ok(()) } -// The compiler does not like Clippy's suggestion to use &str in place of &String here. -#[allow(clippy::ptr_arg)] -fn append_dashes_if_not_present(mut acc: Vec, s: &String) -> Vec { - if Some("--") != s.get(0..=1) { - acc.push(format!("--{}", s)); +fn append_dashes_if_not_present(mut acc: Vec, mut s: String) -> Vec { + if !s.starts_with("--") && !s.starts_with("-") { + s.insert_str(0, "--"); } + acc.push(s); acc } @@ -1074,13 +975,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let dashed_args = args .collect_str(InvalidEncodingHandling::Ignore) .accept_any() - .iter() + .into_iter() .fold(Vec::new(), append_dashes_if_not_present); let matches = uu_app() - // TODO: usage, after_help - //.usage(...) - //.after_help(...) + //.after_help(TODO: Add note about multiplier strings here.) .get_matches_from(dashed_args); let result = match ( @@ -1121,7 +1020,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { match result { Ok(_) => RTN_SUCCESS, Err(e) => { - debug_println!("dd exiting with error:\n\t{}", e); + eprintln!("dd exiting with error:\n\t{}", e); RTN_FAILURE } } @@ -1211,7 +1110,7 @@ Printing performance stats is also triggered by the INFO signal (where supported clap::Arg::with_name(options::CONV) .long(options::CONV) .takes_value(true) - .help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file-flags. Conversion options and file flags may be intermixed. + .help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file flags. Conversion options and file flags may be intermixed. Conversion options: \t One of {ascii, ebcdic, ibm} will perform an encoding conversion. @@ -1231,7 +1130,7 @@ Conversion options: \t 'swab' swaps each adjacent pair of bytes. If an odd number of bytes is present, the final byte is omitted. \t 'sync' pad each ibs-sided block with zeros. If 'block' or 'unblock' is specified, pad with spaces instead. -Flags: +Conversion Flags: \t One of {excl, nocreat} \t\t 'excl' the output file must be created. Fail if the output file is already present. \t\t 'nocreat' the output file will not be created. Fail if the output file in not already present. @@ -1264,10 +1163,6 @@ General-Flags \t 'noctty' do not assign a controlling tty. \t 'nofollow' do not follow system links. -Output-Flags -\t 'append' open file in append mode. Consider setting conv=notrunc as well. -\t 'seek_bytes' a value to seek=N will be interpreted as bytes. - ") ) .arg( diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index 83058948a..1b9698638 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -1,20 +1,7 @@ -/* cspell:disable */ - use super::*; -const NL: u8 = '\n' as u8; -const SPACE: u8 = ' ' as u8; - -macro_rules! rs ( - () => - { - ReadStat { - reads_complete: 0, - reads_partial: 0, - records_truncated: 0, - } - }; - ); +const NL: u8 = b'\n'; +const SPACE: u8 = b' '; macro_rules! make_block_test ( ( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) => @@ -25,7 +12,7 @@ macro_rules! make_block_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: None, + print_level: None, count: None, cflags: IConvFlags { ctable: None, @@ -57,7 +44,7 @@ macro_rules! make_unblock_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: None, + print_level: None, count: None, cflags: IConvFlags { ctable: None, @@ -82,7 +69,7 @@ macro_rules! make_unblock_test ( #[test] fn block_test_no_nl() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8]; let res = block(buf, 4, &mut rs); @@ -91,7 +78,7 @@ fn block_test_no_nl() { #[test] fn block_test_no_nl_short_record() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8]; let res = block(buf, 8, &mut rs); @@ -103,17 +90,18 @@ fn block_test_no_nl_short_record() { #[test] fn block_test_no_nl_trunc() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; let res = block(buf, 4, &mut rs); + // Commented section should be truncated and appear for reference only. assert_eq!(res, vec![vec![0u8, 1u8, 2u8, 3u8 /*, 4u8*/],]); assert_eq!(rs.records_truncated, 1); } #[test] fn block_test_nl_gt_cbs_trunc() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![ 0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8, ]; @@ -122,6 +110,7 @@ fn block_test_nl_gt_cbs_trunc() { assert_eq!( res, vec![ + // Commented lines should be truncated and appear for reference only. vec![0u8, 1u8, 2u8, 3u8], // vec![4u8, SPACE, SPACE, SPACE], vec![0u8, 1u8, 2u8, 3u8], @@ -134,7 +123,7 @@ fn block_test_nl_gt_cbs_trunc() { #[test] fn block_test_surrounded_nl() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; let res = block(buf, 8, &mut rs); @@ -149,7 +138,7 @@ fn block_test_surrounded_nl() { #[test] fn block_test_multiple_nl_same_cbs_block() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8]; let res = block(buf, 8, &mut rs); @@ -165,7 +154,7 @@ fn block_test_multiple_nl_same_cbs_block() { #[test] fn block_test_multiple_nl_diff_cbs_block() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8]; let res = block(buf, 8, &mut rs); @@ -181,7 +170,7 @@ fn block_test_multiple_nl_diff_cbs_block() { #[test] fn block_test_end_nl_diff_cbs_block() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL]; let res = block(buf, 4, &mut rs); @@ -190,7 +179,7 @@ fn block_test_end_nl_diff_cbs_block() { #[test] fn block_test_end_nl_same_cbs_block() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, NL]; let res = block(buf, 4, &mut rs); @@ -199,7 +188,7 @@ fn block_test_end_nl_same_cbs_block() { #[test] fn block_test_double_end_nl() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, NL, NL]; let res = block(buf, 4, &mut rs); @@ -211,7 +200,7 @@ fn block_test_double_end_nl() { #[test] fn block_test_start_nl() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![NL, 0u8, 1u8, 2u8, 3u8]; let res = block(buf, 4, &mut rs); @@ -223,7 +212,7 @@ fn block_test_start_nl() { #[test] fn block_test_double_surrounded_nl_no_trunc() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8]; let res = block(buf, 8, &mut rs); @@ -239,13 +228,14 @@ fn block_test_double_surrounded_nl_no_trunc() { #[test] fn block_test_double_surrounded_nl_double_trunc() { - let mut rs = rs!(); + let mut rs = ReadStat::default(); let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8]; let res = block(buf, 4, &mut rs); assert_eq!( res, vec![ + // Commented section should be truncated and appear for reference only. vec![0u8, 1u8, 2u8, 3u8], vec![SPACE, SPACE, SPACE, SPACE], vec![4u8, 5u8, 6u8, 7u8 /*, 8u8*/], diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 5d7dcd5e3..8563985c6 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -1,5 +1,3 @@ -/* cspell:disable */ - use super::*; macro_rules! make_sync_test ( @@ -11,7 +9,7 @@ macro_rules! make_sync_test ( src: $src, non_ascii: false, ibs: $ibs, - xfer_stats: None, + print_level: None, count: None, cflags: IConvFlags { ctable: None, diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 738ead5fd..5276f38b3 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -1,5 +1,3 @@ -/* cspell:disable */ - use super::*; macro_rules! make_conv_test ( @@ -11,7 +9,7 @@ macro_rules! make_conv_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: None, + print_level: None, count: None, cflags: icf!($ctable), iflags: DEFAULT_IFLAGS, @@ -36,7 +34,7 @@ macro_rules! make_icf_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: None, + print_level: None, count: None, cflags: $icf, iflags: DEFAULT_IFLAGS, @@ -141,7 +139,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { .unwrap(), non_ascii: false, ibs: 128, - xfer_stats: None, + print_level: None, count: None, cflags: icf!(Some(&ASCII_TO_EBCDIC)), iflags: DEFAULT_IFLAGS, @@ -163,7 +161,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { src: File::open(&tmp_fname_ae).unwrap(), non_ascii: false, ibs: 256, - xfer_stats: None, + print_level: None, count: None, cflags: icf!(Some(&EBCDIC_TO_ASCII)), iflags: DEFAULT_IFLAGS, diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index 49edadd97..fc8836e11 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -1,5 +1,3 @@ -/* cspell:disable */ - use super::*; mod block_unblock_tests; @@ -39,24 +37,6 @@ const DEFAULT_IFLAGS: IFlags = IFlags { 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, -// }; - struct LazyReader { src: R, } @@ -102,7 +82,7 @@ macro_rules! make_spec_test ( src: $src, non_ascii: false, ibs: 512, - xfer_stats: None, + print_level: None, count: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index 02355964b..fff942d26 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -1,5 +1,3 @@ -/* cspell:disable */ - use super::*; const DST_PLACEHOLDER: Vec = Vec::new(); @@ -52,7 +50,7 @@ make_io_test!( src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), non_ascii: false, ibs: 521, - xfer_stats: None, + print_level: None, count: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -72,7 +70,7 @@ make_io_test!( src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), non_ascii: false, ibs: 1031, - xfer_stats: None, + print_level: None, count: None, cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -92,7 +90,7 @@ make_io_test!( src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), non_ascii: false, ibs: 1024, - xfer_stats: None, + print_level: None, count: Some(CountType::Reads(32)), cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -112,7 +110,7 @@ make_io_test!( src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), non_ascii: false, ibs: 531, - xfer_stats: None, + print_level: None, count: Some(CountType::Bytes(32 * 1024)), cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -132,7 +130,7 @@ make_io_test!( src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), non_ascii: false, ibs: 1024, - xfer_stats: None, + print_level: None, count: Some(CountType::Reads(16)), cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -152,7 +150,7 @@ make_io_test!( src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(), non_ascii: false, ibs: 531, - xfer_stats: None, + print_level: None, count: Some(CountType::Bytes(12345)), cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -172,7 +170,7 @@ make_io_test!( src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), non_ascii: false, ibs: 1024, - xfer_stats: None, + print_level: None, count: Some(CountType::Reads(32)), cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -192,7 +190,7 @@ make_io_test!( src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(), non_ascii: false, ibs: 521, - xfer_stats: None, + print_level: None, count: Some(CountType::Bytes(32 * 1024)), cflags: icf!(), iflags: DEFAULT_IFLAGS, @@ -215,7 +213,7 @@ make_io_test!( }, non_ascii: false, ibs: 521, - xfer_stats: None, + print_level: None, count: None, cflags: icf!(), iflags: IFlags { diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 72645205f..3b9b4dd11 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -5,8 +5,6 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -/* cspell:disable */ - #[cfg(test)] mod unit_tests; @@ -24,9 +22,8 @@ pub enum ParseError { MultipleExclNoCreat, FlagNoMatch(String), ConvFlagNoMatch(String), - NoMatchingMultiplier(String), - ByteStringContainsNoValue(String), - MultiplierStringWouldOverflow(String), + MultiplierStringParseFailure(String), + MultiplierStringOverflow(String), BlockUnblockWithoutCBS, StatusLevelNotRecognized(String), Unimplemented(String), @@ -56,13 +53,10 @@ impl std::fmt::Display for ParseError { Self::ConvFlagNoMatch(arg) => { write!(f, "Unrecognized conv=CONV -> {}", arg) } - Self::NoMatchingMultiplier(arg) => { + Self::MultiplierStringParseFailure(arg) => { write!(f, "Unrecognized byte multiplier -> {}", arg) } - Self::ByteStringContainsNoValue(arg) => { - write!(f, "Unrecognized byte value -> {}", arg) - } - Self::MultiplierStringWouldOverflow(arg) => { + Self::MultiplierStringOverflow(arg) => { write!( f, "Multiplier string would overflow on current system -> {}", @@ -302,61 +296,40 @@ impl std::str::FromStr for StatusLevel { } } -fn parse_multiplier(s: &'_ str) -> Result { - let mult: u128 = match s { - "c" => 1, - "w" => 2, - "b" => 512, - "kB" => 1000, - "K" | "KiB" => 1024, - "MB" => 1000 * 1000, - "M" | "MiB" => 1024 * 1024, - "GB" => 1000 * 1000 * 1000, - "G" | "GiB" => 1024 * 1024 * 1024, - "TB" => 1000 * 1000 * 1000 * 1000, - "T" | "TiB" => 1024 * 1024 * 1024 * 1024, - "PB" => 1000 * 1000 * 1000 * 1000 * 1000, - "P" | "PiB" => 1024 * 1024 * 1024 * 1024 * 1024, - "EB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000, - "E" | "EiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024, - "ZB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000, - "Z" | "ZiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, - "YB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000, - "Y" | "YiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, - _ => return Err(ParseError::NoMatchingMultiplier(s.to_string())), - }; - - mult.try_into() - .map_err(|_e| ParseError::MultiplierStringWouldOverflow(s.to_string())) -} - +/// Parse bytes using str::parse, then map error if needed. fn parse_bytes_only(s: &str) -> Result { - match s.parse() { - Ok(bytes) => Ok(bytes), - Err(_) => Err(ParseError::ByteStringContainsNoValue(s.to_string())), - } + s.parse() + .map_err(|_| ParseError::MultiplierStringParseFailure(s.to_string())) } +/// Parse byte and multiplier like 512, 5KiB, or 1G. +/// Uses uucore::parse_size, and adds the 'w' and 'c' suffixes which are mentioned +/// in dd's info page. fn parse_bytes_with_opt_multiplier(s: &str) -> Result { - match s.find(char::is_alphabetic) { - Some(idx) => { - let base = parse_bytes_only(&s[..idx])?; - let mult = parse_multiplier(&s[idx..])?; + if let Some(idx) = s.rfind('c') { + parse_bytes_only(&s[..idx]) + } else if let Some(idx) = s.rfind('w') { + let partial = parse_bytes_only(&s[..idx])?; - if let Some(bytes) = base.checked_mul(mult) { - Ok(bytes) - } else { - Err(ParseError::MultiplierStringWouldOverflow(s.to_string())) + partial + .checked_mul(2) + .ok_or_else(|| ParseError::MultiplierStringOverflow(s.to_string())) + } else { + uucore::parse_size::parse_size(s).map_err(|e| match e { + uucore::parse_size::ParseSizeError::ParseFailure(s) => { + ParseError::MultiplierStringParseFailure(s) } - } - _ => parse_bytes_only(s), + uucore::parse_size::ParseSizeError::SizeTooBig(s) => { + ParseError::MultiplierStringOverflow(s) + } + }) } } pub fn parse_ibs(matches: &Matches) -> Result { - if let Some(mixed_str) = matches.value_of("bs") { + if let Some(mixed_str) = matches.value_of(options::BS) { parse_bytes_with_opt_multiplier(mixed_str) - } else if let Some(mixed_str) = matches.value_of("ibs") { + } else if let Some(mixed_str) = matches.value_of(options::IBS) { parse_bytes_with_opt_multiplier(mixed_str) } else { Ok(512) @@ -364,7 +337,7 @@ pub fn parse_ibs(matches: &Matches) -> Result { } fn parse_cbs(matches: &Matches) -> Result, ParseError> { - if let Some(s) = matches.value_of("cbs") { + if let Some(s) = matches.value_of(options::CBS) { let bytes = parse_bytes_with_opt_multiplier(s)?; Ok(Some(bytes)) } else { @@ -373,7 +346,7 @@ fn parse_cbs(matches: &Matches) -> Result, ParseError> { } pub fn parse_status_level(matches: &Matches) -> Result, ParseError> { - match matches.value_of("status") { + match matches.value_of(options::STATUS) { Some(s) => { let st = s.parse()?; Ok(Some(st)) diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 25a32eb68..60b027bb6 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -1,5 +1,3 @@ -/* cspell:disable */ - use super::*; use crate::StatusLevel; @@ -7,7 +5,7 @@ use crate::StatusLevel; #[cfg(not(target_os = "linux"))] #[test] fn unimplemented_flags_should_error_non_unix() { - let mut unfailed = Vec::new(); + let mut succeeded = Vec::new(); // The following flags are only implemented in linux for flag in vec![ @@ -28,26 +26,26 @@ fn unimplemented_flags_should_error_non_unix() { let matches = uu_app().get_matches_from_safe(args).unwrap(); match parse_iflags(&matches) { - Ok(_) => unfailed.push(format!("iflag={}", flag)), + Ok(_) => succeeded.push(format!("iflag={}", flag)), Err(_) => { /* expected behaviour :-) */ } } match parse_oflags(&matches) { - Ok(_) => unfailed.push(format!("oflag={}", flag)), + Ok(_) => succeeded.push(format!("oflag={}", flag)), Err(_) => { /* expected behaviour :-) */ } } } - if !unfailed.is_empty() { + if !succeeded.is_empty() { panic!( "The following flags did not panic as expected: {:?}", - unfailed + succeeded ); } } #[test] fn unimplemented_flags_should_error() { - let mut unfailed = Vec::new(); + let mut succeeded = Vec::new(); // The following flags are not implemented for flag in vec!["cio", "nocache", "nolinks", "text", "binary"] { @@ -59,19 +57,19 @@ fn unimplemented_flags_should_error() { let matches = uu_app().get_matches_from_safe(args).unwrap(); match parse_iflags(&matches) { - Ok(_) => unfailed.push(format!("iflag={}", flag)), + Ok(_) => succeeded.push(format!("iflag={}", flag)), Err(_) => { /* expected behaviour :-) */ } } match parse_oflags(&matches) { - Ok(_) => unfailed.push(format!("oflag={}", flag)), + Ok(_) => succeeded.push(format!("oflag={}", flag)), Err(_) => { /* expected behaviour :-) */ } } } - if !unfailed.is_empty() { + if !succeeded.is_empty() { panic!( "The following flags did not panic as expected: {:?}", - unfailed + succeeded ); } } @@ -105,6 +103,176 @@ fn test_status_level_none() { assert_eq!(st, StatusLevel::None); } +#[test] +fn test_all_top_level_args_no_leading_dashes_sep_by_equals() { + let args = vec![ + String::from("dd"), + String::from("if=foo.file"), + String::from("of=bar.file"), + String::from("ibs=10"), + String::from("obs=10"), + String::from("cbs=1"), + String::from("bs=100"), + String::from("count=2"), + String::from("skip=2"), + String::from("seek=2"), + String::from("status=progress"), + String::from("conv=ascii,ucase"), + String::from("iflag=count_bytes,skip_bytes"), + String::from("oflag=append,seek_bytes"), + ]; + let args = args + .into_iter() + .fold(Vec::new(), append_dashes_if_not_present); + + let matches = uu_app().get_matches_from_safe(args).unwrap(); + + assert_eq!(100, parse_ibs(&matches).unwrap()); + assert_eq!(100, parse_obs(&matches).unwrap()); + assert_eq!(1, parse_cbs(&matches).unwrap().unwrap()); + assert_eq!( + CountType::Bytes(2), + parse_count( + &IFlags { + count_bytes: true, + ..IFlags::default() + }, + &matches + ) + .unwrap() + .unwrap() + ); + assert_eq!( + 200, + parse_skip_amt(&100, &IFlags::default(), &matches) + .unwrap() + .unwrap() + ); + assert_eq!( + 200, + parse_seek_amt(&100, &OFlags::default(), &matches) + .unwrap() + .unwrap() + ); + assert_eq!( + StatusLevel::Progress, + parse_status_level(&matches).unwrap().unwrap() + ); + assert_eq!( + IConvFlags { + ctable: Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE), + ..IConvFlags::default() + }, + parse_conv_flag_input(&matches).unwrap() + ); + assert_eq!( + OConvFlags::default(), + parse_conv_flag_output(&matches).unwrap() + ); + assert_eq!( + IFlags { + count_bytes: true, + skip_bytes: true, + ..IFlags::default() + }, + parse_iflags(&matches).unwrap() + ); + assert_eq!( + OFlags { + append: true, + seek_bytes: true, + ..OFlags::default() + }, + parse_oflags(&matches).unwrap() + ); +} + +#[ignore] +#[test] +// TODO: This should work, but Clap doesn't seem to understand it. Leaving it for now since the traditional dd if=foo.file works just fine. +fn test_all_top_level_args_leading_dashes_sep_by_spaces() { + let args = vec![ + String::from("dd"), + String::from("--if foo.file"), + String::from("--of bar.file"), + String::from("--ibs 10"), + String::from("--obs 10"), + String::from("--cbs 1"), + String::from("--bs 100"), + String::from("--count 2"), + String::from("--skip 2"), + String::from("--seek 2"), + String::from("--status progress"), + String::from("--conv ascii,ucase"), + String::from("--iflag count_bytes,skip_bytes"), + String::from("--oflag append,seek_bytes"), + ]; + let args = args + .into_iter() + .fold(Vec::new(), append_dashes_if_not_present); + + let matches = uu_app().get_matches_from_safe(args).unwrap(); + + assert_eq!(100, parse_ibs(&matches).unwrap()); + assert_eq!(100, parse_obs(&matches).unwrap()); + assert_eq!(1, parse_cbs(&matches).unwrap().unwrap()); + assert_eq!( + CountType::Bytes(2), + parse_count( + &IFlags { + count_bytes: true, + ..IFlags::default() + }, + &matches + ) + .unwrap() + .unwrap() + ); + assert_eq!( + 200, + parse_skip_amt(&100, &IFlags::default(), &matches) + .unwrap() + .unwrap() + ); + assert_eq!( + 200, + parse_seek_amt(&100, &OFlags::default(), &matches) + .unwrap() + .unwrap() + ); + assert_eq!( + StatusLevel::Progress, + parse_status_level(&matches).unwrap().unwrap() + ); + assert_eq!( + IConvFlags { + ctable: Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE), + ..IConvFlags::default() + }, + parse_conv_flag_input(&matches).unwrap() + ); + assert_eq!( + OConvFlags::default(), + parse_conv_flag_output(&matches).unwrap() + ); + assert_eq!( + IFlags { + count_bytes: true, + skip_bytes: true, + ..IFlags::default() + }, + parse_iflags(&matches).unwrap() + ); + assert_eq!( + OFlags { + append: true, + seek_bytes: true, + ..OFlags::default() + }, + parse_oflags(&matches).unwrap() + ); +} + #[test] fn test_status_level_progress() { let args = vec![ @@ -374,16 +542,6 @@ test_byte_parser!( 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 ); -#[test] -#[should_panic] -#[allow(non_snake_case)] -fn test_KB_multiplier_error() { - // KB is not valid (kB, K, and KiB are) - let bs_str = "2000KB"; - - parse_bytes_with_opt_multiplier(bs_str).unwrap(); -} - #[test] #[should_panic] fn test_overflow_panic() { @@ -395,7 +553,7 @@ fn test_overflow_panic() { #[test] #[should_panic] fn test_neg_panic() { - let bs_str = format!("{}KiB", -1); + let bs_str = format!("{}", -1); parse_bytes_with_opt_multiplier(&bs_str).unwrap(); } diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index de6e510a9..083a5bf1b 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,5 +1,3 @@ -/* cspell:disable */ - use crate::common::util::*; use std::fs::{File, OpenOptions}; From 989849eca7e5d033222e40476d028a74228cf433 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 21 Jul 2021 18:24:31 -0700 Subject: [PATCH 56/66] Ignores linux-only tests on non-linux platforms. --- src/uu/dd/src/dd.rs | 2 +- src/uu/dd/src/parseargs.rs | 218 ++++++++++++++++--------------------- tests/by-util/test_dd.rs | 8 +- 3 files changed, 99 insertions(+), 129 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 712e10e1e..717c9d88b 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -943,7 +943,7 @@ fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box, mut s: String) -> Vec { - if !s.starts_with("--") && !s.starts_with("-") { + if !s.starts_with("--") && !s.starts_with('-') { s.insert_str(0, "--"); } acc.push(s); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 3b9b4dd11..c0e22c520 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -508,170 +508,136 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result Result { - let flags = parse_flag_list("conv", matches)?; + let flags = parse_flag_list(options::CONV, matches)?; - let mut sparse = false; - let mut excl = false; - let mut nocreat = false; - let mut notrunc = false; - let mut fdatasync = false; - let mut fsync = false; + let mut oconvflags = OConvFlags { + sparse: false, + excl: false, + nocreat: false, + notrunc: false, + fdatasync: false, + fsync: false, + }; for flag in flags { match flag { - ConvFlag::Sparse => sparse = true, + ConvFlag::Sparse => oconvflags.sparse = true, ConvFlag::Excl => { - if !nocreat { - excl = true; + if !oconvflags.nocreat { + oconvflags.excl = true; } else { return Err(ParseError::MultipleExclNoCreat); } } ConvFlag::NoCreat => { - if !excl { - nocreat = true; + if !oconvflags.excl { + oconvflags.nocreat = true; } else { return Err(ParseError::MultipleExclNoCreat); } } - ConvFlag::NoTrunc => notrunc = true, - ConvFlag::FDataSync => fdatasync = true, - ConvFlag::FSync => fsync = true, + ConvFlag::NoTrunc => oconvflags.notrunc = true, + ConvFlag::FDataSync => oconvflags.fdatasync = true, + ConvFlag::FSync => oconvflags.fsync = true, _ => {} } } - Ok(OConvFlags { - sparse, - excl, - nocreat, - notrunc, - fdatasync, - fsync, - }) + Ok(oconvflags) } /// Parse IFlags struct from CL-input pub fn parse_iflags(matches: &Matches) -> 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 mut 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, + }; - let flags = parse_flag_list("iflag", matches)?; + let flags = parse_flag_list(options::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::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, + Flag::Cio => iflags.cio = true, + Flag::Direct => iflags.direct = true, + Flag::Directory => iflags.directory = true, + Flag::Dsync => iflags.dsync = true, + Flag::Sync => iflags.sync = true, + Flag::NoCache => iflags.nocache = true, + Flag::NonBlock => iflags.nonblock = true, + Flag::NoATime => iflags.noatime = true, + Flag::NoCtty => iflags.noctty = true, + Flag::NoFollow => iflags.nofollow = true, + Flag::NoLinks => iflags.nolinks = true, + Flag::Binary => iflags.binary = true, + Flag::Text => iflags.text = true, + Flag::FullBlock => iflags.fullblock = true, + Flag::CountBytes => iflags.count_bytes = true, + Flag::SkipBytes => iflags.skip_bytes = true, _ => {} } } - Ok(IFlags { - cio, - direct, - directory, - dsync, - sync, - nocache, - nonblock, - noatime, - noctty, - nofollow, - nolinks, - binary, - text, - fullblock, - count_bytes, - skip_bytes, - }) + Ok(iflags) } /// Parse OFlags struct from CL-input pub fn parse_oflags(matches: &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 mut 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, + }; - let flags = parse_flag_list("oflag", matches)?; + let flags = parse_flag_list(options::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::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, + Flag::Append => oflags.append = true, + Flag::Cio => oflags.cio = true, + Flag::Direct => oflags.direct = true, + Flag::Directory => oflags.directory = true, + Flag::Dsync => oflags.dsync = true, + Flag::Sync => oflags.sync = true, + Flag::NoCache => oflags.nocache = true, + Flag::NonBlock => oflags.nonblock = true, + Flag::NoATime => oflags.noatime = true, + Flag::NoCtty => oflags.noctty = true, + Flag::NoFollow => oflags.nofollow = true, + Flag::NoLinks => oflags.nolinks = true, + Flag::Binary => oflags.binary = true, + Flag::Text => oflags.text = true, + Flag::SeekBytes => oflags.seek_bytes = true, _ => {} } } - Ok(OFlags { - append, - cio, - direct, - directory, - dsync, - sync, - nocache, - nonblock, - noatime, - noctty, - nofollow, - nolinks, - binary, - text, - seek_bytes, - }) + Ok(oflags) } /// Parse the amount of the input file to skip. @@ -680,7 +646,7 @@ pub fn parse_skip_amt( iflags: &IFlags, matches: &Matches, ) -> Result, ParseError> { - if let Some(amt) = matches.value_of("skip") { + if let Some(amt) = matches.value_of(options::SKIP) { let n = parse_bytes_with_opt_multiplier(amt)?; if iflags.skip_bytes { Ok(Some(n)) @@ -698,7 +664,7 @@ pub fn parse_seek_amt( oflags: &OFlags, matches: &Matches, ) -> Result, ParseError> { - if let Some(amt) = matches.value_of("seek") { + if let Some(amt) = matches.value_of(options::SEEK) { let n = parse_bytes_with_opt_multiplier(amt)?; if oflags.seek_bytes { Ok(Some(n)) @@ -712,7 +678,7 @@ pub fn parse_seek_amt( /// Parse the value of count=N and the type of N implied by iflags pub fn parse_count(iflags: &IFlags, matches: &Matches) -> Result, ParseError> { - if let Some(amt) = matches.value_of("count") { + if let Some(amt) = matches.value_of(options::COUNT) { let n = parse_bytes_with_opt_multiplier(amt)?; if iflags.count_bytes { Ok(Some(CountType::Bytes(n))) @@ -726,7 +692,7 @@ pub fn parse_count(iflags: &IFlags, matches: &Matches) -> Result Result { - if let Some(conv_opts) = matches.value_of("conv") { + if let Some(conv_opts) = matches.value_of(options::CONV) { Ok(conv_opts.contains("ascii")) } else { Ok(false) diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 083a5bf1b..a601190d9 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -202,6 +202,7 @@ fn test_final_stats_unspec() { new_ucmd!().run().stderr_only(&output).success(); } +#[cfg(target_os = "linux")] #[test] fn test_excl_causes_failure_when_present() { let fname = "this-file-exists-excl.txt"; @@ -212,7 +213,7 @@ fn test_excl_causes_failure_when_present() { .fails(); } -#[ignore] +#[cfg(target_os = "linux")] #[test] fn test_atime_updated() { let fname = "this-file-exists-no-noatime.txt"; @@ -230,6 +231,7 @@ fn test_atime_updated() { assert!(pre_atime != post_atime); } +#[cfg(target_os = "linux")] #[test] fn test_noatime_does_not_update_infile_atime() { let fname = "this-ifile-exists-noatime.txt"; @@ -246,6 +248,7 @@ fn test_noatime_does_not_update_infile_atime() { assert_eq!(pre_atime, post_atime); } +#[cfg(target_os = "linux")] #[test] fn test_noatime_does_not_update_ofile_atime() { let fname = "this-ofile-exists-noatime.txt"; @@ -262,6 +265,7 @@ fn test_noatime_does_not_update_ofile_atime() { assert_eq!(pre_atime, post_atime); } +#[cfg(target_os = "linux")] #[test] fn test_nocreat_causes_failure_when_outfile_not_present() { let fname = "this-file-does-not-exist.txt"; @@ -345,7 +349,7 @@ fn test_null_fullblock() { } #[cfg(unix)] -// #[ignore] // See note below before running this test! +#[ignore] // See note below before using this test. #[test] fn test_fullblock() { let tname = "fullblock-from-urand"; From 56c2f503d41d6b485e5564e985a9006947f616de Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 22 Jul 2021 15:49:17 -0700 Subject: [PATCH 57/66] Addresses build errors - Adds words to cspell exceptions - Converts test macros to use Default trait. - Converts parser to use Default trait. - Adds Windows-friendly test files for block/unblock when nl is present in test/spec file. --- Cargo.lock | 7 -- src/uu/dd/Cargo.toml | 1 - src/uu/dd/cspell.json | 25 +++- src/uu/dd/src/dd.rs | 32 +++-- .../src/dd_unit_tests/block_unblock_tests.rs | 44 ++++--- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 10 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 8 +- src/uu/dd/src/dd_unit_tests/mod.rs | 44 +------ src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 66 +++++------ src/uu/dd/src/parseargs.rs | 109 ++++++------------ src/uu/dd/src/parseargs/unit_tests.rs | 50 ++++---- .../dd/test-resources/dd-block-cbs16-win.test | 16 +++ .../dd-block-consecutive-nl-win.test | 4 + .../test-resources/dd-unblock-cbs16-win.spec | 16 +++ .../test-resources/dd-unblock-cbs8-win.spec | 32 +++++ tests/by-util/test_dd.rs | 24 +--- tests/fixtures/dd/cspell.json | 23 ++++ 17 files changed, 265 insertions(+), 246 deletions(-) create mode 100644 src/uu/dd/test-resources/dd-block-cbs16-win.test create mode 100644 src/uu/dd/test-resources/dd-block-consecutive-nl-win.test create mode 100644 src/uu/dd/test-resources/dd-unblock-cbs16-win.spec create mode 100644 src/uu/dd/test-resources/dd-unblock-cbs8-win.spec diff --git a/Cargo.lock b/Cargo.lock index 1aa3c3416..7a34ad49b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -543,12 +543,6 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" -[[package]] -name = "debug_print" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f215f9b7224f49fb73256115331f677d868b34d18b65dbe4db392e6021eea90" - [[package]] name = "diff" version = "0.1.12" @@ -1917,7 +1911,6 @@ version = "0.0.7" dependencies = [ "byte-unit", "clap", - "debug_print", "gcd", "libc", "signal-hook", diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index f65d0bf12..da0165245 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -17,7 +17,6 @@ path = "src/dd.rs" [dependencies] byte-unit = "4.0" clap = { version = "2.33", features = [ "wrap_help" ] } -debug_print = "1.0" gcd = "2.0" libc = "0.2" signal-hook = "0.3.9" diff --git a/src/uu/dd/cspell.json b/src/uu/dd/cspell.json index fb29630b2..4e47b090f 100644 --- a/src/uu/dd/cspell.json +++ b/src/uu/dd/cspell.json @@ -17,7 +17,9 @@ "fullblock", "noxfer", "iflag", + "iflags", "oflag", + "oflags", "infile", "outfile", "fileio", @@ -33,9 +35,30 @@ "datastructures", "creat", "mult", - "unfailed", "behaviour", "ctty", + "noatime", + "fname", + "fileout", + "ofile", + "doesnt", + "parseargs", + "rlen", + "wlen", + "rstat", + "wstat", + "rposition", + "btotal", + "sigval", + "sigusr", + "rmax", + "rsofar", + "rremain", + "bremain", + "oconvflags", + "fpath", + "testfile", + "specfile" ], "ignorePaths": [ "**/test-resources/*.test", diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 717c9d88b..d0faaacf7 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -24,6 +24,7 @@ use conversion_tables::*; use byte_unit::Byte; use clap::{self, crate_version}; use gcd::Gcd; +#[cfg(target_os = "linux")] use signal_hook::consts::signal; use std::cmp; use std::convert::TryInto; @@ -34,7 +35,9 @@ use std::io::{self, Read, Seek, Write}; #[cfg(target_os = "linux")] use std::os::unix::fs::OpenOptionsExt; use std::path::Path; -use std::sync::{atomic::AtomicUsize, atomic::Ordering, mpsc, Arc}; +#[cfg(target_os = "linux")] +use std::sync::atomic::Ordering; +use std::sync::{atomic::AtomicUsize, mpsc, Arc}; use std::thread; use std::time; @@ -168,15 +171,15 @@ impl Input { impl Read for Input { fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut base_idx = 0; - let tlen = buf.len(); + let target_len = buf.len(); loop { match self.src.read(&mut buf[base_idx..]) { Ok(0) => return Ok(base_idx), Ok(rlen) if self.iflags.fullblock => { base_idx += rlen; - if base_idx >= tlen { - return Ok(tlen); + if base_idx >= target_len { + return Ok(target_len); } } Ok(len) => return Ok(len), @@ -707,6 +710,7 @@ fn gen_prog_updater(rx: mpsc::Receiver, print_level: Option bool { env::var("POSIXLY_CORRECT").is_ok() } @@ -731,21 +735,11 @@ fn gen_prog_updater(rx: mpsc::Receiver, print_level: Option { - reprint_prog_line(&update); - - update - } - (Ok(update), _) => update, - (Err(_), _) => { - // recv only fails permanently, so we break here to - // avoid recv'ing on a broken pipe - break; - } - }; + while let Ok(update) = rx.recv() { + // (Re)print status line if progress is requested. + if Some(StatusLevel::Progress) == print_level { + reprint_prog_line(&update); + } // Handle signals #[cfg(target_os = "linux")] if let SIGUSR1_USIZE = sigval.load(Ordering::Relaxed) { diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index 1b9698638..e343a227f 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -15,19 +15,15 @@ macro_rules! make_block_test ( print_level: None, count: None, cflags: IConvFlags { - ctable: None, block: $block, - unblock: None, - swab: false, - sync: None, - noerror: false, + ..IConvFlags::default() }, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -54,12 +50,12 @@ macro_rules! make_unblock_test ( sync: None, noerror: false, }, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -247,7 +243,11 @@ fn block_test_double_surrounded_nl_double_trunc() { make_block_test!( block_cbs16, "block-cbs-16", - File::open("./test-resources/dd-block-cbs16.test").unwrap(), + if cfg!(unix) { + File::open("./test-resources/dd-block-cbs16.test").unwrap() + } else { + File::open("./test-resources/dd-block-cbs16-win.test").unwrap() + }, Some(16), File::open("./test-resources/dd-block-cbs16.spec").unwrap() ); @@ -255,7 +255,11 @@ make_block_test!( make_block_test!( block_cbs16_as_cbs8, "block-cbs-16-as-cbs8", - File::open("./test-resources/dd-block-cbs16.test").unwrap(), + if cfg!(unix) { + File::open("./test-resources/dd-block-cbs16.test").unwrap() + } else { + File::open("./test-resources/dd-block-cbs16-win.test").unwrap() + }, Some(8), File::open("./test-resources/dd-block-cbs8.spec").unwrap() ); @@ -263,7 +267,11 @@ make_block_test!( make_block_test!( block_consecutive_nl, "block-consecutive-nl", - File::open("./test-resources/dd-block-consecutive-nl.test").unwrap(), + if cfg!(unix) { + File::open("./test-resources/dd-block-consecutive-nl.test").unwrap() + } else { + File::open("./test-resources/dd-block-consecutive-nl-win.test").unwrap() + }, Some(16), File::open("./test-resources/dd-block-consecutive-nl-cbs16.spec").unwrap() ); @@ -335,7 +343,11 @@ make_unblock_test!( "unblock-multi-16", File::open("./test-resources/dd-unblock-cbs16.test").unwrap(), Some(16), - File::open("./test-resources/dd-unblock-cbs16.spec").unwrap() + if cfg!(unix) { + File::open("./test-resources/dd-unblock-cbs16.spec").unwrap() + } else { + File::open("./test-resources/dd-unblock-cbs16-win.spec").unwrap() + }, ); make_unblock_test!( @@ -343,5 +355,9 @@ make_unblock_test!( "unblock-multi-16-as-8", File::open("./test-resources/dd-unblock-cbs16.test").unwrap(), Some(8), - File::open("./test-resources/dd-unblock-cbs8.spec").unwrap() + if cfg!(unix) { + File::open("./test-resources/dd-unblock-cbs8.spec").unwrap() + } else { + File::open("./test-resources/dd-unblock-cbs8-win.spec").unwrap() + }, ); diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 8563985c6..945fbe7af 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -12,19 +12,15 @@ macro_rules! make_sync_test ( print_level: None, count: None, cflags: IConvFlags { - ctable: None, - block: None, - unblock: None, - swab: false, sync: $sync, - noerror: false, + ..IConvFlags::default() }, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: $obs, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 5276f38b3..2004a5624 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -12,12 +12,12 @@ macro_rules! make_conv_test ( print_level: None, count: None, cflags: icf!($ctable), - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -37,12 +37,12 @@ macro_rules! make_icf_test ( print_level: None, count: None, cflags: $icf, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index fc8836e11..90ef00eaf 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -9,34 +9,6 @@ use std::fs; use std::io::prelude::*; use std::io::BufReader; -const DEFAULT_CFO: OConvFlags = OConvFlags { - sparse: false, - excl: false, - nocreat: false, - notrunc: false, - fdatasync: false, - 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, -}; - struct LazyReader { src: R, } @@ -50,19 +22,11 @@ impl Read for LazyReader { #[macro_export] macro_rules! icf ( - () => - { - icf!(None) - }; ( $ctable:expr ) => { IConvFlags { ctable: $ctable, - block: None, - unblock: None, - swab: false, - sync: None, - noerror: false, + ..IConvFlags::default() } }; ); @@ -84,13 +48,13 @@ macro_rules! make_spec_test ( ibs: 512, print_level: None, count: None, - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index fff942d26..cc19d3241 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -52,8 +52,8 @@ make_io_test!( ibs: 521, print_level: None, count: None, - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, @@ -72,13 +72,13 @@ make_io_test!( ibs: 1031, print_level: None, count: None, - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 521, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); @@ -92,13 +92,13 @@ make_io_test!( ibs: 1024, print_level: None, count: Some(CountType::Reads(32)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1024, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); @@ -112,13 +112,13 @@ make_io_test!( ibs: 531, print_level: None, count: Some(CountType::Bytes(32 * 1024)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); @@ -132,13 +132,13 @@ make_io_test!( ibs: 1024, print_level: None, count: Some(CountType::Reads(16)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-deadbeef-first-16k.spec").unwrap() ); @@ -152,13 +152,13 @@ make_io_test!( ibs: 531, print_level: None, count: Some(CountType::Bytes(12345)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-deadbeef-first-12345.spec").unwrap() ); @@ -172,13 +172,13 @@ make_io_test!( ibs: 1024, print_level: None, count: Some(CountType::Reads(32)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1024, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); @@ -192,13 +192,13 @@ make_io_test!( ibs: 521, print_level: None, count: Some(CountType::Bytes(32 * 1024)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); @@ -218,27 +218,13 @@ make_io_test!( cflags: icf!(), iflags: IFlags { fullblock: true, - 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, - count_bytes: false, - skip_bytes: false, + ..IFlags::default() }, }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); @@ -316,7 +302,7 @@ fn bsize_test_bs_eq() { #[test] #[should_panic] -fn test_nocreate_causes_failure_when_ofile_doesnt_exist() { +fn test_nocreat_causes_failure_when_ofile_doesnt_exist() { let args = vec![ String::from("dd"), String::from("--conv=nocreat"), diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index c0e22c520..a27ab3cb0 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -186,7 +186,7 @@ impl std::str::FromStr for Flag { "direct" => // Ok(Self::Direct), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Direct) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -195,7 +195,7 @@ impl std::str::FromStr for Flag { "directory" => // Ok(Self::Directory), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Directory) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -204,7 +204,7 @@ impl std::str::FromStr for Flag { "dsync" => // Ok(Self::Dsync), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Dsync) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -213,7 +213,7 @@ impl std::str::FromStr for Flag { "sync" => // Ok(Self::Sync), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Sync) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -227,7 +227,7 @@ impl std::str::FromStr for Flag { "nonblock" => // Ok(Self::NonBlock), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NonBlock) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -236,7 +236,7 @@ impl std::str::FromStr for Flag { "noatime" => // Ok(Self::NoATime), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NoATime) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -245,7 +245,7 @@ impl std::str::FromStr for Flag { "noctty" => // Ok(Self::NoCtty), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NoCtty) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -254,7 +254,7 @@ impl std::str::FromStr for Flag { "nofollow" => // Ok(Self::NoFollow), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NoFollow) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -421,16 +421,13 @@ fn parse_flag_list>( /// Parse Conversion Options (Input Variety) /// Construct and validate a IConvFlags pub fn parse_conv_flag_input(matches: &Matches) -> Result { - let flags = parse_flag_list("conv", matches)?; - let cbs = parse_cbs(matches)?; - + let mut iconvflags = IConvFlags::default(); let mut fmt = None; let mut case = None; - let mut block = None; - let mut unblock = None; - let mut swab = false; - let mut sync = false; - let mut noerror = false; + let mut is_sync = false; + + let flags = parse_flag_list(options::CONV, matches)?; + let cbs = parse_cbs(matches)?; for flag in flags { match flag { @@ -469,27 +466,36 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result match (cbs, unblock) { - (Some(cbs), None) => block = Some(cbs), + ConvFlag::Block => match (cbs, iconvflags.unblock) { + (Some(cbs), None) => iconvflags.block = Some(cbs), (None, _) => return Err(ParseError::BlockUnblockWithoutCBS), (_, Some(_)) => return Err(ParseError::MultipleBlockUnblock), }, - ConvFlag::Unblock => match (cbs, block) { - (Some(cbs), None) => unblock = Some(cbs), + ConvFlag::Unblock => match (cbs, iconvflags.block) { + (Some(cbs), None) => iconvflags.unblock = Some(cbs), (None, _) => return Err(ParseError::BlockUnblockWithoutCBS), (_, Some(_)) => return Err(ParseError::MultipleBlockUnblock), }, - ConvFlag::Swab => swab = true, - ConvFlag::Sync => sync = true, - ConvFlag::NoError => noerror = true, + ConvFlag::Swab => iconvflags.swab = true, + ConvFlag::Sync => is_sync = true, + ConvFlag::NoError => iconvflags.noerror = true, _ => {} } } + // The final conversion table depends on both + // fmt (eg. ASCII -> EBCDIC) + // case (eg. UCASE -> LCASE) + // So the final value can't be set until all flags are parsed. let ctable = parse_ctable(fmt, case); - let sync = if sync && (block.is_some() || unblock.is_some()) { + + // The final value of sync depends on block/unblock + // 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()) { Some(b' ') - } else if sync { + } else if is_sync { Some(0u8) } else { None @@ -497,27 +503,17 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result Result { - let flags = parse_flag_list(options::CONV, matches)?; + let mut oconvflags = OConvFlags::default(); - let mut oconvflags = OConvFlags { - sparse: false, - excl: false, - nocreat: false, - notrunc: false, - fdatasync: false, - fsync: false, - }; + let flags = parse_flag_list(options::CONV, matches)?; for flag in flags { match flag { @@ -548,24 +544,7 @@ pub fn parse_conv_flag_output(matches: &Matches) -> Result Result { - let mut 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, - }; + let mut iflags = IFlags::default(); let flags = parse_flag_list(options::IFLAG, matches)?; @@ -596,23 +575,7 @@ pub fn parse_iflags(matches: &Matches) -> Result { /// Parse OFlags struct from CL-input pub fn parse_oflags(matches: &Matches) -> Result { - let mut 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, - }; + let mut oflags = OFlags::default(); let flags = parse_flag_list(options::OFLAG, matches)?; diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 60b027bb6..765eb6f46 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -4,7 +4,7 @@ use crate::StatusLevel; #[cfg(not(target_os = "linux"))] #[test] -fn unimplemented_flags_should_error_non_unix() { +fn unimplemented_flags_should_error_non_linux() { let mut succeeded = Vec::new(); // The following flags are only implemented in linux @@ -518,29 +518,35 @@ test_byte_parser!(test_bytes_GB, "3GB", 3 * 1000 * 1000 * 1000); test_byte_parser!(test_bytes_G, "3G", 3 * 1024 * 1024 * 1024); test_byte_parser!(test_bytes_Gi, "3GiB", 3 * 1024 * 1024 * 1024); -test_byte_parser!(test_bytes_TB, "4TB", 4 * 1000 * 1000 * 1000 * 1000); -test_byte_parser!(test_bytes_T, "4T", 4 * 1024 * 1024 * 1024 * 1024); -test_byte_parser!(test_bytes_Ti, "4TiB", 4 * 1024 * 1024 * 1024 * 1024); +#[cfg(target_pointer_width = "64")] +#[cfg(test)] +mod test_64bit_arch { + use super::*; -test_byte_parser!(test_bytes_PB, "5PB", 5 * 1000 * 1000 * 1000 * 1000 * 1000); -test_byte_parser!(test_bytes_P, "5P", 5 * 1024 * 1024 * 1024 * 1024 * 1024); -test_byte_parser!(test_bytes_Pi, "5PiB", 5 * 1024 * 1024 * 1024 * 1024 * 1024); + test_byte_parser!(test_bytes_TB, "4TB", 4 * 1000 * 1000 * 1000 * 1000); + test_byte_parser!(test_bytes_T, "4T", 4 * 1024 * 1024 * 1024 * 1024); + test_byte_parser!(test_bytes_Ti, "4TiB", 4 * 1024 * 1024 * 1024 * 1024); -test_byte_parser!( - test_bytes_EB, - "6EB", - 6 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 -); -test_byte_parser!( - test_bytes_E, - "6E", - 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 -); -test_byte_parser!( - test_bytes_Ei, - "6EiB", - 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 -); + test_byte_parser!(test_bytes_PB, "5PB", 5 * 1000 * 1000 * 1000 * 1000 * 1000); + test_byte_parser!(test_bytes_P, "5P", 5 * 1024 * 1024 * 1024 * 1024 * 1024); + test_byte_parser!(test_bytes_Pi, "5PiB", 5 * 1024 * 1024 * 1024 * 1024 * 1024); + + test_byte_parser!( + test_bytes_EB, + "6EB", + 6 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 + ); + test_byte_parser!( + test_bytes_E, + "6E", + 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 + ); + test_byte_parser!( + test_bytes_Ei, + "6EiB", + 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 + ); +} #[test] #[should_panic] diff --git a/src/uu/dd/test-resources/dd-block-cbs16-win.test b/src/uu/dd/test-resources/dd-block-cbs16-win.test new file mode 100644 index 000000000..9b51cb8f3 --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-cbs16-win.test @@ -0,0 +1,16 @@ +0 +01 +012 +0123 +01234 +012345 +0123456 +01234567 +012345678 +0123456789 +0123456789a +0123456789ab +0123456789abc +0123456789abcd +0123456789abcde +0123456789abcdef diff --git a/src/uu/dd/test-resources/dd-block-consecutive-nl-win.test b/src/uu/dd/test-resources/dd-block-consecutive-nl-win.test new file mode 100644 index 000000000..1bf473b2b --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-consecutive-nl-win.test @@ -0,0 +1,4 @@ +pre + + +post diff --git a/src/uu/dd/test-resources/dd-unblock-cbs16-win.spec b/src/uu/dd/test-resources/dd-unblock-cbs16-win.spec new file mode 100644 index 000000000..9b51cb8f3 --- /dev/null +++ b/src/uu/dd/test-resources/dd-unblock-cbs16-win.spec @@ -0,0 +1,16 @@ +0 +01 +012 +0123 +01234 +012345 +0123456 +01234567 +012345678 +0123456789 +0123456789a +0123456789ab +0123456789abc +0123456789abcd +0123456789abcde +0123456789abcdef diff --git a/src/uu/dd/test-resources/dd-unblock-cbs8-win.spec b/src/uu/dd/test-resources/dd-unblock-cbs8-win.spec new file mode 100644 index 000000000..8031036c1 --- /dev/null +++ b/src/uu/dd/test-resources/dd-unblock-cbs8-win.spec @@ -0,0 +1,32 @@ +0 + +01 + +012 + +0123 + +01234 + +012345 + +0123456 + +01234567 + +01234567 +8 +01234567 +89 +01234567 +89a +01234567 +89ab +01234567 +89abc +01234567 +89abcd +01234567 +89abcde +01234567 +89abcdef diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index a601190d9..e038ee24b 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -213,27 +213,12 @@ fn test_excl_causes_failure_when_present() { .fails(); } -#[cfg(target_os = "linux")] -#[test] -fn test_atime_updated() { - let fname = "this-file-exists-no-noatime.txt"; - assert_fixture_exists!(&fname); - - let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&["status=none", inf!(fname)]); - - let pre_atime = fix.metadata(fname).accessed().unwrap(); - - ucmd.pipe_in("").run().no_stderr().success(); - std::thread::sleep(std::time::Duration::from_millis(10)); - - let post_atime = fix.metadata(fname).accessed().unwrap(); - assert!(pre_atime != post_atime); -} - #[cfg(target_os = "linux")] #[test] fn test_noatime_does_not_update_infile_atime() { + // NOTE: Not all environments support tracking access time. If this + // test fails on some systems and passes on others, assume the functionality + // is not working and the systems that pass it simply don't update file access time. let fname = "this-ifile-exists-noatime.txt"; assert_fixture_exists!(&fname); @@ -251,6 +236,9 @@ fn test_noatime_does_not_update_infile_atime() { #[cfg(target_os = "linux")] #[test] fn test_noatime_does_not_update_ofile_atime() { + // NOTE: Not all environments support tracking access time. If this + // test fails on some systems and passes on others, assume the functionality + // is not working and the systems that pass it simply don't update file access time. let fname = "this-ofile-exists-noatime.txt"; assert_fixture_exists!(&fname); diff --git a/tests/fixtures/dd/cspell.json b/tests/fixtures/dd/cspell.json index 19986ad83..65fa927d6 100644 --- a/tests/fixtures/dd/cspell.json +++ b/tests/fixtures/dd/cspell.json @@ -1,6 +1,29 @@ { "version": "0.1", "language": "en", + "words": [ + "fname", + "fpath", + "specfile", + "testfile", + "iflag", + "iflags", + "oflag", + "oflags", + "noxfer", + "nocreat", + "noatime", + "infile", + "outfile", + "unspec", + "fullblock", + "urand", + "tname", + "fileio", + "gibi", + "ucase", + "lcase" + ], "ignorePaths": [ "*.txt", "*.spec" From 885a875552d599107c515961ed07319596e0503a Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 22 Jul 2021 15:49:17 -0700 Subject: [PATCH 58/66] Addresses build errors - Adds words to cspell exceptions - Converts test macros to use Default trait. - Converts parser to use Default trait. - Adds Windows-friendly test files for block/unblock when nl is present in test/spec file. --- Cargo.lock | 7 -- src/uu/dd/Cargo.toml | 1 - src/uu/dd/cspell.json | 25 +++- src/uu/dd/src/dd.rs | 32 +++-- .../src/dd_unit_tests/block_unblock_tests.rs | 50 +++++--- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 10 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 16 +-- src/uu/dd/src/dd_unit_tests/mod.rs | 44 +------ src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 70 +++++------ src/uu/dd/src/parseargs.rs | 109 ++++++------------ src/uu/dd/src/parseargs/unit_tests.rs | 50 ++++---- .../dd/test-resources/dd-block-cbs16-win.test | 16 +++ .../dd-block-consecutive-nl-win.test | 4 + .../test-resources/dd-unblock-cbs16-win.spec | 16 +++ .../test-resources/dd-unblock-cbs8-win.spec | 32 +++++ tests/by-util/test_dd.rs | 24 +--- tests/fixtures/dd/cspell.json | 23 ++++ 17 files changed, 272 insertions(+), 257 deletions(-) create mode 100644 src/uu/dd/test-resources/dd-block-cbs16-win.test create mode 100644 src/uu/dd/test-resources/dd-block-consecutive-nl-win.test create mode 100644 src/uu/dd/test-resources/dd-unblock-cbs16-win.spec create mode 100644 src/uu/dd/test-resources/dd-unblock-cbs8-win.spec diff --git a/Cargo.lock b/Cargo.lock index 1aa3c3416..7a34ad49b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -543,12 +543,6 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" -[[package]] -name = "debug_print" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f215f9b7224f49fb73256115331f677d868b34d18b65dbe4db392e6021eea90" - [[package]] name = "diff" version = "0.1.12" @@ -1917,7 +1911,6 @@ version = "0.0.7" dependencies = [ "byte-unit", "clap", - "debug_print", "gcd", "libc", "signal-hook", diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index f65d0bf12..da0165245 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -17,7 +17,6 @@ path = "src/dd.rs" [dependencies] byte-unit = "4.0" clap = { version = "2.33", features = [ "wrap_help" ] } -debug_print = "1.0" gcd = "2.0" libc = "0.2" signal-hook = "0.3.9" diff --git a/src/uu/dd/cspell.json b/src/uu/dd/cspell.json index fb29630b2..4e47b090f 100644 --- a/src/uu/dd/cspell.json +++ b/src/uu/dd/cspell.json @@ -17,7 +17,9 @@ "fullblock", "noxfer", "iflag", + "iflags", "oflag", + "oflags", "infile", "outfile", "fileio", @@ -33,9 +35,30 @@ "datastructures", "creat", "mult", - "unfailed", "behaviour", "ctty", + "noatime", + "fname", + "fileout", + "ofile", + "doesnt", + "parseargs", + "rlen", + "wlen", + "rstat", + "wstat", + "rposition", + "btotal", + "sigval", + "sigusr", + "rmax", + "rsofar", + "rremain", + "bremain", + "oconvflags", + "fpath", + "testfile", + "specfile" ], "ignorePaths": [ "**/test-resources/*.test", diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 717c9d88b..d0faaacf7 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -24,6 +24,7 @@ use conversion_tables::*; use byte_unit::Byte; use clap::{self, crate_version}; use gcd::Gcd; +#[cfg(target_os = "linux")] use signal_hook::consts::signal; use std::cmp; use std::convert::TryInto; @@ -34,7 +35,9 @@ use std::io::{self, Read, Seek, Write}; #[cfg(target_os = "linux")] use std::os::unix::fs::OpenOptionsExt; use std::path::Path; -use std::sync::{atomic::AtomicUsize, atomic::Ordering, mpsc, Arc}; +#[cfg(target_os = "linux")] +use std::sync::atomic::Ordering; +use std::sync::{atomic::AtomicUsize, mpsc, Arc}; use std::thread; use std::time; @@ -168,15 +171,15 @@ impl Input { impl Read for Input { fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut base_idx = 0; - let tlen = buf.len(); + let target_len = buf.len(); loop { match self.src.read(&mut buf[base_idx..]) { Ok(0) => return Ok(base_idx), Ok(rlen) if self.iflags.fullblock => { base_idx += rlen; - if base_idx >= tlen { - return Ok(tlen); + if base_idx >= target_len { + return Ok(target_len); } } Ok(len) => return Ok(len), @@ -707,6 +710,7 @@ fn gen_prog_updater(rx: mpsc::Receiver, print_level: Option bool { env::var("POSIXLY_CORRECT").is_ok() } @@ -731,21 +735,11 @@ fn gen_prog_updater(rx: mpsc::Receiver, print_level: Option { - reprint_prog_line(&update); - - update - } - (Ok(update), _) => update, - (Err(_), _) => { - // recv only fails permanently, so we break here to - // avoid recv'ing on a broken pipe - break; - } - }; + while let Ok(update) = rx.recv() { + // (Re)print status line if progress is requested. + if Some(StatusLevel::Progress) == print_level { + reprint_prog_line(&update); + } // Handle signals #[cfg(target_os = "linux")] if let SIGUSR1_USIZE = sigval.load(Ordering::Relaxed) { diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index 1b9698638..ca633906e 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -15,19 +15,15 @@ macro_rules! make_block_test ( print_level: None, count: None, cflags: IConvFlags { - ctable: None, block: $block, - unblock: None, - swab: false, - sync: None, - noerror: false, + ..IConvFlags::default() }, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -47,19 +43,15 @@ macro_rules! make_unblock_test ( print_level: None, count: None, cflags: IConvFlags { - ctable: None, - block: None, unblock: $unblock, - swab: false, - sync: None, - noerror: false, + ..IConvFlags::default() }, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -247,7 +239,11 @@ fn block_test_double_surrounded_nl_double_trunc() { make_block_test!( block_cbs16, "block-cbs-16", - File::open("./test-resources/dd-block-cbs16.test").unwrap(), + if cfg!(unix) { + File::open("./test-resources/dd-block-cbs16.test").unwrap() + } else { + File::open("./test-resources/dd-block-cbs16-win.test").unwrap() + }, Some(16), File::open("./test-resources/dd-block-cbs16.spec").unwrap() ); @@ -255,7 +251,11 @@ make_block_test!( make_block_test!( block_cbs16_as_cbs8, "block-cbs-16-as-cbs8", - File::open("./test-resources/dd-block-cbs16.test").unwrap(), + if cfg!(unix) { + File::open("./test-resources/dd-block-cbs16.test").unwrap() + } else { + File::open("./test-resources/dd-block-cbs16-win.test").unwrap() + }, Some(8), File::open("./test-resources/dd-block-cbs8.spec").unwrap() ); @@ -263,7 +263,11 @@ make_block_test!( make_block_test!( block_consecutive_nl, "block-consecutive-nl", - File::open("./test-resources/dd-block-consecutive-nl.test").unwrap(), + if cfg!(unix) { + File::open("./test-resources/dd-block-consecutive-nl.test").unwrap() + } else { + File::open("./test-resources/dd-block-consecutive-nl-win.test").unwrap() + }, Some(16), File::open("./test-resources/dd-block-consecutive-nl-cbs16.spec").unwrap() ); @@ -335,7 +339,11 @@ make_unblock_test!( "unblock-multi-16", File::open("./test-resources/dd-unblock-cbs16.test").unwrap(), Some(16), - File::open("./test-resources/dd-unblock-cbs16.spec").unwrap() + if cfg!(unix) { + File::open("./test-resources/dd-unblock-cbs16.spec").unwrap() + } else { + File::open("./test-resources/dd-unblock-cbs16-win.spec").unwrap() + } ); make_unblock_test!( @@ -343,5 +351,9 @@ make_unblock_test!( "unblock-multi-16-as-8", File::open("./test-resources/dd-unblock-cbs16.test").unwrap(), Some(8), - File::open("./test-resources/dd-unblock-cbs8.spec").unwrap() + if cfg!(unix) { + File::open("./test-resources/dd-unblock-cbs8.spec").unwrap() + } else { + File::open("./test-resources/dd-unblock-cbs8-win.spec").unwrap() + } ); diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 8563985c6..945fbe7af 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -12,19 +12,15 @@ macro_rules! make_sync_test ( print_level: None, count: None, cflags: IConvFlags { - ctable: None, - block: None, - unblock: None, - swab: false, sync: $sync, - noerror: false, + ..IConvFlags::default() }, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: $obs, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index 5276f38b3..d3a9229b6 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -12,12 +12,12 @@ macro_rules! make_conv_test ( print_level: None, count: None, cflags: icf!($ctable), - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -37,12 +37,12 @@ macro_rules! make_icf_test ( print_level: None, count: None, cflags: $icf, - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) @@ -142,13 +142,13 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { print_level: None, count: None, cflags: icf!(Some(&ASCII_TO_EBCDIC)), - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }; let o = Output { dst: File::create(&tmp_fname_ae).unwrap(), obs: 1024, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }; dd_fileout(i, o).unwrap(); @@ -164,13 +164,13 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { print_level: None, count: None, cflags: icf!(Some(&EBCDIC_TO_ASCII)), - iflags: DEFAULT_IFLAGS, + iflags: IFlags::default(), }; let o = Output { dst: File::create(&tmp_fname_ea).unwrap(), obs: 1024, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }; dd_fileout(i, o).unwrap(); diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index fc8836e11..90ef00eaf 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -9,34 +9,6 @@ use std::fs; use std::io::prelude::*; use std::io::BufReader; -const DEFAULT_CFO: OConvFlags = OConvFlags { - sparse: false, - excl: false, - nocreat: false, - notrunc: false, - fdatasync: false, - 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, -}; - struct LazyReader { src: R, } @@ -50,19 +22,11 @@ impl Read for LazyReader { #[macro_export] macro_rules! icf ( - () => - { - icf!(None) - }; ( $ctable:expr ) => { IConvFlags { ctable: $ctable, - block: None, - unblock: None, - swab: false, - sync: None, - noerror: false, + ..IConvFlags::default() } }; ); @@ -84,13 +48,13 @@ macro_rules! make_spec_test ( ibs: 512, print_level: None, count: None, - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(), obs: 512, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, $spec, format!("./test-resources/FAILED-{}.test", $test_name) diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index fff942d26..1d0276cb3 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -52,13 +52,13 @@ make_io_test!( ibs: 521, print_level: None, count: None, - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); @@ -72,13 +72,13 @@ make_io_test!( ibs: 1031, print_level: None, count: None, - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 521, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); @@ -92,13 +92,13 @@ make_io_test!( ibs: 1024, print_level: None, count: Some(CountType::Reads(32)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1024, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); @@ -112,13 +112,13 @@ make_io_test!( ibs: 531, print_level: None, count: Some(CountType::Bytes(32 * 1024)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap() ); @@ -132,13 +132,13 @@ make_io_test!( ibs: 1024, print_level: None, count: Some(CountType::Reads(16)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-deadbeef-first-16k.spec").unwrap() ); @@ -152,13 +152,13 @@ make_io_test!( ibs: 531, print_level: None, count: Some(CountType::Bytes(12345)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-deadbeef-first-12345.spec").unwrap() ); @@ -172,13 +172,13 @@ make_io_test!( ibs: 1024, print_level: None, count: Some(CountType::Reads(32)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1024, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); @@ -192,13 +192,13 @@ make_io_test!( ibs: 521, print_level: None, count: Some(CountType::Bytes(32 * 1024)), - cflags: icf!(), - iflags: DEFAULT_IFLAGS, + cflags: IConvFlags::default(), + iflags: IFlags::default(), }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/gnudd-random-first-32k.spec").unwrap() ); @@ -215,30 +215,16 @@ make_io_test!( ibs: 521, print_level: None, count: None, - cflags: icf!(), + cflags: IConvFlags::default(), iflags: IFlags { fullblock: true, - 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, - count_bytes: false, - skip_bytes: false, + ..IFlags::default() }, }, Output { dst: DST_PLACEHOLDER, obs: 1031, - cflags: DEFAULT_CFO, + cflags: OConvFlags::default(), }, File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap() ); @@ -316,7 +302,7 @@ fn bsize_test_bs_eq() { #[test] #[should_panic] -fn test_nocreate_causes_failure_when_ofile_doesnt_exist() { +fn test_nocreat_causes_failure_when_ofile_doesnt_exist() { let args = vec![ String::from("dd"), String::from("--conv=nocreat"), diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index c0e22c520..a27ab3cb0 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -186,7 +186,7 @@ impl std::str::FromStr for Flag { "direct" => // Ok(Self::Direct), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Direct) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -195,7 +195,7 @@ impl std::str::FromStr for Flag { "directory" => // Ok(Self::Directory), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Directory) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -204,7 +204,7 @@ impl std::str::FromStr for Flag { "dsync" => // Ok(Self::Dsync), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Dsync) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -213,7 +213,7 @@ impl std::str::FromStr for Flag { "sync" => // Ok(Self::Sync), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::Sync) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -227,7 +227,7 @@ impl std::str::FromStr for Flag { "nonblock" => // Ok(Self::NonBlock), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NonBlock) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -236,7 +236,7 @@ impl std::str::FromStr for Flag { "noatime" => // Ok(Self::NoATime), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NoATime) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -245,7 +245,7 @@ impl std::str::FromStr for Flag { "noctty" => // Ok(Self::NoCtty), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NoCtty) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -254,7 +254,7 @@ impl std::str::FromStr for Flag { "nofollow" => // Ok(Self::NoFollow), { - if cfg!(unix) { + if cfg!(target_os = "linux") { Ok(Self::NoFollow) } else { Err(ParseError::Unimplemented(s.to_string())) @@ -421,16 +421,13 @@ fn parse_flag_list>( /// Parse Conversion Options (Input Variety) /// Construct and validate a IConvFlags pub fn parse_conv_flag_input(matches: &Matches) -> Result { - let flags = parse_flag_list("conv", matches)?; - let cbs = parse_cbs(matches)?; - + let mut iconvflags = IConvFlags::default(); let mut fmt = None; let mut case = None; - let mut block = None; - let mut unblock = None; - let mut swab = false; - let mut sync = false; - let mut noerror = false; + let mut is_sync = false; + + let flags = parse_flag_list(options::CONV, matches)?; + let cbs = parse_cbs(matches)?; for flag in flags { match flag { @@ -469,27 +466,36 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result match (cbs, unblock) { - (Some(cbs), None) => block = Some(cbs), + ConvFlag::Block => match (cbs, iconvflags.unblock) { + (Some(cbs), None) => iconvflags.block = Some(cbs), (None, _) => return Err(ParseError::BlockUnblockWithoutCBS), (_, Some(_)) => return Err(ParseError::MultipleBlockUnblock), }, - ConvFlag::Unblock => match (cbs, block) { - (Some(cbs), None) => unblock = Some(cbs), + ConvFlag::Unblock => match (cbs, iconvflags.block) { + (Some(cbs), None) => iconvflags.unblock = Some(cbs), (None, _) => return Err(ParseError::BlockUnblockWithoutCBS), (_, Some(_)) => return Err(ParseError::MultipleBlockUnblock), }, - ConvFlag::Swab => swab = true, - ConvFlag::Sync => sync = true, - ConvFlag::NoError => noerror = true, + ConvFlag::Swab => iconvflags.swab = true, + ConvFlag::Sync => is_sync = true, + ConvFlag::NoError => iconvflags.noerror = true, _ => {} } } + // The final conversion table depends on both + // fmt (eg. ASCII -> EBCDIC) + // case (eg. UCASE -> LCASE) + // So the final value can't be set until all flags are parsed. let ctable = parse_ctable(fmt, case); - let sync = if sync && (block.is_some() || unblock.is_some()) { + + // The final value of sync depends on block/unblock + // 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()) { Some(b' ') - } else if sync { + } else if is_sync { Some(0u8) } else { None @@ -497,27 +503,17 @@ pub fn parse_conv_flag_input(matches: &Matches) -> Result Result { - let flags = parse_flag_list(options::CONV, matches)?; + let mut oconvflags = OConvFlags::default(); - let mut oconvflags = OConvFlags { - sparse: false, - excl: false, - nocreat: false, - notrunc: false, - fdatasync: false, - fsync: false, - }; + let flags = parse_flag_list(options::CONV, matches)?; for flag in flags { match flag { @@ -548,24 +544,7 @@ pub fn parse_conv_flag_output(matches: &Matches) -> Result Result { - let mut 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, - }; + let mut iflags = IFlags::default(); let flags = parse_flag_list(options::IFLAG, matches)?; @@ -596,23 +575,7 @@ pub fn parse_iflags(matches: &Matches) -> Result { /// Parse OFlags struct from CL-input pub fn parse_oflags(matches: &Matches) -> Result { - let mut 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, - }; + let mut oflags = OFlags::default(); let flags = parse_flag_list(options::OFLAG, matches)?; diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 60b027bb6..765eb6f46 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -4,7 +4,7 @@ use crate::StatusLevel; #[cfg(not(target_os = "linux"))] #[test] -fn unimplemented_flags_should_error_non_unix() { +fn unimplemented_flags_should_error_non_linux() { let mut succeeded = Vec::new(); // The following flags are only implemented in linux @@ -518,29 +518,35 @@ test_byte_parser!(test_bytes_GB, "3GB", 3 * 1000 * 1000 * 1000); test_byte_parser!(test_bytes_G, "3G", 3 * 1024 * 1024 * 1024); test_byte_parser!(test_bytes_Gi, "3GiB", 3 * 1024 * 1024 * 1024); -test_byte_parser!(test_bytes_TB, "4TB", 4 * 1000 * 1000 * 1000 * 1000); -test_byte_parser!(test_bytes_T, "4T", 4 * 1024 * 1024 * 1024 * 1024); -test_byte_parser!(test_bytes_Ti, "4TiB", 4 * 1024 * 1024 * 1024 * 1024); +#[cfg(target_pointer_width = "64")] +#[cfg(test)] +mod test_64bit_arch { + use super::*; -test_byte_parser!(test_bytes_PB, "5PB", 5 * 1000 * 1000 * 1000 * 1000 * 1000); -test_byte_parser!(test_bytes_P, "5P", 5 * 1024 * 1024 * 1024 * 1024 * 1024); -test_byte_parser!(test_bytes_Pi, "5PiB", 5 * 1024 * 1024 * 1024 * 1024 * 1024); + test_byte_parser!(test_bytes_TB, "4TB", 4 * 1000 * 1000 * 1000 * 1000); + test_byte_parser!(test_bytes_T, "4T", 4 * 1024 * 1024 * 1024 * 1024); + test_byte_parser!(test_bytes_Ti, "4TiB", 4 * 1024 * 1024 * 1024 * 1024); -test_byte_parser!( - test_bytes_EB, - "6EB", - 6 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 -); -test_byte_parser!( - test_bytes_E, - "6E", - 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 -); -test_byte_parser!( - test_bytes_Ei, - "6EiB", - 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 -); + test_byte_parser!(test_bytes_PB, "5PB", 5 * 1000 * 1000 * 1000 * 1000 * 1000); + test_byte_parser!(test_bytes_P, "5P", 5 * 1024 * 1024 * 1024 * 1024 * 1024); + test_byte_parser!(test_bytes_Pi, "5PiB", 5 * 1024 * 1024 * 1024 * 1024 * 1024); + + test_byte_parser!( + test_bytes_EB, + "6EB", + 6 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 + ); + test_byte_parser!( + test_bytes_E, + "6E", + 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 + ); + test_byte_parser!( + test_bytes_Ei, + "6EiB", + 6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 + ); +} #[test] #[should_panic] diff --git a/src/uu/dd/test-resources/dd-block-cbs16-win.test b/src/uu/dd/test-resources/dd-block-cbs16-win.test new file mode 100644 index 000000000..9b51cb8f3 --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-cbs16-win.test @@ -0,0 +1,16 @@ +0 +01 +012 +0123 +01234 +012345 +0123456 +01234567 +012345678 +0123456789 +0123456789a +0123456789ab +0123456789abc +0123456789abcd +0123456789abcde +0123456789abcdef diff --git a/src/uu/dd/test-resources/dd-block-consecutive-nl-win.test b/src/uu/dd/test-resources/dd-block-consecutive-nl-win.test new file mode 100644 index 000000000..1bf473b2b --- /dev/null +++ b/src/uu/dd/test-resources/dd-block-consecutive-nl-win.test @@ -0,0 +1,4 @@ +pre + + +post diff --git a/src/uu/dd/test-resources/dd-unblock-cbs16-win.spec b/src/uu/dd/test-resources/dd-unblock-cbs16-win.spec new file mode 100644 index 000000000..9b51cb8f3 --- /dev/null +++ b/src/uu/dd/test-resources/dd-unblock-cbs16-win.spec @@ -0,0 +1,16 @@ +0 +01 +012 +0123 +01234 +012345 +0123456 +01234567 +012345678 +0123456789 +0123456789a +0123456789ab +0123456789abc +0123456789abcd +0123456789abcde +0123456789abcdef diff --git a/src/uu/dd/test-resources/dd-unblock-cbs8-win.spec b/src/uu/dd/test-resources/dd-unblock-cbs8-win.spec new file mode 100644 index 000000000..8031036c1 --- /dev/null +++ b/src/uu/dd/test-resources/dd-unblock-cbs8-win.spec @@ -0,0 +1,32 @@ +0 + +01 + +012 + +0123 + +01234 + +012345 + +0123456 + +01234567 + +01234567 +8 +01234567 +89 +01234567 +89a +01234567 +89ab +01234567 +89abc +01234567 +89abcd +01234567 +89abcde +01234567 +89abcdef diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index a601190d9..e038ee24b 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -213,27 +213,12 @@ fn test_excl_causes_failure_when_present() { .fails(); } -#[cfg(target_os = "linux")] -#[test] -fn test_atime_updated() { - let fname = "this-file-exists-no-noatime.txt"; - assert_fixture_exists!(&fname); - - let (fix, mut ucmd) = at_and_ucmd!(); - ucmd.args(&["status=none", inf!(fname)]); - - let pre_atime = fix.metadata(fname).accessed().unwrap(); - - ucmd.pipe_in("").run().no_stderr().success(); - std::thread::sleep(std::time::Duration::from_millis(10)); - - let post_atime = fix.metadata(fname).accessed().unwrap(); - assert!(pre_atime != post_atime); -} - #[cfg(target_os = "linux")] #[test] fn test_noatime_does_not_update_infile_atime() { + // NOTE: Not all environments support tracking access time. If this + // test fails on some systems and passes on others, assume the functionality + // is not working and the systems that pass it simply don't update file access time. let fname = "this-ifile-exists-noatime.txt"; assert_fixture_exists!(&fname); @@ -251,6 +236,9 @@ fn test_noatime_does_not_update_infile_atime() { #[cfg(target_os = "linux")] #[test] fn test_noatime_does_not_update_ofile_atime() { + // NOTE: Not all environments support tracking access time. If this + // test fails on some systems and passes on others, assume the functionality + // is not working and the systems that pass it simply don't update file access time. let fname = "this-ofile-exists-noatime.txt"; assert_fixture_exists!(&fname); diff --git a/tests/fixtures/dd/cspell.json b/tests/fixtures/dd/cspell.json index 19986ad83..65fa927d6 100644 --- a/tests/fixtures/dd/cspell.json +++ b/tests/fixtures/dd/cspell.json @@ -1,6 +1,29 @@ { "version": "0.1", "language": "en", + "words": [ + "fname", + "fpath", + "specfile", + "testfile", + "iflag", + "iflags", + "oflag", + "oflags", + "noxfer", + "nocreat", + "noatime", + "infile", + "outfile", + "unspec", + "fullblock", + "urand", + "tname", + "fileio", + "gibi", + "ucase", + "lcase" + ], "ignorePaths": [ "*.txt", "*.spec" From 076ff32e85740e42634c66783d77a8699c1a5ee5 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 23 Jul 2021 14:53:24 -0700 Subject: [PATCH 59/66] Removes project-specific cspell files. --- .vscode/cSpell.json | 2 +- .../cspell.dictionaries/jargon.wordlist.txt | 19 +++ src/uu/dd/cspell.json | 67 ---------- src/uu/dd/src/dd.rs | 56 ++++++-- src/uu/dd/src/parseargs/unit_tests.rs | 120 +++++++++++------- tests/by-util/test_dd.rs | 2 + tests/fixtures/dd/cspell.json | 31 ----- 7 files changed, 138 insertions(+), 159 deletions(-) delete mode 100644 src/uu/dd/cspell.json delete mode 100644 tests/fixtures/dd/cspell.json diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json index 9869923a4..498360139 100644 --- a/.vscode/cSpell.json +++ b/.vscode/cSpell.json @@ -11,7 +11,7 @@ { "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" } ], // ignorePaths - a list of globs to specify which files are to be ignored - "ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**"], + "ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**", "src/uu/dd/test-resources/**"], // ignoreWords - a list of words to be ignored (even if they are in the flagWords) "ignoreWords": [], // words - list of words to be always considered correct diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index c2e2c29f3..38abc75b1 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -12,6 +12,7 @@ colorizable colorize coprime consts +conv cyclomatic dedup deduplication @@ -23,6 +24,7 @@ dev devs discoverability duplicative +dsync enqueue errored executable @@ -30,7 +32,10 @@ executables exponentiate eval falsey +fileio flamegraph +fullblock +gibi gibibytes glob globbing @@ -39,8 +44,12 @@ hardlink hardlinks hasher hashsums +infile +iflag +iflags kibi kibibytes +lcase mebi mebibytes mergeable @@ -49,8 +58,15 @@ microbenchmarks microbenchmarking multibyte multicall +noatime +nocreat nonportable nonprinting +notrunc +noxfer +ofile +oflag +oflags peekable performant precompiled @@ -73,6 +89,7 @@ shortcodes subcommand subexpression submodule +sync symlink symlinks syscall @@ -80,12 +97,14 @@ syscalls tokenize toolchain truthy +ucase unbuffered unescape unintuitive unprefixed unportable unsync +urand whitespace wordlist wordlists diff --git a/src/uu/dd/cspell.json b/src/uu/dd/cspell.json deleted file mode 100644 index 4e47b090f..000000000 --- a/src/uu/dd/cspell.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "version": "0.1", - "language": "en", - "words": [ - "ucase", - "lcase", - "nocreat", - "noerror", - "notrunc", - "nocache", - "dsync", - "sync", - "nonblock", - "noctty", - "nofollow", - "nolinks", - "fullblock", - "noxfer", - "iflag", - "iflags", - "oflag", - "oflags", - "infile", - "outfile", - "fileio", - "urand", - "xfer", - "cflags", - "ctable", - "gnudd", - "atoe", - "etoa", - "atoibm", - "bmax", - "datastructures", - "creat", - "mult", - "behaviour", - "ctty", - "noatime", - "fname", - "fileout", - "ofile", - "doesnt", - "parseargs", - "rlen", - "wlen", - "rstat", - "wstat", - "rposition", - "btotal", - "sigval", - "sigusr", - "rmax", - "rsofar", - "rremain", - "bremain", - "oconvflags", - "fpath", - "testfile", - "specfile" - ], - "ignorePaths": [ - "**/test-resources/*.test", - "**/test-resources/*.spec" - ] -} diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index d0faaacf7..6eef1350e 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -714,6 +714,10 @@ fn gen_prog_updater(rx: mpsc::Receiver, print_level: Option bool { env::var("POSIXLY_CORRECT").is_ok() } + // Since signal-prompted progress printing is only availible on Linux so far, we allow unused + // variables to remove build warnings on Windows and other platforms. + // Remove when possible. + #[allow(unused_variables)] fn register_signal_handlers(sigval: Arc) -> Result<(), Box> { #[cfg(target_os = "linux")] if !posixly_correct() { @@ -1028,61 +1032,81 @@ pub fn uu_app() -> clap::App<'static, 'static> { clap::Arg::with_name(options::INFILE) .long(options::INFILE) .takes_value(true) - .help("if=FILE (alternatively --if FILE) specifies the file used for input. When not specified, stdin is used instead") + .require_equals(true) + .value_name("FILE") + .help("(alternatively if=FILE) specifies the file used for input. When not specified, stdin is used instead") ) .arg( clap::Arg::with_name(options::OUTFILE) .long(options::OUTFILE) .takes_value(true) - .help("of=FILE (alternatively --of FILE) specifies the file used for output. When not specified, stdout is used instead") + .require_equals(true) + .value_name("FILE") + .help("(alternatively of=FILE) specifies the file used for output. When not specified, stdout is used instead") ) .arg( clap::Arg::with_name(options::IBS) .long(options::IBS) .takes_value(true) - .help("ibs=N (alternatively --ibs N) specifies the size of buffer used for reads (default: 512). Multiplier strings permitted.") + .require_equals(true) + .value_name("N") + .help("(alternatively ibs=N) specifies the size of buffer used for reads (default: 512). Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::OBS) .long(options::OBS) .takes_value(true) - .help("obs=N (alternatively --obs N) specifies the size of buffer used for writes (default: 512). Multiplier strings permitted.") + .require_equals(true) + .value_name("N") + .help("(alternatively obs=N) specifies the size of buffer used for writes (default: 512). Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::BS) .long(options::BS) .takes_value(true) - .help("bs=N (alternatively --bs N) specifies ibs=N and obs=N (default: 512). If ibs or obs are also specified, bs=N takes precedence. Multiplier strings permitted.") + .require_equals(true) + .value_name("N") + .help("(alternatively bs=N) specifies ibs=N and obs=N (default: 512). If ibs or obs are also specified, bs=N takes precedence. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::CBS) .long(options::CBS) .takes_value(true) - .help("cbs=BYTES (alternatively --cbs BYTES) specifies the 'conversion block size' in bytes. Applies to the conv=block, and conv=unblock operations. Multiplier strings permitted.") + .require_equals(true) + .value_name("N") + .help("(alternatively cbs=BYTES) specifies the 'conversion block size' in bytes. Applies to the conv=block, and conv=unblock operations. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::SKIP) .long(options::SKIP) .takes_value(true) - .help("skip=N (alternatively --skip N) causes N ibs-sized records of input to be skipped before beginning copy/convert operations. See iflag=count_bytes if skipping N bytes is preferred. Multiplier strings permitted.") + .require_equals(true) + .value_name("N") + .help("(alternatively skip=N) causes N ibs-sized records of input to be skipped before beginning copy/convert operations. See iflag=count_bytes if skipping N bytes is preferred. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::SEEK) .long(options::SEEK) .takes_value(true) - .help("seek=N (alternatively --seek N) seeks N obs-sized records into output before beginning copy/convert operations. See oflag=seek_bytes if seeking N bytes is preferred. Multiplier strings permitted.") + .require_equals(true) + .value_name("N") + .help("(alternatively seek=N) seeks N obs-sized records into output before beginning copy/convert operations. See oflag=seek_bytes if seeking N bytes is preferred. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::COUNT) .long(options::COUNT) .takes_value(true) - .help("count=N (alternatively --count N) stop reading input after N ibs-sized read operations rather than proceeding until EOF. See iflag=count_bytes if stopping after N bytes is preferred. Multiplier strings permitted.") + .require_equals(true) + .value_name("N") + .help("(alternatively count=N) stop reading input after N ibs-sized read operations rather than proceeding until EOF. See iflag=count_bytes if stopping after N bytes is preferred. Multiplier strings permitted.") ) .arg( clap::Arg::with_name(options::STATUS) .long(options::STATUS) .takes_value(true) - .help("status=LEVEL (alternatively --status LEVEL) controls whether volume and performance stats are written to stderr. + .require_equals(true) + .value_name("LEVEL") + .help("(alternatively status=LEVEL) controls whether volume and performance stats are written to stderr. When unspecified, dd will print stats upon completion. An example is below. \t6+0 records in @@ -1104,7 +1128,9 @@ Printing performance stats is also triggered by the INFO signal (where supported clap::Arg::with_name(options::CONV) .long(options::CONV) .takes_value(true) - .help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file flags. Conversion options and file flags may be intermixed. + .require_equals(true) + .value_name("CONV") + .help("(alternatively conv=CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file flags. Conversion options and file flags may be intermixed. Conversion options: \t One of {ascii, ebcdic, ibm} will perform an encoding conversion. @@ -1139,7 +1165,9 @@ Conversion Flags: clap::Arg::with_name(options::IFLAG) .long(options::IFLAG) .takes_value(true) - .help("iflag=FLAG[,FLAG] (alternatively --iflag FLAG[,FLAG]) a comma separated list of input flags which specify how the input source is treated. FLAG may be any of the input-flags or general-flags specified below. + .require_equals(true) + .value_name("FLAG") + .help("(alternatively iflag=FLAG[,FLAG]) a comma separated list of input flags which specify how the input source is treated. FLAG may be any of the input-flags or general-flags specified below. Input-Flags \t 'count_bytes' a value to count=N will be interpreted as bytes. @@ -1163,7 +1191,9 @@ General-Flags clap::Arg::with_name(options::OFLAG) .long(options::OFLAG) .takes_value(true) - .help("oflag=FLAG[,FLAG] (alternatively --oflag FLAG[,FLAG]) a comma separated list of output flags which specify how the output source is treated. FLAG may be any of the output-flags or general-flags specified below. + .require_equals(true) + .value_name("FLAG") + .help("(alternatively oflag=FLAG[,FLAG]) a comma separated list of output flags which specify how the output source is treated. FLAG may be any of the output-flags or general-flags specified below. Output-Flags \t 'append' open file in append mode. Consider setting conv=notrunc as well. diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 765eb6f46..7a6fefb5a 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -104,7 +104,7 @@ fn test_status_level_none() { } #[test] -fn test_all_top_level_args_no_leading_dashes_sep_by_equals() { +fn test_all_top_level_args_no_leading_dashes() { let args = vec![ String::from("dd"), String::from("if=foo.file"), @@ -187,25 +187,23 @@ fn test_all_top_level_args_no_leading_dashes_sep_by_equals() { ); } -#[ignore] #[test] -// TODO: This should work, but Clap doesn't seem to understand it. Leaving it for now since the traditional dd if=foo.file works just fine. -fn test_all_top_level_args_leading_dashes_sep_by_spaces() { +fn test_all_top_level_args_with_leading_dashes() { let args = vec![ String::from("dd"), - String::from("--if foo.file"), - String::from("--of bar.file"), - String::from("--ibs 10"), - String::from("--obs 10"), - String::from("--cbs 1"), - String::from("--bs 100"), - String::from("--count 2"), - String::from("--skip 2"), - String::from("--seek 2"), - String::from("--status progress"), - String::from("--conv ascii,ucase"), - String::from("--iflag count_bytes,skip_bytes"), - String::from("--oflag append,seek_bytes"), + String::from("--if=foo.file"), + String::from("--of=bar.file"), + String::from("--ibs=10"), + String::from("--obs=10"), + String::from("--cbs=1"), + String::from("--bs=100"), + String::from("--count=2"), + String::from("--skip=2"), + String::from("--seek=2"), + String::from("--status=progress"), + String::from("--conv=ascii,ucase"), + String::from("--iflag=count_bytes,skip_bytes"), + String::from("--oflag=append,seek_bytes"), ]; let args = args .into_iter() @@ -415,27 +413,13 @@ fn parse_iflag_tokens() { Flag::FullBlock, Flag::CountBytes, Flag::SkipBytes, - // Flag::Cio, - Flag::Direct, - Flag::Directory, - Flag::Dsync, - Flag::Sync, - // Flag::NoCache, - Flag::NonBlock, - Flag::NoATime, - Flag::NoCtty, - Flag::NoFollow, - // Flag::NoLinks, - // Flag::Binary, - // Flag::Text, Flag::Append, Flag::SeekBytes, ]; let args = vec![ String::from("dd"), - String::from("--iflag=fullblock,count_bytes,skip_bytes,direct,directory,dsync,sync,nonblock,noatime,noctty,nofollow,append,seek_bytes"), - // String::from("--iflag=fullblock,count_bytes,skip_bytes,cio,direct,directory,dsync,sync,nocache,nonblock,noatime,noctty,nofollow,nolinks,binary,text,append,seek_bytes"), + String::from("--iflag=fullblock,count_bytes,skip_bytes,append,seek_bytes"), ]; let matches = uu_app().get_matches_from_safe(args).unwrap(); @@ -453,27 +437,69 @@ fn parse_oflag_tokens() { Flag::FullBlock, Flag::CountBytes, Flag::SkipBytes, - // Flag::Cio, - Flag::Direct, - Flag::Directory, - Flag::Dsync, - Flag::Sync, - // Flag::NoCache, - Flag::NonBlock, - Flag::NoATime, - Flag::NoCtty, - Flag::NoFollow, - // Flag::NoLinks, - // Flag::Binary, - // Flag::Text, Flag::Append, Flag::SeekBytes, ]; let args = vec![ String::from("dd"), - String::from("--oflag=fullblock,count_bytes,skip_bytes,direct,directory,dsync,sync,nonblock,noatime,noctty,nofollow,append,seek_bytes"), - // String::from("--oflag=fullblock,count_bytes,skip_bytes,cio,direct,directory,dsync,sync,nocache,nonblock,noatime,noctty,nofollow,nolinks,binary,text,append,seek_bytes"), + String::from("--oflag=fullblock,count_bytes,skip_bytes,append,seek_bytes"), + ]; + let matches = uu_app().get_matches_from_safe(args).unwrap(); + + let act = parse_flag_list::("oflag", &matches).unwrap(); + + assert_eq!(exp.len(), act.len()); + for cf in &exp { + assert!(exp.contains(&cf)); + } +} + +#[cfg(target_os = "linux")] +#[test] +fn parse_iflag_tokens_linux() { + let exp = vec![ + Flag::Direct, + Flag::Directory, + Flag::Dsync, + Flag::Sync, + Flag::NonBlock, + Flag::NoATime, + Flag::NoCtty, + Flag::NoFollow, + ]; + + let args = vec![ + String::from("dd"), + String::from("--iflag=direct,directory,dsync,sync,nonblock,noatime,noctty,nofollow"), + ]; + let matches = uu_app().get_matches_from_safe(args).unwrap(); + + let act = parse_flag_list::("iflag", &matches).unwrap(); + + assert_eq!(exp.len(), act.len()); + for cf in &exp { + assert!(exp.contains(&cf)); + } +} + +#[cfg(target_os = "linux")] +#[test] +fn parse_oflag_tokens_linux() { + let exp = vec![ + Flag::Direct, + Flag::Directory, + Flag::Dsync, + Flag::Sync, + Flag::NonBlock, + Flag::NoATime, + Flag::NoCtty, + Flag::NoFollow, + ]; + + let args = vec![ + String::from("dd"), + String::from("--oflag=direct,directory,dsync,sync,nonblock,noatime,noctty,nofollow"), ]; let matches = uu_app().get_matches_from_safe(args).unwrap(); diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index e038ee24b..821e9f0c3 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio + use crate::common::util::*; use std::fs::{File, OpenOptions}; diff --git a/tests/fixtures/dd/cspell.json b/tests/fixtures/dd/cspell.json deleted file mode 100644 index 65fa927d6..000000000 --- a/tests/fixtures/dd/cspell.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "version": "0.1", - "language": "en", - "words": [ - "fname", - "fpath", - "specfile", - "testfile", - "iflag", - "iflags", - "oflag", - "oflags", - "noxfer", - "nocreat", - "noatime", - "infile", - "outfile", - "unspec", - "fullblock", - "urand", - "tname", - "fileio", - "gibi", - "ucase", - "lcase" - ], - "ignorePaths": [ - "*.txt", - "*.spec" - ] -} From 86245ec7542b63407b699cbc60281b4b8f18ed80 Mon Sep 17 00:00:00 2001 From: Tyler Date: Sat, 24 Jul 2021 15:03:17 -0700 Subject: [PATCH 60/66] Adds spell checker ignores to remaining project files. --- src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs | 8 +++++--- src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs | 2 ++ src/uu/dd/src/dd_unit_tests/conversion_tests.rs | 2 ++ src/uu/dd/src/dd_unit_tests/mod.rs | 2 ++ src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 2 ++ src/uu/dd/src/parseargs/unit_tests.rs | 2 ++ 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs index ca633906e..35dda92eb 100644 --- a/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/block_unblock_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio + use super::*; const NL: u8 = b'\n'; @@ -86,7 +88,7 @@ fn block_test_no_nl_trunc() { let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8]; let res = block(buf, 4, &mut rs); - // Commented section should be truncated and appear for reference only. + // Commented section(s) should be truncated and appear for reference only. assert_eq!(res, vec![vec![0u8, 1u8, 2u8, 3u8 /*, 4u8*/],]); assert_eq!(rs.records_truncated, 1); } @@ -102,7 +104,7 @@ fn block_test_nl_gt_cbs_trunc() { assert_eq!( res, vec![ - // Commented lines should be truncated and appear for reference only. + // Commented section(s) should be truncated and appear for reference only. vec![0u8, 1u8, 2u8, 3u8], // vec![4u8, SPACE, SPACE, SPACE], vec![0u8, 1u8, 2u8, 3u8], @@ -227,7 +229,7 @@ fn block_test_double_surrounded_nl_double_trunc() { assert_eq!( res, vec![ - // Commented section should be truncated and appear for reference only. + // Commented section(s) should be truncated and appear for reference only. vec![0u8, 1u8, 2u8, 3u8], vec![SPACE, SPACE, SPACE, SPACE], vec![4u8, 5u8, 6u8, 7u8 /*, 8u8*/], diff --git a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs index 945fbe7af..7d7c7d879 100644 --- a/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conv_sync_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio + use super::*; macro_rules! make_sync_test ( diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index d3a9229b6..163d77e7e 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio + use super::*; macro_rules! make_conv_test ( diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index 90ef00eaf..f3f0858e7 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio + use super::*; mod block_unblock_tests; diff --git a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs index 1d0276cb3..9d1c126b0 100644 --- a/src/uu/dd/src/dd_unit_tests/sanity_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/sanity_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio + use super::*; const DST_PLACEHOLDER: Vec = Vec::new(); diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index 7a6fefb5a..35dcdf8e0 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -1,3 +1,5 @@ +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio + use super::*; use crate::StatusLevel; From d1907edd651439cd555bbdfab38003a4f2e479e1 Mon Sep 17 00:00:00 2001 From: Tyler Date: Sat, 24 Jul 2021 16:38:08 -0700 Subject: [PATCH 61/66] Adds additional words to exclude list. Improves documentation in dd.rs. --- src/uu/dd/src/dd.rs | 48 ++++++++++++++----- .../src/dd_unit_tests/block_unblock_tests.rs | 2 +- .../dd/src/dd_unit_tests/conv_sync_tests.rs | 2 +- .../dd/src/dd_unit_tests/conversion_tests.rs | 2 +- src/uu/dd/src/dd_unit_tests/mod.rs | 2 +- src/uu/dd/src/dd_unit_tests/sanity_tests.rs | 2 +- src/uu/dd/src/parseargs/unit_tests.rs | 2 +- tests/by-util/test_dd.rs | 2 +- 8 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 6eef1350e..ef250c67c 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -5,6 +5,8 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat + #[macro_use] extern crate uucore; use uucore::InvalidEncodingHandling; @@ -41,7 +43,6 @@ use std::sync::{atomic::AtomicUsize, mpsc, Arc}; use std::thread; use std::time; -// const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION"; const ABOUT: &str = "copy, and optionally convert, a file system resource"; const BUF_INIT_BYTE: u8 = 0xDD; const RTN_SUCCESS: i32 = 0; @@ -428,6 +429,7 @@ impl Write for Output { } impl Output { + /// Write all data in the given buffer in writes of size obs. fn write_blocks(&mut self, buf: Vec) -> io::Result { let mut writes_complete = 0; let mut writes_partial = 0; @@ -455,6 +457,7 @@ impl Output { } impl Output { + /// Write all data in the given buffer in writes of size obs. fn write_blocks(&mut self, buf: Vec) -> io::Result { let mut writes_complete = 0; let mut writes_partial = 0; @@ -483,6 +486,7 @@ impl Output { /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N +/// Expects ascii encoded data fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> { let mut blocks = buf .split(|&e| e == b'\n') @@ -508,6 +512,7 @@ fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> { /// Trims padding from each cbs-length partition of buf /// as specified by conv=unblock and cbs=N +/// Expects ascii encoded data fn unblock(buf: Vec, cbs: usize) -> Vec { buf.chunks(cbs).fold(Vec::new(), |mut acc, block| { if let Some(last_char_idx) = block.iter().rposition(|&e| e != b' ') { @@ -523,6 +528,13 @@ fn unblock(buf: Vec, cbs: usize) -> Vec { }) } +/// A helper for teasing out which options must be applied and in which order. +/// Some user options, such as the presence of conversion tables, will determine whether the input is assumed to be ascii. The parser sets the Input::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` fn conv_block_unblock_helper( mut buf: Vec, i: &mut Input, @@ -615,6 +627,7 @@ fn conv_block_unblock_helper( } } +/// Read helper performs read operations common to all dd reads, and dispatches the buffer to relevent helper functions as dictated by the operations requested by the user. fn read_helper( i: &mut Input, bsize: usize, @@ -659,6 +672,9 @@ fn read_helper( } } +// Print io lines of a status update: +// + records in +// + records out fn print_io_lines(update: &ProgUpdate) { eprintln!( "{}+{} records in", @@ -672,6 +688,8 @@ fn print_io_lines(update: &ProgUpdate) { update.write_stat.writes_complete, update.write_stat.writes_partial ); } +// Print the progress line of a status update: +// bytes (, ) copied,