mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 12:37:49 +00:00
Merge pull request #2573 from backwaterred/master
dd: Apply OutputTrait to reduce code duplication #2572
This commit is contained in:
commit
b58f4189fb
3 changed files with 109 additions and 199 deletions
|
@ -275,13 +275,19 @@ impl<R: Read> Input<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait OutputTrait: Sized + Write {
|
||||||
|
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>>;
|
||||||
|
fn fsync(&mut self) -> io::Result<()>;
|
||||||
|
fn fdatasync(&mut self) -> io::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
struct Output<W: Write> {
|
struct Output<W: Write> {
|
||||||
dst: W,
|
dst: W,
|
||||||
obs: usize,
|
obs: usize,
|
||||||
cflags: OConvFlags,
|
cflags: OConvFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output<io::Stdout> {
|
impl OutputTrait for Output<io::Stdout> {
|
||||||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||||
let obs = parseargs::parse_obs(matches)?;
|
let obs = parseargs::parse_obs(matches)?;
|
||||||
let cflags = parseargs::parse_conv_flag_output(matches)?;
|
let cflags = parseargs::parse_conv_flag_output(matches)?;
|
||||||
|
@ -300,6 +306,100 @@ impl Output<io::Stdout> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<W: Write> Output<W>
|
||||||
|
where
|
||||||
|
Self: OutputTrait,
|
||||||
|
{
|
||||||
|
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
|
||||||
|
let mut writes_complete = 0;
|
||||||
|
let mut writes_partial = 0;
|
||||||
|
let mut bytes_total = 0;
|
||||||
|
|
||||||
|
for chunk in buf.chunks(self.obs) {
|
||||||
|
match self.write(chunk)? {
|
||||||
|
wlen if wlen < chunk.len() => {
|
||||||
|
writes_partial += 1;
|
||||||
|
bytes_total += wlen;
|
||||||
|
}
|
||||||
|
wlen => {
|
||||||
|
writes_complete += 1;
|
||||||
|
bytes_total += wlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(WriteStat {
|
||||||
|
writes_complete,
|
||||||
|
writes_partial,
|
||||||
|
bytes_total: bytes_total.try_into().unwrap_or(0u128),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dd_out<R: Read>(mut self, mut i: Input<R>) -> Result<(), Box<dyn Error>> {
|
||||||
|
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, self.obs);
|
||||||
|
|
||||||
|
let prog_tx = {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
thread::spawn(gen_prog_updater(rx, i.print_level));
|
||||||
|
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 = self.write_blocks(buf)?;
|
||||||
|
|
||||||
|
rstat += rstat_update;
|
||||||
|
wstat += wstat_update;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Update Prog
|
||||||
|
prog_tx.send(ProgUpdate {
|
||||||
|
read_stat: rstat,
|
||||||
|
write_stat: wstat,
|
||||||
|
duration: start.elapsed(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.cflags.fsync {
|
||||||
|
self.fsync()?;
|
||||||
|
} else if self.cflags.fdatasync {
|
||||||
|
self.fdatasync()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match i.print_level {
|
||||||
|
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
||||||
|
_ => print_transfer_stats(&ProgUpdate {
|
||||||
|
read_stat: rstat,
|
||||||
|
write_stat: wstat,
|
||||||
|
duration: start.elapsed(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
|
fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
|
||||||
let mut flag = 0;
|
let mut flag = 0;
|
||||||
|
@ -340,7 +440,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output<File> {
|
impl OutputTrait for Output<File> {
|
||||||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||||
fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
|
fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
|
||||||
let mut opts = OpenOptions::new();
|
let mut opts = OpenOptions::new();
|
||||||
|
@ -430,62 +530,6 @@ impl Write for Output<io::Stdout> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output<io::Stdout> {
|
|
||||||
/// Write all data in the given buffer in writes of size obs.
|
|
||||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
|
|
||||||
let mut writes_complete = 0;
|
|
||||||
let mut writes_partial = 0;
|
|
||||||
let mut bytes_total = 0;
|
|
||||||
|
|
||||||
for chunk in buf.chunks(self.obs) {
|
|
||||||
match self.write(chunk)? {
|
|
||||||
wlen if wlen < chunk.len() => {
|
|
||||||
writes_partial += 1;
|
|
||||||
bytes_total += wlen;
|
|
||||||
}
|
|
||||||
wlen => {
|
|
||||||
writes_complete += 1;
|
|
||||||
bytes_total += wlen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(WriteStat {
|
|
||||||
writes_complete,
|
|
||||||
writes_partial,
|
|
||||||
bytes_total: bytes_total.try_into().unwrap_or(0u128),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Output<File> {
|
|
||||||
/// Write all data in the given buffer in writes of size obs.
|
|
||||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
|
|
||||||
let mut writes_complete = 0;
|
|
||||||
let mut writes_partial = 0;
|
|
||||||
let mut bytes_total = 0;
|
|
||||||
|
|
||||||
for chunk in buf.chunks(self.obs) {
|
|
||||||
match self.write(chunk)? {
|
|
||||||
wlen if wlen < chunk.len() => {
|
|
||||||
writes_partial += 1;
|
|
||||||
bytes_total += wlen;
|
|
||||||
}
|
|
||||||
wlen => {
|
|
||||||
writes_complete += 1;
|
|
||||||
bytes_total += wlen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(WriteStat {
|
|
||||||
writes_complete,
|
|
||||||
writes_partial,
|
|
||||||
bytes_total: bytes_total.try_into().unwrap_or(0u128),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Splits the content of buf into cbs-length blocks
|
/// Splits the content of buf into cbs-length blocks
|
||||||
/// Appends padding as specified by conv=block and cbs=N
|
/// Appends padding as specified by conv=block and cbs=N
|
||||||
/// Expects ascii encoded data
|
/// Expects ascii encoded data
|
||||||
|
@ -827,140 +871,6 @@ fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform the copy/convert operations. Stdout version
|
|
||||||
/// 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<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(), Box<dyn Error>> {
|
|
||||||
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.print_level));
|
|
||||||
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 {
|
|
||||||
read_stat: rstat,
|
|
||||||
write_stat: wstat,
|
|
||||||
duration: start.elapsed(),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.cflags.fsync {
|
|
||||||
o.fsync()?;
|
|
||||||
} else if o.cflags.fdatasync {
|
|
||||||
o.fdatasync()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match i.print_level {
|
|
||||||
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
|
||||||
_ => print_transfer_stats(&ProgUpdate {
|
|
||||||
read_stat: rstat,
|
|
||||||
write_stat: wstat,
|
|
||||||
duration: start.elapsed(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform the copy/convert operations. File backed output version
|
|
||||||
/// 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<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<dyn Error>> {
|
|
||||||
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.print_level));
|
|
||||||
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 {
|
|
||||||
read_stat: rstat,
|
|
||||||
write_stat: wstat,
|
|
||||||
duration: start.elapsed(),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.cflags.fsync {
|
|
||||||
o.fsync()?;
|
|
||||||
} else if o.cflags.fdatasync {
|
|
||||||
o.fdatasync()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match i.print_level {
|
|
||||||
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
|
||||||
_ => print_transfer_stats(&ProgUpdate {
|
|
||||||
read_stat: rstat,
|
|
||||||
write_stat: wstat,
|
|
||||||
duration: start.elapsed(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_dashes_if_not_present(mut acc: Vec<String>, mut s: String) -> Vec<String> {
|
fn append_dashes_if_not_present(mut acc: Vec<String>, mut s: String) -> Vec<String> {
|
||||||
if !s.starts_with("--") && !s.starts_with('-') {
|
if !s.starts_with("--") && !s.starts_with('-') {
|
||||||
s.insert_str(0, "--");
|
s.insert_str(0, "--");
|
||||||
|
@ -1009,7 +919,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let (i, o) =
|
let (i, o) =
|
||||||
unpack_or_rtn!(Input::<File>::new(&matches), Output::<File>::new(&matches));
|
unpack_or_rtn!(Input::<File>::new(&matches), Output::<File>::new(&matches));
|
||||||
|
|
||||||
dd_fileout(i, o)
|
o.dd_out(i)
|
||||||
}
|
}
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
let (i, o) = unpack_or_rtn!(
|
let (i, o) = unpack_or_rtn!(
|
||||||
|
@ -1017,7 +927,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Output::<File>::new(&matches)
|
Output::<File>::new(&matches)
|
||||||
);
|
);
|
||||||
|
|
||||||
dd_fileout(i, o)
|
o.dd_out(i)
|
||||||
}
|
}
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
let (i, o) = unpack_or_rtn!(
|
let (i, o) = unpack_or_rtn!(
|
||||||
|
@ -1025,7 +935,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Output::<io::Stdout>::new(&matches)
|
Output::<io::Stdout>::new(&matches)
|
||||||
);
|
);
|
||||||
|
|
||||||
dd_stdout(i, o)
|
o.dd_out(i)
|
||||||
}
|
}
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
let (i, o) = unpack_or_rtn!(
|
let (i, o) = unpack_or_rtn!(
|
||||||
|
@ -1033,7 +943,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Output::<io::Stdout>::new(&matches)
|
Output::<io::Stdout>::new(&matches)
|
||||||
);
|
);
|
||||||
|
|
||||||
dd_stdout(i, o)
|
o.dd_out(i)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match result {
|
match result {
|
||||||
|
|
|
@ -153,7 +153,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
|
||||||
cflags: OConvFlags::default(),
|
cflags: OConvFlags::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
dd_fileout(i, o).unwrap();
|
o.dd_out(i).unwrap();
|
||||||
|
|
||||||
// EBCDIC->ASCII
|
// EBCDIC->ASCII
|
||||||
let test_name = "all-valid-ebcdic-to-ascii";
|
let test_name = "all-valid-ebcdic-to-ascii";
|
||||||
|
@ -175,7 +175,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
|
||||||
cflags: OConvFlags::default(),
|
cflags: OConvFlags::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
dd_fileout(i, o).unwrap();
|
o.dd_out(i).unwrap();
|
||||||
|
|
||||||
// Final Comparison
|
// Final Comparison
|
||||||
let res = File::open(&tmp_fname_ea).unwrap();
|
let res = File::open(&tmp_fname_ea).unwrap();
|
||||||
|
|
|
@ -67,7 +67,7 @@ macro_rules! make_spec_test (
|
||||||
#[test]
|
#[test]
|
||||||
fn $test_id()
|
fn $test_id()
|
||||||
{
|
{
|
||||||
dd_fileout($i,$o).unwrap();
|
$o.dd_out($i).unwrap();
|
||||||
|
|
||||||
let res = File::open($tmp_fname).unwrap();
|
let res = File::open($tmp_fname).unwrap();
|
||||||
// Check test file isn't empty (unless spec file is too)
|
// Check test file isn't empty (unless spec file is too)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue