mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 23:32:39 +00:00
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!
This commit is contained in:
parent
d97672dfd3
commit
d0b4fe34c1
4 changed files with 424 additions and 154 deletions
|
@ -52,9 +52,8 @@ enum SrcStat
|
|||
pub struct IConvFlags
|
||||
{
|
||||
ctable: Option<&'static ConversionTable>,
|
||||
cbs: Option<usize>,
|
||||
block: bool,
|
||||
unblock: bool,
|
||||
block: Option<usize>,
|
||||
unblock: Option<usize>,
|
||||
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<R: Read>
|
||||
{
|
||||
src: R,
|
||||
non_ascii: bool,
|
||||
ibs: usize,
|
||||
xfer_stats: StatusLevel,
|
||||
cflags: IConvFlags,
|
||||
iflags: IFlags,
|
||||
}
|
||||
|
||||
impl<R: Read> Read for Input<R>
|
||||
{
|
||||
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize>
|
||||
{
|
||||
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<io::Stdin>
|
||||
{
|
||||
fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
|
||||
{
|
||||
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<io::Stdin>
|
|||
|
||||
let mut i = Input {
|
||||
src: io::stdin(),
|
||||
non_ascii,
|
||||
ibs,
|
||||
xfer_stats,
|
||||
cflags,
|
||||
|
@ -217,6 +192,7 @@ impl Input<File>
|
|||
fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
|
||||
{
|
||||
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<File>
|
|||
|
||||
let i = Input {
|
||||
src,
|
||||
non_ascii,
|
||||
ibs,
|
||||
xfer_stats,
|
||||
cflags,
|
||||
|
@ -250,13 +227,42 @@ impl Input<File>
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Read for Input<R>
|
||||
{
|
||||
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize>
|
||||
{
|
||||
// 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<R: Read> Input<R>
|
||||
{
|
||||
fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result<SrcStat, Box<dyn Error>>
|
||||
/// 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<usize, Box<dyn Error>>
|
||||
{
|
||||
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<R: Read> Input<R>
|
|||
}
|
||||
}
|
||||
|
||||
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<dyn Error>>
|
||||
/// 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<dyn Error>>
|
||||
{
|
||||
let mut total_len = 0;
|
||||
|
||||
|
@ -283,12 +288,13 @@ impl<R: Read> Input<R>
|
|||
{
|
||||
total_len += self.read(&mut buf)?;
|
||||
|
||||
if total_len == len
|
||||
if total_len == target_len
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Output<W: Write>
|
||||
|
@ -313,6 +319,16 @@ impl Output<io::Stdout> {
|
|||
oflags,
|
||||
})
|
||||
}
|
||||
|
||||
fn fsync(&mut self) -> io::Result<()>
|
||||
{
|
||||
self.dst.flush()
|
||||
}
|
||||
|
||||
fn fdatasync(&mut self) -> io::Result<()>
|
||||
{
|
||||
self.dst.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl Output<File> {
|
||||
|
@ -346,9 +362,25 @@ impl Output<File> {
|
|||
}
|
||||
else
|
||||
{
|
||||
// The following error should only occur if someone
|
||||
// mistakenly calls Output::<File>::new() without checking
|
||||
// if 'of' has been provided. In this case,
|
||||
// Output::<io::stdout>::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<File>
|
||||
|
@ -359,11 +391,29 @@ impl Seek for Output<File>
|
|||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for Output<W>
|
||||
impl Write for Output<File>
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
|
||||
{
|
||||
#[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<W: Write> Write for Output<W>
|
|||
}
|
||||
}
|
||||
|
||||
fn is_sparse(buf: &[u8]) -> bool
|
||||
impl Write for Output<io::Stdout>
|
||||
{
|
||||
buf.iter().all(| &e | e == 0)
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
|
||||
{
|
||||
self.dst.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()>
|
||||
{
|
||||
self.dst.flush()
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
|
||||
|
@ -400,22 +458,243 @@ fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dd_read_helper<R: Read, W: Write>(mut buf: &mut [u8], i: &mut Input<R>, o: &Output<W>) -> Result<SrcStat, Box<dyn Error>>
|
||||
fn pad(buf: &mut Vec<u8>, 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<Vec<u8>>
|
||||
{
|
||||
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<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<R>, o: &Output<W>) -> Result<Vec<u8>, Box<dyn Error>>
|
||||
{
|
||||
#[inline]
|
||||
fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool
|
||||
{
|
||||
!i.non_ascii
|
||||
&& i.cflags.block.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn should_conv_then_block<R: Read>(i: &Input<R>) -> bool
|
||||
{
|
||||
i.non_ascii
|
||||
&& i.cflags.block.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn should_unblock_then_conv<R: Read>(i: &Input<R>) -> bool
|
||||
{
|
||||
!i.non_ascii
|
||||
&& i.cflags.unblock.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn should_conv_then_unblock<R: Read>(i: &Input<R>) -> bool
|
||||
{
|
||||
i.non_ascii
|
||||
&& i.cflags.unblock.is_some()
|
||||
}
|
||||
fn conv_only<R: Read>(i: &Input<R>) -> 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<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>) -> Result<(usize, Vec<u8>), Box<dyn Error>>
|
||||
{
|
||||
#[inline]
|
||||
fn is_fast_read<R: Read, W: Write>(i: &Input<R>, o: &Output<W>) -> bool
|
||||
{
|
||||
i.ibs == o.obs
|
||||
&& !is_conv(i)
|
||||
&& !is_block(i)
|
||||
&& !is_unblock(i)
|
||||
&& !i.cflags.swab
|
||||
}
|
||||
#[inline]
|
||||
fn is_conv<R: Read>(i: &Input<R>) -> bool
|
||||
{
|
||||
i.cflags.ctable.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn is_block<R: Read>(i: &Input<R>) -> bool
|
||||
{
|
||||
i.cflags.block.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn is_unblock<R: Read>(i: &Input<R>) -> 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<R: Read, W: Write>(mut buf: &mut [u8], i: &mut Input<R>, o: &O
|
|||
// and should be fixed in the future.
|
||||
fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(usize, usize), Box<dyn Error>>
|
||||
{
|
||||
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<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> 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<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(usi
|
|||
// and should be fixed in the future.
|
||||
fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(usize, usize), Box<dyn Error>>
|
||||
{
|
||||
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<R: Read>(mut i: Input<R>, mut o: Output<File>) -> 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<R: Read>(mut i: Input<R>, mut o: Output<File>) -> 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))
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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<Option<usize>, ParseError>
|
|||
|
||||
pub fn parse_status_level(matches: &getopts::Matches) -> Result<StatusLevel, ParseError>
|
||||
{
|
||||
// TODO: Impl
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -322,7 +322,7 @@ fn parse_ctable(fmt: Option<ConvFlag>, case: Option<ConvFlag>) -> 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<ConvFlag>, case: Option<ConvFlag>) -> 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<ConvFlag>, case: Option<ConvFlag>) -> 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<IConvFlags, P
|
|||
|
||||
let mut fmt = None;
|
||||
let mut case = None;
|
||||
let mut block = false;
|
||||
let mut unblock = false;
|
||||
let mut block = None;
|
||||
let mut unblock = None;
|
||||
let mut swab = false;
|
||||
let mut sync = false;
|
||||
let mut noerror = false;
|
||||
|
@ -445,22 +445,24 @@ pub fn parse_conv_flag_input(matches: &getopts::Matches) -> Result<IConvFlags, P
|
|||
case = Some(flag)
|
||||
},
|
||||
ConvFlag::Block =>
|
||||
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<IConvFlags, P
|
|||
|
||||
Ok(IConvFlags {
|
||||
ctable,
|
||||
cbs,
|
||||
block,
|
||||
unblock,
|
||||
swab,
|
||||
|
@ -580,8 +581,6 @@ pub fn parse_iflags(matches: &getopts::Matches) -> Result<IFlags, ParseError>
|
|||
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<OFlags, ParseError>
|
|||
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<bool, ParseError>
|
||||
{
|
||||
if let Some(conv_opts) = matches.opt_str("conv")
|
||||
{
|
||||
Ok(conv_opts.contains("ascii"))
|
||||
}
|
||||
else
|
||||
{
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue