mirror of
https://github.com/uutils/coreutils
synced 2024-07-23 10:54:14 +00:00
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)
This commit is contained in:
parent
a511db504b
commit
fc110bb656
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<R: Read>
|
|||
src: R,
|
||||
non_ascii: bool,
|
||||
ibs: usize,
|
||||
xfer_stats: StatusLevel,
|
||||
xfer_stats: Option<StatusLevel>,
|
||||
cflags: IConvFlags,
|
||||
iflags: IFlags,
|
||||
}
|
||||
|
@ -252,60 +282,96 @@ impl<R: Read> Input<R>
|
|||
/// 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<u8>) -> Result<usize, Box<dyn Error>>
|
||||
fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> Result<ReadStat, Box<dyn Error>>
|
||||
{
|
||||
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<u8>, obs: usize, pad: u8) -> Result<usize, Box<dyn Error>>
|
||||
fn fill_blocks(&mut self, buf: &mut Vec<u8>, obs: usize, pad: u8) -> Result<ReadStat, Box<dyn Error>>
|
||||
{
|
||||
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<io::Stdout>
|
|||
|
||||
impl Output<io::Stdout>
|
||||
{
|
||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<usize>
|
||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat>
|
||||
{
|
||||
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<File>
|
||||
{
|
||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<usize>
|
||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat>
|
||||
{
|
||||
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<u8>, cbs: usize) -> Vec<Vec<u8>>
|
||||
fn block(buf: Vec<u8>, cbs: usize, rstats: &mut ReadStat) -> Vec<Vec<u8>>
|
||||
{
|
||||
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<u8>, cbs: usize) -> Vec<u8>
|
|||
|
||||
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<u8>, cbs: usize) -> Vec<u8>
|
|||
.collect()
|
||||
}
|
||||
|
||||
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>>
|
||||
fn conv_block_unblock_helper<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<R>, o: &Output<W>, rstats: &mut ReadStat) -> Result<Vec<u8>, Box<dyn Error>>
|
||||
{
|
||||
// Local Predicate Fns -------------------------------------------------
|
||||
#[inline]
|
||||
|
@ -633,7 +737,7 @@ fn conv_block_unblock_helper<R: Read, W: Write>(mut buf: Vec<u8>, 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<R: Read, W: Write>(mut buf: Vec<u8>, 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<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<
|
|||
}
|
||||
}
|
||||
|
||||
fn read_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>, bsize: usize) -> Result<(usize, Vec<u8>), Box<dyn Error>>
|
||||
fn read_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>, bsize: usize) -> Result<(ReadStat, Vec<u8>), Box<dyn Error>>
|
||||
{
|
||||
// Local Predicate Fns -----------------------------------------------
|
||||
#[inline]
|
||||
|
@ -756,58 +860,151 @@ fn read_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>, 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<usize>) -> impl Fn() -> ()
|
||||
fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, xfer_stats: Option<StatusLevel>) -> 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<Write> abstraction,
|
||||
// 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>>
|
||||
fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(), Box<dyn Error>>
|
||||
{
|
||||
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<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> 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<Write> abstraction,
|
||||
// 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>>
|
||||
fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<dyn Error>>
|
||||
{
|
||||
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<R: Read>(mut i: Input<R>, mut o: Output<File>) -> 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -9,8 +9,8 @@ impl<R: Read> Read for LazyReader<R>
|
|||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>
|
||||
{
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -297,7 +297,7 @@ fn parse_cbs(matches: &getopts::Matches) -> Result<Option<usize>, ParseError>
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_status_level(matches: &getopts::Matches) -> Result<StatusLevel, ParseError>
|
||||
pub fn parse_status_level(matches: &getopts::Matches) -> Result<Option<StatusLevel>, ParseError>
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue