mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 03:57:44 +00:00
Addresses code-quality issues from testsdiepraam and miDeb.
- Removes dd from feat_require_unix (keeps it in feat_common_core) - Changes name of make_linux_iflags parameter from oflags to iflags. - Removes roughed out SIGINFO impl. - Renames plen -> target_len. - Removes internal fn def for build_blocks and replaces with a call to chunks from std. - Renames apply_ct to the more descriptive apply_conversion - Replaces manual swap steps in perform_swab with call to swap from std. - Replaces manual solution with chunks where appropriate (in Read, and Write impl). - Renames xfer -> transfer (in local variable names). - Improves documentation for dd_filout/dd_stdout issue. - Removes commented debug_assert statements. - Modifies ProdUpdate to contain ReadStat and WriteStat rather than copying their fields. - Addresses verbose return in `Output<File>::new(...)` - Resoves compiler warning when built as release when signal handler fails to register. - Derives _Default_ trait on ReadStat. - Adds comments for truncated lines in block unblock tests - Removes `as u8` in block unblock tests. - Removes unecessary `#[inline]` - Delegates multiplier string parsing to uucore::parse_size. - Renames 'unfailed' -> 'succeeded' for clairity. - Removes #dead_code warnings. No clippy warnings on my local machine. - Reworks signal handler to better accomodate platform-specific signals. - Removes explicit references to "if" and "of" in dd.rs. - Removes explicit references to "bs", "ibs", "cbs" and "status" in parseargs.rs. - Removes `#[allow(deadcode)]` for OFlags, and IFlags. - Removes spellchecker ignore from all dd files. - Adds tests for 'traditional' and 'modern' CLI.
This commit is contained in:
parent
c3f9557581
commit
9d9267e08b
12 changed files with 377 additions and 418 deletions
|
@ -153,7 +153,6 @@ feat_require_unix = [
|
||||||
"chmod",
|
"chmod",
|
||||||
"chown",
|
"chown",
|
||||||
"chroot",
|
"chroot",
|
||||||
"dd",
|
|
||||||
"groups",
|
"groups",
|
||||||
"hostid",
|
"hostid",
|
||||||
"id",
|
"id",
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
// Note: Conversion tables are just lookup tables.
|
// Note: Conversion tables are just lookup tables.
|
||||||
// eg. The ASCII->EBCDIC table stores the EBCDIC code at the index
|
// eg. The ASCII->EBCDIC table stores the EBCDIC code at the index
|
||||||
// obtained by treating the ASCII representation as a number.
|
// obtained by treating the ASCII representation as a number.
|
||||||
|
|
|
@ -5,23 +5,18 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use crate::conversion_tables::*;
|
use crate::conversion_tables::*;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
pub struct ProgUpdate {
|
pub struct ProgUpdate {
|
||||||
pub reads_complete: u64,
|
pub read_stat: ReadStat,
|
||||||
pub reads_partial: u64,
|
pub write_stat: WriteStat,
|
||||||
pub writes_complete: u64,
|
|
||||||
pub writes_partial: u64,
|
|
||||||
pub bytes_total: u128,
|
|
||||||
pub records_truncated: u32,
|
|
||||||
pub duration: time::Duration,
|
pub duration: time::Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct ReadStat {
|
pub struct ReadStat {
|
||||||
pub reads_complete: u64,
|
pub reads_complete: u64,
|
||||||
pub reads_partial: u64,
|
pub reads_partial: u64,
|
||||||
|
@ -37,6 +32,7 @@ impl std::ops::AddAssign for ReadStat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct WriteStat {
|
pub struct WriteStat {
|
||||||
pub writes_complete: u64,
|
pub writes_complete: u64,
|
||||||
pub writes_partial: u64,
|
pub writes_partial: u64,
|
||||||
|
@ -55,6 +51,7 @@ impl std::ops::AddAssign for WriteStat {
|
||||||
type Cbs = usize;
|
type Cbs = usize;
|
||||||
|
|
||||||
/// Stores all Conv Flags that apply to the input
|
/// Stores all Conv Flags that apply to the input
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
pub struct IConvFlags {
|
pub struct IConvFlags {
|
||||||
pub ctable: Option<&'static ConversionTable>,
|
pub ctable: Option<&'static ConversionTable>,
|
||||||
pub block: Option<Cbs>,
|
pub block: Option<Cbs>,
|
||||||
|
@ -65,7 +62,7 @@ pub struct IConvFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores all Conv Flags that apply to the output
|
/// Stores all Conv Flags that apply to the output
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
pub struct OConvFlags {
|
pub struct OConvFlags {
|
||||||
pub sparse: bool,
|
pub sparse: bool,
|
||||||
pub excl: bool,
|
pub excl: bool,
|
||||||
|
@ -76,32 +73,20 @@ pub struct OConvFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores all Flags that apply to the input
|
/// Stores all Flags that apply to the input
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
pub struct IFlags {
|
pub struct IFlags {
|
||||||
#[allow(dead_code)]
|
|
||||||
pub cio: bool,
|
pub cio: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub direct: bool,
|
pub direct: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub directory: bool,
|
pub directory: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub dsync: bool,
|
pub dsync: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub sync: bool,
|
pub sync: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nocache: bool,
|
pub nocache: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nonblock: bool,
|
pub nonblock: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub noatime: bool,
|
pub noatime: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub noctty: bool,
|
pub noctty: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nofollow: bool,
|
pub nofollow: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nolinks: bool,
|
pub nolinks: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub binary: bool,
|
pub binary: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub text: bool,
|
pub text: bool,
|
||||||
pub fullblock: bool,
|
pub fullblock: bool,
|
||||||
pub count_bytes: bool,
|
pub count_bytes: bool,
|
||||||
|
@ -109,33 +94,21 @@ pub struct IFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores all Flags that apply to the output
|
/// Stores all Flags that apply to the output
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
pub struct OFlags {
|
pub struct OFlags {
|
||||||
pub append: bool,
|
pub append: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub cio: bool,
|
pub cio: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub direct: bool,
|
pub direct: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub directory: bool,
|
pub directory: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub dsync: bool,
|
pub dsync: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub sync: bool,
|
pub sync: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nocache: bool,
|
pub nocache: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nonblock: bool,
|
pub nonblock: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub noatime: bool,
|
pub noatime: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub noctty: bool,
|
pub noctty: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nofollow: bool,
|
pub nofollow: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nolinks: bool,
|
pub nolinks: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub binary: bool,
|
pub binary: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub text: bool,
|
pub text: bool,
|
||||||
pub seek_bytes: bool,
|
pub seek_bytes: bool,
|
||||||
}
|
}
|
||||||
|
@ -153,6 +126,7 @@ pub enum StatusLevel {
|
||||||
/// Defaults to Reads(N)
|
/// Defaults to Reads(N)
|
||||||
/// if iflag=count_bytes
|
/// if iflag=count_bytes
|
||||||
/// then becomes Bytes(N)
|
/// then becomes Bytes(N)
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum CountType {
|
pub enum CountType {
|
||||||
Reads(usize),
|
Reads(usize),
|
||||||
Bytes(usize),
|
Bytes(usize),
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
use uucore::InvalidEncodingHandling;
|
use uucore::InvalidEncodingHandling;
|
||||||
|
@ -25,7 +23,6 @@ use conversion_tables::*;
|
||||||
|
|
||||||
use byte_unit::Byte;
|
use byte_unit::Byte;
|
||||||
use clap::{self, crate_version};
|
use clap::{self, crate_version};
|
||||||
use debug_print::debug_println;
|
|
||||||
use gcd::Gcd;
|
use gcd::Gcd;
|
||||||
use signal_hook::consts::signal;
|
use signal_hook::consts::signal;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
@ -51,7 +48,7 @@ struct Input<R: Read> {
|
||||||
src: R,
|
src: R,
|
||||||
non_ascii: bool,
|
non_ascii: bool,
|
||||||
ibs: usize,
|
ibs: usize,
|
||||||
xfer_stats: Option<StatusLevel>,
|
print_level: Option<StatusLevel>,
|
||||||
count: Option<CountType>,
|
count: Option<CountType>,
|
||||||
cflags: IConvFlags,
|
cflags: IConvFlags,
|
||||||
iflags: IFlags,
|
iflags: IFlags,
|
||||||
|
@ -61,7 +58,7 @@ impl Input<io::Stdin> {
|
||||||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||||
let ibs = parseargs::parse_ibs(matches)?;
|
let ibs = parseargs::parse_ibs(matches)?;
|
||||||
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
||||||
let xfer_stats = parseargs::parse_status_level(matches)?;
|
let print_level = parseargs::parse_status_level(matches)?;
|
||||||
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
||||||
let iflags = parseargs::parse_iflags(matches)?;
|
let iflags = parseargs::parse_iflags(matches)?;
|
||||||
let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?;
|
let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?;
|
||||||
|
@ -71,7 +68,7 @@ impl Input<io::Stdin> {
|
||||||
src: io::stdin(),
|
src: io::stdin(),
|
||||||
non_ascii,
|
non_ascii,
|
||||||
ibs,
|
ibs,
|
||||||
xfer_stats,
|
print_level,
|
||||||
count,
|
count,
|
||||||
cflags,
|
cflags,
|
||||||
iflags,
|
iflags,
|
||||||
|
@ -88,31 +85,31 @@ impl Input<io::Stdin> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn make_linux_iflags(oflags: &IFlags) -> Option<libc::c_int> {
|
fn make_linux_iflags(iflags: &IFlags) -> Option<libc::c_int> {
|
||||||
let mut flag = 0;
|
let mut flag = 0;
|
||||||
|
|
||||||
if oflags.direct {
|
if iflags.direct {
|
||||||
flag |= libc::O_DIRECT;
|
flag |= libc::O_DIRECT;
|
||||||
}
|
}
|
||||||
if oflags.directory {
|
if iflags.directory {
|
||||||
flag |= libc::O_DIRECTORY;
|
flag |= libc::O_DIRECTORY;
|
||||||
}
|
}
|
||||||
if oflags.dsync {
|
if iflags.dsync {
|
||||||
flag |= libc::O_DSYNC;
|
flag |= libc::O_DSYNC;
|
||||||
}
|
}
|
||||||
if oflags.noatime {
|
if iflags.noatime {
|
||||||
flag |= libc::O_NOATIME;
|
flag |= libc::O_NOATIME;
|
||||||
}
|
}
|
||||||
if oflags.noctty {
|
if iflags.noctty {
|
||||||
flag |= libc::O_NOCTTY;
|
flag |= libc::O_NOCTTY;
|
||||||
}
|
}
|
||||||
if oflags.nofollow {
|
if iflags.nofollow {
|
||||||
flag |= libc::O_NOFOLLOW;
|
flag |= libc::O_NOFOLLOW;
|
||||||
}
|
}
|
||||||
if oflags.nonblock {
|
if iflags.nonblock {
|
||||||
flag |= libc::O_NONBLOCK;
|
flag |= libc::O_NONBLOCK;
|
||||||
}
|
}
|
||||||
if oflags.sync {
|
if iflags.sync {
|
||||||
flag |= libc::O_SYNC;
|
flag |= libc::O_SYNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,13 +124,13 @@ impl Input<File> {
|
||||||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||||
let ibs = parseargs::parse_ibs(matches)?;
|
let ibs = parseargs::parse_ibs(matches)?;
|
||||||
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
||||||
let xfer_stats = parseargs::parse_status_level(matches)?;
|
let print_level = parseargs::parse_status_level(matches)?;
|
||||||
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
||||||
let iflags = parseargs::parse_iflags(matches)?;
|
let iflags = parseargs::parse_iflags(matches)?;
|
||||||
let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?;
|
let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?;
|
||||||
let count = parseargs::parse_count(&iflags, matches)?;
|
let count = parseargs::parse_count(&iflags, matches)?;
|
||||||
|
|
||||||
if let Some(fname) = matches.value_of("if") {
|
if let Some(fname) = matches.value_of(options::INFILE) {
|
||||||
let mut src = {
|
let mut src = {
|
||||||
let mut opts = OpenOptions::new();
|
let mut opts = OpenOptions::new();
|
||||||
opts.read(true);
|
opts.read(true);
|
||||||
|
@ -155,7 +152,7 @@ impl Input<File> {
|
||||||
src,
|
src,
|
||||||
non_ascii,
|
non_ascii,
|
||||||
ibs,
|
ibs,
|
||||||
xfer_stats,
|
print_level,
|
||||||
count,
|
count,
|
||||||
cflags,
|
cflags,
|
||||||
iflags,
|
iflags,
|
||||||
|
@ -198,28 +195,27 @@ impl<R: Read> Input<R> {
|
||||||
fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> Result<ReadStat, 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_complete = 0;
|
||||||
let mut reads_partial = 0;
|
let mut reads_partial = 0;
|
||||||
let mut base_idx = 0;
|
let mut bytes_total = 0;
|
||||||
|
|
||||||
while base_idx < buf.len() {
|
for chunk in buf.chunks_mut(self.ibs) {
|
||||||
let next_blk = cmp::min(base_idx + self.ibs, buf.len());
|
match self.read(chunk)? {
|
||||||
|
|
||||||
match self.read(&mut buf[base_idx..next_blk])? {
|
|
||||||
rlen if rlen == self.ibs => {
|
rlen if rlen == self.ibs => {
|
||||||
base_idx += rlen;
|
bytes_total += rlen;
|
||||||
reads_complete += 1;
|
reads_complete += 1;
|
||||||
}
|
}
|
||||||
rlen if rlen > 0 => {
|
rlen if rlen > 0 => {
|
||||||
base_idx += rlen;
|
bytes_total += rlen;
|
||||||
reads_partial += 1;
|
reads_partial += 1;
|
||||||
}
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.truncate(base_idx);
|
buf.truncate(bytes_total);
|
||||||
Ok(ReadStat {
|
Ok(ReadStat {
|
||||||
reads_complete,
|
reads_complete,
|
||||||
reads_partial,
|
reads_partial,
|
||||||
|
// Records are not truncated when filling.
|
||||||
records_truncated: 0,
|
records_truncated: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -234,35 +230,19 @@ impl<R: Read> Input<R> {
|
||||||
|
|
||||||
while base_idx < buf.len() {
|
while base_idx < buf.len() {
|
||||||
let next_blk = cmp::min(base_idx + self.ibs, buf.len());
|
let next_blk = cmp::min(base_idx + self.ibs, buf.len());
|
||||||
let plen = next_blk - base_idx;
|
let target_len = next_blk - base_idx;
|
||||||
|
|
||||||
match self.read(&mut buf[base_idx..next_blk])? {
|
match self.read(&mut buf[base_idx..next_blk])? {
|
||||||
0 => break,
|
0 => break,
|
||||||
rlen if rlen < plen => {
|
rlen if rlen < target_len => {
|
||||||
reads_partial += 1;
|
reads_partial += 1;
|
||||||
let padding = vec![pad; plen - rlen];
|
let padding = vec![pad; target_len - rlen];
|
||||||
buf.splice(base_idx + rlen..next_blk, padding.into_iter());
|
buf.splice(base_idx + rlen..next_blk, padding.into_iter());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
reads_complete += 1;
|
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;
|
|
||||||
// }
|
|
||||||
|
|
||||||
base_idx += self.ibs;
|
base_idx += self.ibs;
|
||||||
}
|
}
|
||||||
|
@ -356,11 +336,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
|
||||||
|
|
||||||
impl Output<File> {
|
impl Output<File> {
|
||||||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||||
fn open_dst(
|
fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
|
||||||
path: &Path,
|
|
||||||
cflags: &OConvFlags,
|
|
||||||
oflags: &OFlags,
|
|
||||||
) -> Result<File, Box<dyn Error>> {
|
|
||||||
let mut opts = OpenOptions::new();
|
let mut opts = OpenOptions::new();
|
||||||
opts.write(true)
|
opts.write(true)
|
||||||
.create(!cflags.nocreat)
|
.create(!cflags.nocreat)
|
||||||
|
@ -373,15 +349,14 @@ impl Output<File> {
|
||||||
opts.custom_flags(libc_flags);
|
opts.custom_flags(libc_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dst = opts.open(path)?;
|
opts.open(path)
|
||||||
Ok(dst)
|
|
||||||
}
|
}
|
||||||
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)?;
|
||||||
let oflags = parseargs::parse_oflags(matches)?;
|
let oflags = parseargs::parse_oflags(matches)?;
|
||||||
let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?;
|
let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?;
|
||||||
|
|
||||||
if let Some(fname) = matches.value_of("of") {
|
if let Some(fname) = matches.value_of(options::OUTFILE) {
|
||||||
let mut dst = open_dst(Path::new(&fname), &cflags, &oflags)?;
|
let mut dst = open_dst(Path::new(&fname), &cflags, &oflags)?;
|
||||||
|
|
||||||
if let Some(amt) = seek {
|
if let Some(amt) = seek {
|
||||||
|
@ -418,7 +393,6 @@ impl Seek for Output<File> {
|
||||||
|
|
||||||
impl Write for Output<File> {
|
impl Write for Output<File> {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
#[inline]
|
|
||||||
fn is_sparse(buf: &[u8]) -> bool {
|
fn is_sparse(buf: &[u8]) -> bool {
|
||||||
buf.iter().all(|&e| e == 0u8)
|
buf.iter().all(|&e| e == 0u8)
|
||||||
}
|
}
|
||||||
|
@ -454,20 +428,17 @@ impl Output<io::Stdout> {
|
||||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
|
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
|
||||||
let mut writes_complete = 0;
|
let mut writes_complete = 0;
|
||||||
let mut writes_partial = 0;
|
let mut writes_partial = 0;
|
||||||
let mut base_idx = 0;
|
let mut bytes_total = 0;
|
||||||
|
|
||||||
while base_idx < buf.len() {
|
for chunk in buf.chunks(self.obs) {
|
||||||
let next_blk = cmp::min(base_idx + self.obs, buf.len());
|
match self.write(chunk)? {
|
||||||
let plen = next_blk - base_idx;
|
wlen if wlen < chunk.len() => {
|
||||||
|
|
||||||
match self.write(&buf[base_idx..next_blk])? {
|
|
||||||
wlen if wlen < plen => {
|
|
||||||
writes_partial += 1;
|
writes_partial += 1;
|
||||||
base_idx += wlen;
|
bytes_total += wlen;
|
||||||
}
|
}
|
||||||
wlen => {
|
wlen => {
|
||||||
writes_complete += 1;
|
writes_complete += 1;
|
||||||
base_idx += wlen;
|
bytes_total += wlen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,7 +446,7 @@ impl Output<io::Stdout> {
|
||||||
Ok(WriteStat {
|
Ok(WriteStat {
|
||||||
writes_complete,
|
writes_complete,
|
||||||
writes_partial,
|
writes_partial,
|
||||||
bytes_total: base_idx.try_into().unwrap_or(0u128),
|
bytes_total: bytes_total.try_into().unwrap_or(0u128),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -484,20 +455,17 @@ impl Output<File> {
|
||||||
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
|
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
|
||||||
let mut writes_complete = 0;
|
let mut writes_complete = 0;
|
||||||
let mut writes_partial = 0;
|
let mut writes_partial = 0;
|
||||||
let mut base_idx = 0;
|
let mut bytes_total = 0;
|
||||||
|
|
||||||
while base_idx < buf.len() {
|
for chunk in buf.chunks(self.obs) {
|
||||||
let next_blk = cmp::min(base_idx + self.obs, buf.len());
|
match self.write(chunk)? {
|
||||||
let plen = next_blk - base_idx;
|
wlen if wlen < chunk.len() => {
|
||||||
|
|
||||||
match self.write(&buf[base_idx..next_blk])? {
|
|
||||||
wlen if wlen < plen => {
|
|
||||||
writes_partial += 1;
|
writes_partial += 1;
|
||||||
base_idx += wlen;
|
bytes_total += wlen;
|
||||||
}
|
}
|
||||||
wlen => {
|
wlen => {
|
||||||
writes_complete += 1;
|
writes_complete += 1;
|
||||||
base_idx += wlen;
|
bytes_total += wlen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,7 +473,7 @@ impl Output<File> {
|
||||||
Ok(WriteStat {
|
Ok(WriteStat {
|
||||||
writes_complete,
|
writes_complete,
|
||||||
writes_partial,
|
writes_partial,
|
||||||
bytes_total: base_idx.try_into().unwrap_or(0u128),
|
bytes_total: bytes_total.try_into().unwrap_or(0u128),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -538,47 +506,18 @@ fn block(buf: Vec<u8>, cbs: usize, rstat: &mut ReadStat) -> Vec<Vec<u8>> {
|
||||||
/// Trims padding from each cbs-length partition of buf
|
/// Trims padding from each cbs-length partition of buf
|
||||||
/// as specified by conv=unblock and cbs=N
|
/// as specified by conv=unblock and cbs=N
|
||||||
fn unblock(buf: Vec<u8>, cbs: usize) -> Vec<u8> {
|
fn unblock(buf: Vec<u8>, cbs: usize) -> Vec<u8> {
|
||||||
// Local Helper Fns ----------------------------------------------------
|
buf.chunks(cbs).fold(Vec::new(), |mut acc, block| {
|
||||||
#[inline]
|
if let Some(last_char_idx) = block.iter().rposition(|&e| e != b' ') {
|
||||||
fn build_blocks(buf: Vec<u8>, cbs: usize) -> Vec<Vec<u8>> {
|
// Find last space
|
||||||
let mut blocks = Vec::new();
|
acc.extend(&block[..=last_char_idx]);
|
||||||
let mut curr = buf;
|
acc.push(b'\n');
|
||||||
let mut next;
|
} else {
|
||||||
let mut width;
|
// The block is filled with only spaces
|
||||||
|
acc.push(b'\n');
|
||||||
|
};
|
||||||
|
|
||||||
while !curr.is_empty() {
|
acc
|
||||||
width = cmp::min(cbs, curr.len());
|
})
|
||||||
next = curr.split_off(width);
|
|
||||||
|
|
||||||
blocks.push(curr);
|
|
||||||
|
|
||||||
curr = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks
|
|
||||||
}
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
build_blocks(buf, cbs)
|
|
||||||
.into_iter()
|
|
||||||
.fold(Vec::new(), |mut unblocks, mut block| {
|
|
||||||
let block = if let Some(last_char_idx) = block.iter().rposition(|&e| e != b' ') {
|
|
||||||
block.truncate(last_char_idx + 1);
|
|
||||||
block.push(b'\n');
|
|
||||||
|
|
||||||
block
|
|
||||||
} else if let Some(b' ') = block.get(0) {
|
|
||||||
vec![b'\n']
|
|
||||||
} else {
|
|
||||||
block
|
|
||||||
};
|
|
||||||
|
|
||||||
unblocks.push(block);
|
|
||||||
|
|
||||||
unblocks
|
|
||||||
})
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conv_block_unblock_helper<R: Read>(
|
fn conv_block_unblock_helper<R: Read>(
|
||||||
|
@ -587,19 +526,15 @@ fn conv_block_unblock_helper<R: Read>(
|
||||||
rstat: &mut ReadStat,
|
rstat: &mut ReadStat,
|
||||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
// Local Predicate Fns -------------------------------------------------
|
// Local Predicate Fns -------------------------------------------------
|
||||||
#[inline]
|
|
||||||
fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool {
|
fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool {
|
||||||
!i.non_ascii && i.cflags.block.is_some()
|
!i.non_ascii && i.cflags.block.is_some()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn should_conv_then_block<R: Read>(i: &Input<R>) -> bool {
|
fn should_conv_then_block<R: Read>(i: &Input<R>) -> bool {
|
||||||
i.non_ascii && i.cflags.block.is_some()
|
i.non_ascii && i.cflags.block.is_some()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn should_unblock_then_conv<R: Read>(i: &Input<R>) -> bool {
|
fn should_unblock_then_conv<R: Read>(i: &Input<R>) -> bool {
|
||||||
!i.non_ascii && i.cflags.unblock.is_some()
|
!i.non_ascii && i.cflags.unblock.is_some()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn should_conv_then_unblock<R: Read>(i: &Input<R>) -> bool {
|
fn should_conv_then_unblock<R: Read>(i: &Input<R>) -> bool {
|
||||||
i.non_ascii && i.cflags.unblock.is_some()
|
i.non_ascii && i.cflags.unblock.is_some()
|
||||||
}
|
}
|
||||||
|
@ -607,8 +542,7 @@ fn conv_block_unblock_helper<R: Read>(
|
||||||
i.cflags.ctable.is_some() && i.cflags.block.is_none() && i.cflags.unblock.is_none()
|
i.cflags.ctable.is_some() && i.cflags.block.is_none() && i.cflags.unblock.is_none()
|
||||||
}
|
}
|
||||||
// Local Helper Fns ----------------------------------------------------
|
// Local Helper Fns ----------------------------------------------------
|
||||||
#[inline]
|
fn apply_conversion(buf: &mut [u8], ct: &ConversionTable) {
|
||||||
fn apply_ct(buf: &mut [u8], ct: &ConversionTable) {
|
|
||||||
for idx in 0..buf.len() {
|
for idx in 0..buf.len() {
|
||||||
buf[idx] = ct[buf[idx] as usize];
|
buf[idx] = ct[buf[idx] as usize];
|
||||||
}
|
}
|
||||||
|
@ -617,7 +551,7 @@ fn conv_block_unblock_helper<R: Read>(
|
||||||
if conv_only(i) {
|
if conv_only(i) {
|
||||||
// no block/unblock
|
// no block/unblock
|
||||||
let ct = i.cflags.ctable.unwrap();
|
let ct = i.cflags.ctable.unwrap();
|
||||||
apply_ct(&mut buf, ct);
|
apply_conversion(&mut buf, ct);
|
||||||
|
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
} else if should_block_then_conv(i) {
|
} else if should_block_then_conv(i) {
|
||||||
|
@ -628,7 +562,7 @@ fn conv_block_unblock_helper<R: Read>(
|
||||||
|
|
||||||
if let Some(ct) = i.cflags.ctable {
|
if let Some(ct) = i.cflags.ctable {
|
||||||
for buf in blocks.iter_mut() {
|
for buf in blocks.iter_mut() {
|
||||||
apply_ct(buf, ct);
|
apply_conversion(buf, ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,7 +574,7 @@ fn conv_block_unblock_helper<R: Read>(
|
||||||
let cbs = i.cflags.block.unwrap();
|
let cbs = i.cflags.block.unwrap();
|
||||||
|
|
||||||
if let Some(ct) = i.cflags.ctable {
|
if let Some(ct) = i.cflags.ctable {
|
||||||
apply_ct(&mut buf, ct);
|
apply_conversion(&mut buf, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
let blocks = block(buf, cbs, rstat).into_iter().flatten().collect();
|
let blocks = block(buf, cbs, rstat).into_iter().flatten().collect();
|
||||||
|
@ -653,7 +587,7 @@ fn conv_block_unblock_helper<R: Read>(
|
||||||
let mut buf = unblock(buf, cbs);
|
let mut buf = unblock(buf, cbs);
|
||||||
|
|
||||||
if let Some(ct) = i.cflags.ctable {
|
if let Some(ct) = i.cflags.ctable {
|
||||||
apply_ct(&mut buf, ct);
|
apply_conversion(&mut buf, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
|
@ -662,7 +596,7 @@ fn conv_block_unblock_helper<R: Read>(
|
||||||
let cbs = i.cflags.unblock.unwrap();
|
let cbs = i.cflags.unblock.unwrap();
|
||||||
|
|
||||||
if let Some(ct) = i.cflags.ctable {
|
if let Some(ct) = i.cflags.ctable {
|
||||||
apply_ct(&mut buf, ct);
|
apply_conversion(&mut buf, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
let buf = unblock(buf, cbs);
|
let buf = unblock(buf, cbs);
|
||||||
|
@ -683,27 +617,19 @@ fn read_helper<R: Read>(
|
||||||
bsize: usize,
|
bsize: usize,
|
||||||
) -> Result<(ReadStat, Vec<u8>), Box<dyn Error>> {
|
) -> Result<(ReadStat, Vec<u8>), Box<dyn Error>> {
|
||||||
// Local Predicate Fns -----------------------------------------------
|
// Local Predicate Fns -----------------------------------------------
|
||||||
#[inline]
|
|
||||||
fn is_conv<R: Read>(i: &Input<R>) -> bool {
|
fn is_conv<R: Read>(i: &Input<R>) -> bool {
|
||||||
i.cflags.ctable.is_some()
|
i.cflags.ctable.is_some()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn is_block<R: Read>(i: &Input<R>) -> bool {
|
fn is_block<R: Read>(i: &Input<R>) -> bool {
|
||||||
i.cflags.block.is_some()
|
i.cflags.block.is_some()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn is_unblock<R: Read>(i: &Input<R>) -> bool {
|
fn is_unblock<R: Read>(i: &Input<R>) -> bool {
|
||||||
i.cflags.unblock.is_some()
|
i.cflags.unblock.is_some()
|
||||||
}
|
}
|
||||||
// Local Helper Fns -------------------------------------------------
|
// Local Helper Fns -------------------------------------------------
|
||||||
#[inline]
|
|
||||||
fn perform_swab(buf: &mut [u8]) {
|
fn perform_swab(buf: &mut [u8]) {
|
||||||
let mut tmp;
|
|
||||||
|
|
||||||
for base in (1..buf.len()).step_by(2) {
|
for base in (1..buf.len()).step_by(2) {
|
||||||
tmp = buf[base];
|
buf.swap(base, base - 1);
|
||||||
buf[base] = buf[base - 1];
|
|
||||||
buf[base - 1] = tmp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
@ -733,35 +659,35 @@ fn read_helper<R: Read>(
|
||||||
fn print_io_lines(update: &ProgUpdate) {
|
fn print_io_lines(update: &ProgUpdate) {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}+{} records in",
|
"{}+{} records in",
|
||||||
update.reads_complete, update.reads_partial
|
update.read_stat.reads_complete, update.read_stat.reads_partial
|
||||||
);
|
);
|
||||||
if update.records_truncated > 0 {
|
if update.read_stat.records_truncated > 0 {
|
||||||
eprintln!("{} truncated records", update.records_truncated);
|
eprintln!("{} truncated records", update.read_stat.records_truncated);
|
||||||
}
|
}
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}+{} records out",
|
"{}+{} records out",
|
||||||
update.writes_complete, update.writes_partial
|
update.write_stat.writes_complete, update.write_stat.writes_partial
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
fn make_prog_line(update: &ProgUpdate) -> String {
|
fn make_prog_line(update: &ProgUpdate) -> String {
|
||||||
let btotal_metric = Byte::from_bytes(update.bytes_total)
|
let btotal_metric = Byte::from_bytes(update.write_stat.bytes_total)
|
||||||
.get_appropriate_unit(false)
|
.get_appropriate_unit(false)
|
||||||
.format(0);
|
.format(0);
|
||||||
let btotal_bin = Byte::from_bytes(update.bytes_total)
|
let btotal_bin = Byte::from_bytes(update.write_stat.bytes_total)
|
||||||
.get_appropriate_unit(true)
|
.get_appropriate_unit(true)
|
||||||
.format(0);
|
.format(0);
|
||||||
let safe_millis = cmp::max(1, update.duration.as_millis());
|
let safe_millis = cmp::max(1, update.duration.as_millis());
|
||||||
let xfer_rate = Byte::from_bytes(1000 * (update.bytes_total / safe_millis))
|
let transfer_rate = Byte::from_bytes(1000 * (update.write_stat.bytes_total / safe_millis))
|
||||||
.get_appropriate_unit(false)
|
.get_appropriate_unit(false)
|
||||||
.format(1);
|
.format(1);
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"{} bytes ({}, {}) copied, {:.1} s, {}/s",
|
"{} bytes ({}, {}) copied, {:.1} s, {}/s",
|
||||||
update.bytes_total,
|
update.write_stat.bytes_total,
|
||||||
btotal_metric,
|
btotal_metric,
|
||||||
btotal_bin,
|
btotal_bin,
|
||||||
update.duration.as_secs_f64(),
|
update.duration.as_secs_f64(),
|
||||||
xfer_rate
|
transfer_rate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn reprint_prog_line(update: &ProgUpdate) {
|
fn reprint_prog_line(update: &ProgUpdate) {
|
||||||
|
@ -770,65 +696,60 @@ fn reprint_prog_line(update: &ProgUpdate) {
|
||||||
fn print_prog_line(update: &ProgUpdate) {
|
fn print_prog_line(update: &ProgUpdate) {
|
||||||
eprintln!("{}", make_prog_line(update));
|
eprintln!("{}", make_prog_line(update));
|
||||||
}
|
}
|
||||||
fn print_xfer_stats(update: &ProgUpdate) {
|
fn print_transfer_stats(update: &ProgUpdate) {
|
||||||
print_io_lines(update);
|
print_io_lines(update);
|
||||||
print_prog_line(update);
|
print_prog_line(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a progress updater that tracks progress, receives updates, and responds to signals.
|
/// Generate a progress updater that tracks progress, receives updates, and responds to progress update requests (signals).
|
||||||
fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, xfer_stats: Option<StatusLevel>) -> impl Fn() {
|
fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, print_level: Option<StatusLevel>) -> impl Fn() {
|
||||||
|
// --------------------------------------------------------------
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
const SIGUSR1_USIZE: usize = signal::SIGUSR1 as usize;
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
fn posixly_correct() -> bool {
|
fn posixly_correct() -> bool {
|
||||||
env::var("POSIXLY_CORRECT").is_ok()
|
env::var("POSIXLY_CORRECT").is_ok()
|
||||||
}
|
}
|
||||||
|
fn register_signal_handlers(sigval: Arc<AtomicUsize>) -> Result<(), Box<dyn Error>> {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
if !posixly_correct() {
|
||||||
|
signal_hook::flag::register_usize(signal::SIGUSR1, sigval, SIGUSR1_USIZE)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
move || {
|
move || {
|
||||||
const SIGUSR1_USIZE: usize = signal::SIGUSR1 as usize;
|
|
||||||
|
|
||||||
let sigval = Arc::new(AtomicUsize::new(0));
|
let sigval = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
// TODO: SIGINFO seems to only exist for BSD (and therefore MACOS)
|
register_signal_handlers(sigval.clone()).unwrap_or_else(|e| {
|
||||||
// I will probably want put this behind a feature-gate and may need to pass the value to handle as my own constant.
|
if Some(StatusLevel::None) != print_level {
|
||||||
// This may involve some finagling with the signals library.
|
eprintln!(
|
||||||
// see -> https://unix.stackexchange.com/questions/179481/siginfo-on-gnu-linux-arch-linux-missing
|
"Internal dd Warning: Unable to register signal handler \n\t{}",
|
||||||
// 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
|
e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Wait for update
|
// Wait for update
|
||||||
let update = match (rx.recv(), xfer_stats) {
|
let update = match (rx.recv(), print_level) {
|
||||||
(Ok(update), Some(StatusLevel::Progress)) => {
|
(Ok(update), Some(StatusLevel::Progress)) => {
|
||||||
reprint_prog_line(&update);
|
reprint_prog_line(&update);
|
||||||
|
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
(Ok(update), _) => update,
|
(Ok(update), _) => update,
|
||||||
(Err(_), _) =>
|
(Err(_), _) => {
|
||||||
// recv only fails permanently
|
// recv only fails permanently, so we break here to
|
||||||
{
|
// avoid recv'ing on a broken pipe
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Handle signals
|
// Handle signals
|
||||||
#[allow(clippy::single_match)]
|
#[cfg(target_os = "linux")]
|
||||||
match sigval.load(Ordering::Relaxed) {
|
if let SIGUSR1_USIZE = sigval.load(Ordering::Relaxed) {
|
||||||
SIGUSR1_USIZE => {
|
print_transfer_stats(&update);
|
||||||
print_xfer_stats(&update);
|
|
||||||
}
|
|
||||||
// SIGINFO_USIZE => ...
|
|
||||||
_ => { /* no signals recv'd */ }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -840,7 +761,6 @@ fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, xfer_stats: Option<StatusLev
|
||||||
/// sane real-world memory use, it should not be too large. I believe
|
/// sane real-world memory use, it should not be too large. I believe
|
||||||
/// the least common multiple is a good representation of these interests.
|
/// the least common multiple is a good representation of these interests.
|
||||||
/// https://en.wikipedia.org/wiki/Least_common_multiple#Using_the_greatest_common_divisor
|
/// https://en.wikipedia.org/wiki/Least_common_multiple#Using_the_greatest_common_divisor
|
||||||
#[inline]
|
|
||||||
fn calc_bsize(ibs: usize, obs: usize) -> usize {
|
fn calc_bsize(ibs: usize, obs: usize) -> usize {
|
||||||
let gcd = Gcd::gcd(ibs, obs);
|
let gcd = Gcd::gcd(ibs, obs);
|
||||||
// calculate the lcm from gcd
|
// calculate the lcm from gcd
|
||||||
|
@ -878,12 +798,10 @@ fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteS
|
||||||
match count {
|
match count {
|
||||||
Some(CountType::Reads(n)) => {
|
Some(CountType::Reads(n)) => {
|
||||||
let n = (*n).try_into().unwrap();
|
let n = (*n).try_into().unwrap();
|
||||||
// debug_assert!(rstat.reads_complete + rstat.reads_partial >= n);
|
|
||||||
rstat.reads_complete + rstat.reads_partial <= n
|
rstat.reads_complete + rstat.reads_partial <= n
|
||||||
}
|
}
|
||||||
Some(CountType::Bytes(n)) => {
|
Some(CountType::Bytes(n)) => {
|
||||||
let n = (*n).try_into().unwrap();
|
let n = (*n).try_into().unwrap();
|
||||||
// debug_assert!(wstat.bytes_total >= n);
|
|
||||||
wstat.bytes_total <= n
|
wstat.bytes_total <= n
|
||||||
}
|
}
|
||||||
None => true,
|
None => true,
|
||||||
|
@ -891,8 +809,8 @@ fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteS
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform the copy/convert operations. Stdout version
|
/// Perform the copy/convert operations. Stdout version
|
||||||
// Note: Some of dd's functionality depends on whether the output is actually a file. This breaks the Output<Write> abstraction,
|
/// 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 fixed in the future.
|
/// 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>> {
|
fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(), Box<dyn Error>> {
|
||||||
let mut rstat = ReadStat {
|
let mut rstat = ReadStat {
|
||||||
reads_complete: 0,
|
reads_complete: 0,
|
||||||
|
@ -909,7 +827,7 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
|
||||||
|
|
||||||
let prog_tx = {
|
let prog_tx = {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
thread::spawn(gen_prog_updater(rx, i.xfer_stats));
|
thread::spawn(gen_prog_updater(rx, i.print_level));
|
||||||
tx
|
tx
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -934,12 +852,8 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
|
||||||
};
|
};
|
||||||
// Update Prog
|
// Update Prog
|
||||||
prog_tx.send(ProgUpdate {
|
prog_tx.send(ProgUpdate {
|
||||||
reads_complete: rstat.reads_complete,
|
read_stat: rstat,
|
||||||
reads_partial: rstat.reads_partial,
|
write_stat: wstat,
|
||||||
writes_complete: wstat.writes_complete,
|
|
||||||
writes_partial: wstat.writes_partial,
|
|
||||||
bytes_total: wstat.bytes_total,
|
|
||||||
records_truncated: rstat.records_truncated,
|
|
||||||
duration: start.elapsed(),
|
duration: start.elapsed(),
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -950,15 +864,11 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
|
||||||
o.fdatasync()?;
|
o.fdatasync()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
match i.xfer_stats {
|
match i.print_level {
|
||||||
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
||||||
_ => print_xfer_stats(&ProgUpdate {
|
_ => print_transfer_stats(&ProgUpdate {
|
||||||
reads_complete: rstat.reads_complete,
|
read_stat: rstat,
|
||||||
reads_partial: rstat.reads_partial,
|
write_stat: wstat,
|
||||||
writes_complete: wstat.writes_complete,
|
|
||||||
writes_partial: wstat.writes_partial,
|
|
||||||
bytes_total: wstat.bytes_total,
|
|
||||||
records_truncated: rstat.records_truncated,
|
|
||||||
duration: start.elapsed(),
|
duration: start.elapsed(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -966,8 +876,8 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform the copy/convert operations. File backed output version
|
/// Perform the copy/convert operations. 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,
|
/// 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 fixed in the future.
|
/// 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>> {
|
fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<dyn Error>> {
|
||||||
let mut rstat = ReadStat {
|
let mut rstat = ReadStat {
|
||||||
reads_complete: 0,
|
reads_complete: 0,
|
||||||
|
@ -984,7 +894,7 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<d
|
||||||
|
|
||||||
let prog_tx = {
|
let prog_tx = {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
thread::spawn(gen_prog_updater(rx, i.xfer_stats));
|
thread::spawn(gen_prog_updater(rx, i.print_level));
|
||||||
tx
|
tx
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1009,12 +919,8 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<d
|
||||||
};
|
};
|
||||||
// Update Prog
|
// Update Prog
|
||||||
prog_tx.send(ProgUpdate {
|
prog_tx.send(ProgUpdate {
|
||||||
reads_complete: rstat.reads_complete,
|
read_stat: rstat,
|
||||||
reads_partial: rstat.reads_partial,
|
write_stat: wstat,
|
||||||
writes_complete: wstat.writes_complete,
|
|
||||||
writes_partial: wstat.writes_partial,
|
|
||||||
bytes_total: wstat.bytes_total,
|
|
||||||
records_truncated: rstat.records_truncated,
|
|
||||||
duration: start.elapsed(),
|
duration: start.elapsed(),
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -1025,27 +931,22 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<d
|
||||||
o.fdatasync()?;
|
o.fdatasync()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
match i.xfer_stats {
|
match i.print_level {
|
||||||
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
||||||
_ => print_xfer_stats(&ProgUpdate {
|
_ => print_transfer_stats(&ProgUpdate {
|
||||||
reads_complete: rstat.reads_complete,
|
read_stat: rstat,
|
||||||
reads_partial: rstat.reads_partial,
|
write_stat: wstat,
|
||||||
writes_complete: wstat.writes_complete,
|
|
||||||
writes_partial: wstat.writes_partial,
|
|
||||||
bytes_total: wstat.bytes_total,
|
|
||||||
records_truncated: rstat.records_truncated,
|
|
||||||
duration: start.elapsed(),
|
duration: start.elapsed(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// The compiler does not like Clippy's suggestion to use &str in place of &String here.
|
fn append_dashes_if_not_present(mut acc: Vec<String>, mut s: String) -> Vec<String> {
|
||||||
#[allow(clippy::ptr_arg)]
|
if !s.starts_with("--") && !s.starts_with("-") {
|
||||||
fn append_dashes_if_not_present(mut acc: Vec<String>, s: &String) -> Vec<String> {
|
s.insert_str(0, "--");
|
||||||
if Some("--") != s.get(0..=1) {
|
|
||||||
acc.push(format!("--{}", s));
|
|
||||||
}
|
}
|
||||||
|
acc.push(s);
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1074,13 +975,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let dashed_args = args
|
let dashed_args = args
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any()
|
.accept_any()
|
||||||
.iter()
|
.into_iter()
|
||||||
.fold(Vec::new(), append_dashes_if_not_present);
|
.fold(Vec::new(), append_dashes_if_not_present);
|
||||||
|
|
||||||
let matches = uu_app()
|
let matches = uu_app()
|
||||||
// TODO: usage, after_help
|
//.after_help(TODO: Add note about multiplier strings here.)
|
||||||
//.usage(...)
|
|
||||||
//.after_help(...)
|
|
||||||
.get_matches_from(dashed_args);
|
.get_matches_from(dashed_args);
|
||||||
|
|
||||||
let result = match (
|
let result = match (
|
||||||
|
@ -1121,7 +1020,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => RTN_SUCCESS,
|
Ok(_) => RTN_SUCCESS,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug_println!("dd exiting with error:\n\t{}", e);
|
eprintln!("dd exiting with error:\n\t{}", e);
|
||||||
RTN_FAILURE
|
RTN_FAILURE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1211,7 +1110,7 @@ Printing performance stats is also triggered by the INFO signal (where supported
|
||||||
clap::Arg::with_name(options::CONV)
|
clap::Arg::with_name(options::CONV)
|
||||||
.long(options::CONV)
|
.long(options::CONV)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file-flags. Conversion options and file flags may be intermixed.
|
.help("conv=CONV[,CONV] (alternatively --conv CONV[,CONV]) specifies a comma-separated list of conversion options or (for legacy reasons) file flags. Conversion options and file flags may be intermixed.
|
||||||
|
|
||||||
Conversion options:
|
Conversion options:
|
||||||
\t One of {ascii, ebcdic, ibm} will perform an encoding conversion.
|
\t One of {ascii, ebcdic, ibm} will perform an encoding conversion.
|
||||||
|
@ -1231,7 +1130,7 @@ Conversion options:
|
||||||
\t 'swab' swaps each adjacent pair of bytes. If an odd number of bytes is present, the final byte is omitted.
|
\t 'swab' swaps each adjacent pair of bytes. If an odd number of bytes is present, the final byte is omitted.
|
||||||
\t 'sync' pad each ibs-sided block with zeros. If 'block' or 'unblock' is specified, pad with spaces instead.
|
\t 'sync' pad each ibs-sided block with zeros. If 'block' or 'unblock' is specified, pad with spaces instead.
|
||||||
|
|
||||||
Flags:
|
Conversion Flags:
|
||||||
\t One of {excl, nocreat}
|
\t One of {excl, nocreat}
|
||||||
\t\t 'excl' the output file must be created. Fail if the output file is already present.
|
\t\t 'excl' the output file must be created. Fail if the output file is already present.
|
||||||
\t\t 'nocreat' the output file will not be created. Fail if the output file in not already present.
|
\t\t 'nocreat' the output file will not be created. Fail if the output file in not already present.
|
||||||
|
@ -1264,10 +1163,6 @@ General-Flags
|
||||||
\t 'noctty' do not assign a controlling tty.
|
\t 'noctty' do not assign a controlling tty.
|
||||||
\t 'nofollow' do not follow system links.
|
\t 'nofollow' do not follow system links.
|
||||||
|
|
||||||
Output-Flags
|
|
||||||
\t 'append' open file in append mode. Consider setting conv=notrunc as well.
|
|
||||||
\t 'seek_bytes' a value to seek=N will be interpreted as bytes.
|
|
||||||
|
|
||||||
")
|
")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
|
|
@ -1,20 +1,7 @@
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const NL: u8 = '\n' as u8;
|
const NL: u8 = b'\n';
|
||||||
const SPACE: u8 = ' ' as u8;
|
const SPACE: u8 = b' ';
|
||||||
|
|
||||||
macro_rules! rs (
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
ReadStat {
|
|
||||||
reads_complete: 0,
|
|
||||||
reads_partial: 0,
|
|
||||||
records_truncated: 0,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! make_block_test (
|
macro_rules! make_block_test (
|
||||||
( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) =>
|
( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) =>
|
||||||
|
@ -25,7 +12,7 @@ macro_rules! make_block_test (
|
||||||
src: $src,
|
src: $src,
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 512,
|
ibs: 512,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: IConvFlags {
|
cflags: IConvFlags {
|
||||||
ctable: None,
|
ctable: None,
|
||||||
|
@ -57,7 +44,7 @@ macro_rules! make_unblock_test (
|
||||||
src: $src,
|
src: $src,
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 512,
|
ibs: 512,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: IConvFlags {
|
cflags: IConvFlags {
|
||||||
ctable: None,
|
ctable: None,
|
||||||
|
@ -82,7 +69,7 @@ macro_rules! make_unblock_test (
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_no_nl() {
|
fn block_test_no_nl() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8];
|
||||||
let res = block(buf, 4, &mut rs);
|
let res = block(buf, 4, &mut rs);
|
||||||
|
|
||||||
|
@ -91,7 +78,7 @@ fn block_test_no_nl() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_no_nl_short_record() {
|
fn block_test_no_nl_short_record() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8];
|
||||||
let res = block(buf, 8, &mut rs);
|
let res = block(buf, 8, &mut rs);
|
||||||
|
|
||||||
|
@ -103,17 +90,18 @@ fn block_test_no_nl_short_record() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_no_nl_trunc() {
|
fn block_test_no_nl_trunc() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8];
|
||||||
let res = block(buf, 4, &mut rs);
|
let res = block(buf, 4, &mut rs);
|
||||||
|
|
||||||
|
// Commented section should be truncated and appear for reference only.
|
||||||
assert_eq!(res, vec![vec![0u8, 1u8, 2u8, 3u8 /*, 4u8*/],]);
|
assert_eq!(res, vec![vec![0u8, 1u8, 2u8, 3u8 /*, 4u8*/],]);
|
||||||
assert_eq!(rs.records_truncated, 1);
|
assert_eq!(rs.records_truncated, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_nl_gt_cbs_trunc() {
|
fn block_test_nl_gt_cbs_trunc() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![
|
let buf = vec![
|
||||||
0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8,
|
0u8, 1u8, 2u8, 3u8, 4u8, NL, 0u8, 1u8, 2u8, 3u8, 4u8, NL, 5u8, 6u8, 7u8, 8u8,
|
||||||
];
|
];
|
||||||
|
@ -122,6 +110,7 @@ fn block_test_nl_gt_cbs_trunc() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
vec![
|
vec![
|
||||||
|
// Commented lines should be truncated and appear for reference only.
|
||||||
vec![0u8, 1u8, 2u8, 3u8],
|
vec![0u8, 1u8, 2u8, 3u8],
|
||||||
// vec![4u8, SPACE, SPACE, SPACE],
|
// vec![4u8, SPACE, SPACE, SPACE],
|
||||||
vec![0u8, 1u8, 2u8, 3u8],
|
vec![0u8, 1u8, 2u8, 3u8],
|
||||||
|
@ -134,7 +123,7 @@ fn block_test_nl_gt_cbs_trunc() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_surrounded_nl() {
|
fn block_test_surrounded_nl() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, 8u8];
|
||||||
let res = block(buf, 8, &mut rs);
|
let res = block(buf, 8, &mut rs);
|
||||||
|
|
||||||
|
@ -149,7 +138,7 @@ fn block_test_surrounded_nl() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_multiple_nl_same_cbs_block() {
|
fn block_test_multiple_nl_same_cbs_block() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, NL, 5u8, 6u8, 7u8, 8u8, 9u8];
|
||||||
let res = block(buf, 8, &mut rs);
|
let res = block(buf, 8, &mut rs);
|
||||||
|
|
||||||
|
@ -165,7 +154,7 @@ fn block_test_multiple_nl_same_cbs_block() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_multiple_nl_diff_cbs_block() {
|
fn block_test_multiple_nl_diff_cbs_block() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, 4u8, 5u8, 6u8, 7u8, NL, 8u8, 9u8];
|
||||||
let res = block(buf, 8, &mut rs);
|
let res = block(buf, 8, &mut rs);
|
||||||
|
|
||||||
|
@ -181,7 +170,7 @@ fn block_test_multiple_nl_diff_cbs_block() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_end_nl_diff_cbs_block() {
|
fn block_test_end_nl_diff_cbs_block() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8, NL];
|
let buf = vec![0u8, 1u8, 2u8, 3u8, NL];
|
||||||
let res = block(buf, 4, &mut rs);
|
let res = block(buf, 4, &mut rs);
|
||||||
|
|
||||||
|
@ -190,7 +179,7 @@ fn block_test_end_nl_diff_cbs_block() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_end_nl_same_cbs_block() {
|
fn block_test_end_nl_same_cbs_block() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, NL];
|
let buf = vec![0u8, 1u8, 2u8, NL];
|
||||||
let res = block(buf, 4, &mut rs);
|
let res = block(buf, 4, &mut rs);
|
||||||
|
|
||||||
|
@ -199,7 +188,7 @@ fn block_test_end_nl_same_cbs_block() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_double_end_nl() {
|
fn block_test_double_end_nl() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, NL, NL];
|
let buf = vec![0u8, 1u8, 2u8, NL, NL];
|
||||||
let res = block(buf, 4, &mut rs);
|
let res = block(buf, 4, &mut rs);
|
||||||
|
|
||||||
|
@ -211,7 +200,7 @@ fn block_test_double_end_nl() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_start_nl() {
|
fn block_test_start_nl() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![NL, 0u8, 1u8, 2u8, 3u8];
|
let buf = vec![NL, 0u8, 1u8, 2u8, 3u8];
|
||||||
let res = block(buf, 4, &mut rs);
|
let res = block(buf, 4, &mut rs);
|
||||||
|
|
||||||
|
@ -223,7 +212,7 @@ fn block_test_start_nl() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_double_surrounded_nl_no_trunc() {
|
fn block_test_double_surrounded_nl_no_trunc() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8];
|
||||||
let res = block(buf, 8, &mut rs);
|
let res = block(buf, 8, &mut rs);
|
||||||
|
|
||||||
|
@ -239,13 +228,14 @@ fn block_test_double_surrounded_nl_no_trunc() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test_double_surrounded_nl_double_trunc() {
|
fn block_test_double_surrounded_nl_double_trunc() {
|
||||||
let mut rs = rs!();
|
let mut rs = ReadStat::default();
|
||||||
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8];
|
let buf = vec![0u8, 1u8, 2u8, 3u8, NL, NL, 4u8, 5u8, 6u8, 7u8, 8u8];
|
||||||
let res = block(buf, 4, &mut rs);
|
let res = block(buf, 4, &mut rs);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
vec![
|
vec![
|
||||||
|
// Commented section should be truncated and appear for reference only.
|
||||||
vec![0u8, 1u8, 2u8, 3u8],
|
vec![0u8, 1u8, 2u8, 3u8],
|
||||||
vec![SPACE, SPACE, SPACE, SPACE],
|
vec![SPACE, SPACE, SPACE, SPACE],
|
||||||
vec![4u8, 5u8, 6u8, 7u8 /*, 8u8*/],
|
vec![4u8, 5u8, 6u8, 7u8 /*, 8u8*/],
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
macro_rules! make_sync_test (
|
macro_rules! make_sync_test (
|
||||||
|
@ -11,7 +9,7 @@ macro_rules! make_sync_test (
|
||||||
src: $src,
|
src: $src,
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: $ibs,
|
ibs: $ibs,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: IConvFlags {
|
cflags: IConvFlags {
|
||||||
ctable: None,
|
ctable: None,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
macro_rules! make_conv_test (
|
macro_rules! make_conv_test (
|
||||||
|
@ -11,7 +9,7 @@ macro_rules! make_conv_test (
|
||||||
src: $src,
|
src: $src,
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 512,
|
ibs: 512,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: icf!($ctable),
|
cflags: icf!($ctable),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -36,7 +34,7 @@ macro_rules! make_icf_test (
|
||||||
src: $src,
|
src: $src,
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 512,
|
ibs: 512,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: $icf,
|
cflags: $icf,
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -141,7 +139,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 128,
|
ibs: 128,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: icf!(Some(&ASCII_TO_EBCDIC)),
|
cflags: icf!(Some(&ASCII_TO_EBCDIC)),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -163,7 +161,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
|
||||||
src: File::open(&tmp_fname_ae).unwrap(),
|
src: File::open(&tmp_fname_ae).unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 256,
|
ibs: 256,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: icf!(Some(&EBCDIC_TO_ASCII)),
|
cflags: icf!(Some(&EBCDIC_TO_ASCII)),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
mod block_unblock_tests;
|
mod block_unblock_tests;
|
||||||
|
@ -39,24 +37,6 @@ const DEFAULT_IFLAGS: IFlags = IFlags {
|
||||||
skip_bytes: false,
|
skip_bytes: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// const DEFAULT_OFLAGS: OFlags = OFlags {
|
|
||||||
// append: false,
|
|
||||||
// cio: false,
|
|
||||||
// direct: false,
|
|
||||||
// directory: false,
|
|
||||||
// dsync: false,
|
|
||||||
// sync: false,
|
|
||||||
// nocache: false,
|
|
||||||
// nonblock: false,
|
|
||||||
// noatime: false,
|
|
||||||
// noctty: false,
|
|
||||||
// nofollow: false,
|
|
||||||
// nolinks: false,
|
|
||||||
// binary: false,
|
|
||||||
// text: false,
|
|
||||||
// seek_bytes: false,
|
|
||||||
// };
|
|
||||||
|
|
||||||
struct LazyReader<R: Read> {
|
struct LazyReader<R: Read> {
|
||||||
src: R,
|
src: R,
|
||||||
}
|
}
|
||||||
|
@ -102,7 +82,7 @@ macro_rules! make_spec_test (
|
||||||
src: $src,
|
src: $src,
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 512,
|
ibs: 512,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const DST_PLACEHOLDER: Vec<u8> = Vec::new();
|
const DST_PLACEHOLDER: Vec<u8> = Vec::new();
|
||||||
|
@ -52,7 +50,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 521,
|
ibs: 521,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -72,7 +70,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 1031,
|
ibs: 1031,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -92,7 +90,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 1024,
|
ibs: 1024,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: Some(CountType::Reads(32)),
|
count: Some(CountType::Reads(32)),
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -112,7 +110,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 531,
|
ibs: 531,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: Some(CountType::Bytes(32 * 1024)),
|
count: Some(CountType::Bytes(32 * 1024)),
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -132,7 +130,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 1024,
|
ibs: 1024,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: Some(CountType::Reads(16)),
|
count: Some(CountType::Reads(16)),
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -152,7 +150,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 531,
|
ibs: 531,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: Some(CountType::Bytes(12345)),
|
count: Some(CountType::Bytes(12345)),
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -172,7 +170,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 1024,
|
ibs: 1024,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: Some(CountType::Reads(32)),
|
count: Some(CountType::Reads(32)),
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -192,7 +190,7 @@ make_io_test!(
|
||||||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 521,
|
ibs: 521,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: Some(CountType::Bytes(32 * 1024)),
|
count: Some(CountType::Bytes(32 * 1024)),
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: DEFAULT_IFLAGS,
|
iflags: DEFAULT_IFLAGS,
|
||||||
|
@ -215,7 +213,7 @@ make_io_test!(
|
||||||
},
|
},
|
||||||
non_ascii: false,
|
non_ascii: false,
|
||||||
ibs: 521,
|
ibs: 521,
|
||||||
xfer_stats: None,
|
print_level: None,
|
||||||
count: None,
|
count: None,
|
||||||
cflags: icf!(),
|
cflags: icf!(),
|
||||||
iflags: IFlags {
|
iflags: IFlags {
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests;
|
mod unit_tests;
|
||||||
|
|
||||||
|
@ -24,9 +22,8 @@ pub enum ParseError {
|
||||||
MultipleExclNoCreat,
|
MultipleExclNoCreat,
|
||||||
FlagNoMatch(String),
|
FlagNoMatch(String),
|
||||||
ConvFlagNoMatch(String),
|
ConvFlagNoMatch(String),
|
||||||
NoMatchingMultiplier(String),
|
MultiplierStringParseFailure(String),
|
||||||
ByteStringContainsNoValue(String),
|
MultiplierStringOverflow(String),
|
||||||
MultiplierStringWouldOverflow(String),
|
|
||||||
BlockUnblockWithoutCBS,
|
BlockUnblockWithoutCBS,
|
||||||
StatusLevelNotRecognized(String),
|
StatusLevelNotRecognized(String),
|
||||||
Unimplemented(String),
|
Unimplemented(String),
|
||||||
|
@ -56,13 +53,10 @@ impl std::fmt::Display for ParseError {
|
||||||
Self::ConvFlagNoMatch(arg) => {
|
Self::ConvFlagNoMatch(arg) => {
|
||||||
write!(f, "Unrecognized conv=CONV -> {}", arg)
|
write!(f, "Unrecognized conv=CONV -> {}", arg)
|
||||||
}
|
}
|
||||||
Self::NoMatchingMultiplier(arg) => {
|
Self::MultiplierStringParseFailure(arg) => {
|
||||||
write!(f, "Unrecognized byte multiplier -> {}", arg)
|
write!(f, "Unrecognized byte multiplier -> {}", arg)
|
||||||
}
|
}
|
||||||
Self::ByteStringContainsNoValue(arg) => {
|
Self::MultiplierStringOverflow(arg) => {
|
||||||
write!(f, "Unrecognized byte value -> {}", arg)
|
|
||||||
}
|
|
||||||
Self::MultiplierStringWouldOverflow(arg) => {
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Multiplier string would overflow on current system -> {}",
|
"Multiplier string would overflow on current system -> {}",
|
||||||
|
@ -302,61 +296,40 @@ impl std::str::FromStr for StatusLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_multiplier(s: &'_ str) -> Result<usize, ParseError> {
|
/// Parse bytes using str::parse, then map error if needed.
|
||||||
let mult: u128 = match s {
|
|
||||||
"c" => 1,
|
|
||||||
"w" => 2,
|
|
||||||
"b" => 512,
|
|
||||||
"kB" => 1000,
|
|
||||||
"K" | "KiB" => 1024,
|
|
||||||
"MB" => 1000 * 1000,
|
|
||||||
"M" | "MiB" => 1024 * 1024,
|
|
||||||
"GB" => 1000 * 1000 * 1000,
|
|
||||||
"G" | "GiB" => 1024 * 1024 * 1024,
|
|
||||||
"TB" => 1000 * 1000 * 1000 * 1000,
|
|
||||||
"T" | "TiB" => 1024 * 1024 * 1024 * 1024,
|
|
||||||
"PB" => 1000 * 1000 * 1000 * 1000 * 1000,
|
|
||||||
"P" | "PiB" => 1024 * 1024 * 1024 * 1024 * 1024,
|
|
||||||
"EB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
|
||||||
"E" | "EiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
|
||||||
"ZB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
|
||||||
"Z" | "ZiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
|
||||||
"YB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
|
|
||||||
"Y" | "YiB" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
|
||||||
_ => return Err(ParseError::NoMatchingMultiplier(s.to_string())),
|
|
||||||
};
|
|
||||||
|
|
||||||
mult.try_into()
|
|
||||||
.map_err(|_e| ParseError::MultiplierStringWouldOverflow(s.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_bytes_only(s: &str) -> Result<usize, ParseError> {
|
fn parse_bytes_only(s: &str) -> Result<usize, ParseError> {
|
||||||
match s.parse() {
|
s.parse()
|
||||||
Ok(bytes) => Ok(bytes),
|
.map_err(|_| ParseError::MultiplierStringParseFailure(s.to_string()))
|
||||||
Err(_) => Err(ParseError::ByteStringContainsNoValue(s.to_string())),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse byte and multiplier like 512, 5KiB, or 1G.
|
||||||
|
/// Uses uucore::parse_size, and adds the 'w' and 'c' suffixes which are mentioned
|
||||||
|
/// in dd's info page.
|
||||||
fn parse_bytes_with_opt_multiplier(s: &str) -> Result<usize, ParseError> {
|
fn parse_bytes_with_opt_multiplier(s: &str) -> Result<usize, ParseError> {
|
||||||
match s.find(char::is_alphabetic) {
|
if let Some(idx) = s.rfind('c') {
|
||||||
Some(idx) => {
|
parse_bytes_only(&s[..idx])
|
||||||
let base = parse_bytes_only(&s[..idx])?;
|
} else if let Some(idx) = s.rfind('w') {
|
||||||
let mult = parse_multiplier(&s[idx..])?;
|
let partial = parse_bytes_only(&s[..idx])?;
|
||||||
|
|
||||||
if let Some(bytes) = base.checked_mul(mult) {
|
partial
|
||||||
Ok(bytes)
|
.checked_mul(2)
|
||||||
} else {
|
.ok_or_else(|| ParseError::MultiplierStringOverflow(s.to_string()))
|
||||||
Err(ParseError::MultiplierStringWouldOverflow(s.to_string()))
|
} else {
|
||||||
|
uucore::parse_size::parse_size(s).map_err(|e| match e {
|
||||||
|
uucore::parse_size::ParseSizeError::ParseFailure(s) => {
|
||||||
|
ParseError::MultiplierStringParseFailure(s)
|
||||||
}
|
}
|
||||||
}
|
uucore::parse_size::ParseSizeError::SizeTooBig(s) => {
|
||||||
_ => parse_bytes_only(s),
|
ParseError::MultiplierStringOverflow(s)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_ibs(matches: &Matches) -> Result<usize, ParseError> {
|
pub fn parse_ibs(matches: &Matches) -> Result<usize, ParseError> {
|
||||||
if let Some(mixed_str) = matches.value_of("bs") {
|
if let Some(mixed_str) = matches.value_of(options::BS) {
|
||||||
parse_bytes_with_opt_multiplier(mixed_str)
|
parse_bytes_with_opt_multiplier(mixed_str)
|
||||||
} else if let Some(mixed_str) = matches.value_of("ibs") {
|
} else if let Some(mixed_str) = matches.value_of(options::IBS) {
|
||||||
parse_bytes_with_opt_multiplier(mixed_str)
|
parse_bytes_with_opt_multiplier(mixed_str)
|
||||||
} else {
|
} else {
|
||||||
Ok(512)
|
Ok(512)
|
||||||
|
@ -364,7 +337,7 @@ pub fn parse_ibs(matches: &Matches) -> Result<usize, ParseError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cbs(matches: &Matches) -> Result<Option<usize>, ParseError> {
|
fn parse_cbs(matches: &Matches) -> Result<Option<usize>, ParseError> {
|
||||||
if let Some(s) = matches.value_of("cbs") {
|
if let Some(s) = matches.value_of(options::CBS) {
|
||||||
let bytes = parse_bytes_with_opt_multiplier(s)?;
|
let bytes = parse_bytes_with_opt_multiplier(s)?;
|
||||||
Ok(Some(bytes))
|
Ok(Some(bytes))
|
||||||
} else {
|
} else {
|
||||||
|
@ -373,7 +346,7 @@ fn parse_cbs(matches: &Matches) -> Result<Option<usize>, ParseError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_status_level(matches: &Matches) -> Result<Option<StatusLevel>, ParseError> {
|
pub fn parse_status_level(matches: &Matches) -> Result<Option<StatusLevel>, ParseError> {
|
||||||
match matches.value_of("status") {
|
match matches.value_of(options::STATUS) {
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
let st = s.parse()?;
|
let st = s.parse()?;
|
||||||
Ok(Some(st))
|
Ok(Some(st))
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::StatusLevel;
|
use crate::StatusLevel;
|
||||||
|
@ -7,7 +5,7 @@ use crate::StatusLevel;
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn unimplemented_flags_should_error_non_unix() {
|
fn unimplemented_flags_should_error_non_unix() {
|
||||||
let mut unfailed = Vec::new();
|
let mut succeeded = Vec::new();
|
||||||
|
|
||||||
// The following flags are only implemented in linux
|
// The following flags are only implemented in linux
|
||||||
for flag in vec![
|
for flag in vec![
|
||||||
|
@ -28,26 +26,26 @@ fn unimplemented_flags_should_error_non_unix() {
|
||||||
let matches = uu_app().get_matches_from_safe(args).unwrap();
|
let matches = uu_app().get_matches_from_safe(args).unwrap();
|
||||||
|
|
||||||
match parse_iflags(&matches) {
|
match parse_iflags(&matches) {
|
||||||
Ok(_) => unfailed.push(format!("iflag={}", flag)),
|
Ok(_) => succeeded.push(format!("iflag={}", flag)),
|
||||||
Err(_) => { /* expected behaviour :-) */ }
|
Err(_) => { /* expected behaviour :-) */ }
|
||||||
}
|
}
|
||||||
match parse_oflags(&matches) {
|
match parse_oflags(&matches) {
|
||||||
Ok(_) => unfailed.push(format!("oflag={}", flag)),
|
Ok(_) => succeeded.push(format!("oflag={}", flag)),
|
||||||
Err(_) => { /* expected behaviour :-) */ }
|
Err(_) => { /* expected behaviour :-) */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !unfailed.is_empty() {
|
if !succeeded.is_empty() {
|
||||||
panic!(
|
panic!(
|
||||||
"The following flags did not panic as expected: {:?}",
|
"The following flags did not panic as expected: {:?}",
|
||||||
unfailed
|
succeeded
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unimplemented_flags_should_error() {
|
fn unimplemented_flags_should_error() {
|
||||||
let mut unfailed = Vec::new();
|
let mut succeeded = Vec::new();
|
||||||
|
|
||||||
// The following flags are not implemented
|
// The following flags are not implemented
|
||||||
for flag in vec!["cio", "nocache", "nolinks", "text", "binary"] {
|
for flag in vec!["cio", "nocache", "nolinks", "text", "binary"] {
|
||||||
|
@ -59,19 +57,19 @@ fn unimplemented_flags_should_error() {
|
||||||
let matches = uu_app().get_matches_from_safe(args).unwrap();
|
let matches = uu_app().get_matches_from_safe(args).unwrap();
|
||||||
|
|
||||||
match parse_iflags(&matches) {
|
match parse_iflags(&matches) {
|
||||||
Ok(_) => unfailed.push(format!("iflag={}", flag)),
|
Ok(_) => succeeded.push(format!("iflag={}", flag)),
|
||||||
Err(_) => { /* expected behaviour :-) */ }
|
Err(_) => { /* expected behaviour :-) */ }
|
||||||
}
|
}
|
||||||
match parse_oflags(&matches) {
|
match parse_oflags(&matches) {
|
||||||
Ok(_) => unfailed.push(format!("oflag={}", flag)),
|
Ok(_) => succeeded.push(format!("oflag={}", flag)),
|
||||||
Err(_) => { /* expected behaviour :-) */ }
|
Err(_) => { /* expected behaviour :-) */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !unfailed.is_empty() {
|
if !succeeded.is_empty() {
|
||||||
panic!(
|
panic!(
|
||||||
"The following flags did not panic as expected: {:?}",
|
"The following flags did not panic as expected: {:?}",
|
||||||
unfailed
|
succeeded
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,6 +103,176 @@ fn test_status_level_none() {
|
||||||
assert_eq!(st, StatusLevel::None);
|
assert_eq!(st, StatusLevel::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_all_top_level_args_no_leading_dashes_sep_by_equals() {
|
||||||
|
let args = vec![
|
||||||
|
String::from("dd"),
|
||||||
|
String::from("if=foo.file"),
|
||||||
|
String::from("of=bar.file"),
|
||||||
|
String::from("ibs=10"),
|
||||||
|
String::from("obs=10"),
|
||||||
|
String::from("cbs=1"),
|
||||||
|
String::from("bs=100"),
|
||||||
|
String::from("count=2"),
|
||||||
|
String::from("skip=2"),
|
||||||
|
String::from("seek=2"),
|
||||||
|
String::from("status=progress"),
|
||||||
|
String::from("conv=ascii,ucase"),
|
||||||
|
String::from("iflag=count_bytes,skip_bytes"),
|
||||||
|
String::from("oflag=append,seek_bytes"),
|
||||||
|
];
|
||||||
|
let args = args
|
||||||
|
.into_iter()
|
||||||
|
.fold(Vec::new(), append_dashes_if_not_present);
|
||||||
|
|
||||||
|
let matches = uu_app().get_matches_from_safe(args).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(100, parse_ibs(&matches).unwrap());
|
||||||
|
assert_eq!(100, parse_obs(&matches).unwrap());
|
||||||
|
assert_eq!(1, parse_cbs(&matches).unwrap().unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
CountType::Bytes(2),
|
||||||
|
parse_count(
|
||||||
|
&IFlags {
|
||||||
|
count_bytes: true,
|
||||||
|
..IFlags::default()
|
||||||
|
},
|
||||||
|
&matches
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
200,
|
||||||
|
parse_skip_amt(&100, &IFlags::default(), &matches)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
200,
|
||||||
|
parse_seek_amt(&100, &OFlags::default(), &matches)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
StatusLevel::Progress,
|
||||||
|
parse_status_level(&matches).unwrap().unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IConvFlags {
|
||||||
|
ctable: Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE),
|
||||||
|
..IConvFlags::default()
|
||||||
|
},
|
||||||
|
parse_conv_flag_input(&matches).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
OConvFlags::default(),
|
||||||
|
parse_conv_flag_output(&matches).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFlags {
|
||||||
|
count_bytes: true,
|
||||||
|
skip_bytes: true,
|
||||||
|
..IFlags::default()
|
||||||
|
},
|
||||||
|
parse_iflags(&matches).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
OFlags {
|
||||||
|
append: true,
|
||||||
|
seek_bytes: true,
|
||||||
|
..OFlags::default()
|
||||||
|
},
|
||||||
|
parse_oflags(&matches).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
|
#[test]
|
||||||
|
// TODO: This should work, but Clap doesn't seem to understand it. Leaving it for now since the traditional dd if=foo.file works just fine.
|
||||||
|
fn test_all_top_level_args_leading_dashes_sep_by_spaces() {
|
||||||
|
let args = vec![
|
||||||
|
String::from("dd"),
|
||||||
|
String::from("--if foo.file"),
|
||||||
|
String::from("--of bar.file"),
|
||||||
|
String::from("--ibs 10"),
|
||||||
|
String::from("--obs 10"),
|
||||||
|
String::from("--cbs 1"),
|
||||||
|
String::from("--bs 100"),
|
||||||
|
String::from("--count 2"),
|
||||||
|
String::from("--skip 2"),
|
||||||
|
String::from("--seek 2"),
|
||||||
|
String::from("--status progress"),
|
||||||
|
String::from("--conv ascii,ucase"),
|
||||||
|
String::from("--iflag count_bytes,skip_bytes"),
|
||||||
|
String::from("--oflag append,seek_bytes"),
|
||||||
|
];
|
||||||
|
let args = args
|
||||||
|
.into_iter()
|
||||||
|
.fold(Vec::new(), append_dashes_if_not_present);
|
||||||
|
|
||||||
|
let matches = uu_app().get_matches_from_safe(args).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(100, parse_ibs(&matches).unwrap());
|
||||||
|
assert_eq!(100, parse_obs(&matches).unwrap());
|
||||||
|
assert_eq!(1, parse_cbs(&matches).unwrap().unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
CountType::Bytes(2),
|
||||||
|
parse_count(
|
||||||
|
&IFlags {
|
||||||
|
count_bytes: true,
|
||||||
|
..IFlags::default()
|
||||||
|
},
|
||||||
|
&matches
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
200,
|
||||||
|
parse_skip_amt(&100, &IFlags::default(), &matches)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
200,
|
||||||
|
parse_seek_amt(&100, &OFlags::default(), &matches)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
StatusLevel::Progress,
|
||||||
|
parse_status_level(&matches).unwrap().unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IConvFlags {
|
||||||
|
ctable: Some(&EBCDIC_TO_ASCII_LCASE_TO_UCASE),
|
||||||
|
..IConvFlags::default()
|
||||||
|
},
|
||||||
|
parse_conv_flag_input(&matches).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
OConvFlags::default(),
|
||||||
|
parse_conv_flag_output(&matches).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFlags {
|
||||||
|
count_bytes: true,
|
||||||
|
skip_bytes: true,
|
||||||
|
..IFlags::default()
|
||||||
|
},
|
||||||
|
parse_iflags(&matches).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
OFlags {
|
||||||
|
append: true,
|
||||||
|
seek_bytes: true,
|
||||||
|
..OFlags::default()
|
||||||
|
},
|
||||||
|
parse_oflags(&matches).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_status_level_progress() {
|
fn test_status_level_progress() {
|
||||||
let args = vec![
|
let args = vec![
|
||||||
|
@ -374,16 +542,6 @@ test_byte_parser!(
|
||||||
6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
|
6 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn test_KB_multiplier_error() {
|
|
||||||
// KB is not valid (kB, K, and KiB are)
|
|
||||||
let bs_str = "2000KB";
|
|
||||||
|
|
||||||
parse_bytes_with_opt_multiplier(bs_str).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_overflow_panic() {
|
fn test_overflow_panic() {
|
||||||
|
@ -395,7 +553,7 @@ fn test_overflow_panic() {
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_neg_panic() {
|
fn test_neg_panic() {
|
||||||
let bs_str = format!("{}KiB", -1);
|
let bs_str = format!("{}", -1);
|
||||||
|
|
||||||
parse_bytes_with_opt_multiplier(&bs_str).unwrap();
|
parse_bytes_with_opt_multiplier(&bs_str).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* cspell:disable */
|
|
||||||
|
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue