mirror of
https://github.com/uutils/coreutils
synced 2025-01-18 16:14:13 +00:00
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<X,Y> for each valid pair of { File, Stdin, Stdout }.
This commit is contained in:
parent
b461f102cc
commit
5aabdf854d
3 changed files with 105 additions and 21 deletions
|
@ -18,14 +18,18 @@ mod parseargs;
|
||||||
mod conversion_tables;
|
mod conversion_tables;
|
||||||
use conversion_tables::*;
|
use conversion_tables::*;
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::File;
|
use std::fs::{
|
||||||
|
File, OpenOptions,
|
||||||
|
};
|
||||||
|
use getopts;
|
||||||
use std::io::{
|
use std::io::{
|
||||||
self, Read, Write,
|
self, Read, Write,
|
||||||
|
Seek,
|
||||||
};
|
};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use getopts;
|
|
||||||
|
|
||||||
const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION";
|
const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION";
|
||||||
const SUMMARY: &str = "convert, and optionally copy, a file";
|
const SUMMARY: &str = "convert, and optionally copy, a file";
|
||||||
|
@ -218,10 +222,16 @@ impl Output<File> {
|
||||||
let obs = parseargs::parse_obs(matches)?;
|
let obs = parseargs::parse_obs(matches)?;
|
||||||
let cf = parseargs::parse_conv_flag_output(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 {
|
Ok(Output {
|
||||||
dst: File::open(fname)?,
|
dst,
|
||||||
obs,
|
obs,
|
||||||
cf,
|
cf,
|
||||||
})
|
})
|
||||||
|
@ -233,6 +243,32 @@ impl Output<File> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<W: Write> Seek for Output<W>
|
||||||
|
{
|
||||||
|
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Seek for Output<io::Stdout>
|
||||||
|
// {
|
||||||
|
// fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
|
||||||
|
// {
|
||||||
|
// // Default method. Called when output dst not backed by a traditional file and
|
||||||
|
// // should not be seeked (eg. stdout)
|
||||||
|
// Ok(0)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl Seek for Output<File>
|
||||||
|
// {
|
||||||
|
// fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
|
||||||
|
// {
|
||||||
|
// self.dst.seek(pos)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
impl<W: Write> Write for Output<W>
|
impl<W: Write> Write for Output<W>
|
||||||
{
|
{
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
|
||||||
|
@ -246,6 +282,11 @@ impl<W: Write> Write for Output<W>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_sparse(buf: &[u8]) -> bool
|
||||||
|
{
|
||||||
|
buf.iter().all(| &e | e == 0)
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
|
fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
|
||||||
{
|
{
|
||||||
move || {
|
move || {
|
||||||
|
@ -290,8 +331,9 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
|
||||||
loop
|
loop
|
||||||
{
|
{
|
||||||
let mut buf = vec![DEFAULT_FILL_BYTE; o.obs];
|
let mut buf = vec![DEFAULT_FILL_BYTE; o.obs];
|
||||||
let r_len =
|
|
||||||
match i.fill_n(&mut buf, o.obs)? {
|
// Read
|
||||||
|
let r_len = match i.fill_n(&mut buf, o.obs)? {
|
||||||
SrcStat::Read(len) =>
|
SrcStat::Read(len) =>
|
||||||
{
|
{
|
||||||
bytes_in += len;
|
bytes_in += len;
|
||||||
|
@ -301,11 +343,19 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
|
||||||
break,
|
break,
|
||||||
};
|
};
|
||||||
|
|
||||||
let w_len = o.write(&buf[..r_len])?;
|
// Write
|
||||||
|
let w_len = if o.cf.sparse && is_sparse(&buf)
|
||||||
// TODO: Some flag (sync?) controls this behaviour
|
{
|
||||||
// o.flush()?;
|
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;
|
bytes_out += w_len;
|
||||||
|
|
||||||
if let Some(prog_tx) = &prog_tx
|
if let Some(prog_tx) = &prog_tx
|
||||||
|
@ -314,6 +364,13 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Also ensure file metadata is written when fsync option is specified. When _wouldn't_ this happen?
|
||||||
|
// See fs::File::sync_all && fs::File::sync_data methods!
|
||||||
|
if o.cf.fsync || o.cf.fdatasync
|
||||||
|
{
|
||||||
|
o.flush()?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok((bytes_in, bytes_out))
|
Ok((bytes_in, bytes_out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub enum ParseError
|
||||||
MultipleFmtTable,
|
MultipleFmtTable,
|
||||||
MultipleUCaseLCase,
|
MultipleUCaseLCase,
|
||||||
MultipleBlockUnblock,
|
MultipleBlockUnblock,
|
||||||
|
MultipleExclNoCreat,
|
||||||
ConvFlagNoMatch(String),
|
ConvFlagNoMatch(String),
|
||||||
NoMatchingMultiplier(String),
|
NoMatchingMultiplier(String),
|
||||||
MultiplierStringContainsNoValue(String),
|
MultiplierStringContainsNoValue(String),
|
||||||
|
@ -274,11 +275,9 @@ fn parse_conv_opts(matches: &getopts::Matches) -> Result<Vec<ConvFlag>, ParseErr
|
||||||
|
|
||||||
if let Some(comma_str) = matches.opt_str("conv")
|
if let Some(comma_str) = matches.opt_str("conv")
|
||||||
{
|
{
|
||||||
println!("Parsing conv: {}", comma_str);
|
|
||||||
for s in comma_str.split(",")
|
for s in comma_str.split(",")
|
||||||
{
|
{
|
||||||
let flag = s.parse()?;
|
let flag = s.parse()?;
|
||||||
println!("found flag: {:?}", &flag);
|
|
||||||
flags.push(flag);
|
flags.push(flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,9 +408,23 @@ pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result<ConvFlagOutp
|
||||||
ConvFlag::Sparse =>
|
ConvFlag::Sparse =>
|
||||||
sparse = true,
|
sparse = true,
|
||||||
ConvFlag::Excl =>
|
ConvFlag::Excl =>
|
||||||
excl = true,
|
if !nocreat
|
||||||
|
{
|
||||||
|
excl = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Err(ParseError::MultipleExclNoCreat);
|
||||||
|
},
|
||||||
ConvFlag::NoCreat =>
|
ConvFlag::NoCreat =>
|
||||||
nocreat = true,
|
if !excl
|
||||||
|
{
|
||||||
|
nocreat = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Err(ParseError::MultipleExclNoCreat);
|
||||||
|
},
|
||||||
ConvFlag::NoTrunc =>
|
ConvFlag::NoTrunc =>
|
||||||
notrunc = true,
|
notrunc = true,
|
||||||
ConvFlag::FDataSync =>
|
ConvFlag::FDataSync =>
|
||||||
|
|
|
@ -76,6 +76,20 @@ fn cfi_block_error()
|
||||||
let cfi_parsed = parse_conv_flag_input(&matches).unwrap();
|
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]
|
#[test]
|
||||||
fn parse_cfi_token_ibm()
|
fn parse_cfi_token_ibm()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue