mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 23:32:39 +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;
|
||||
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<File> {
|
|||
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<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>
|
||||
{
|
||||
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() -> ()
|
||||
{
|
||||
move || {
|
||||
|
@ -290,22 +331,31 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> 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<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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Vec<ConvFlag>, 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<ConvFlagOutp
|
|||
ConvFlag::Sparse =>
|
||||
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 =>
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue