mirror of
https://github.com/uutils/coreutils
synced 2024-10-06 16:09:08 +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
|
@ -153,7 +153,6 @@ feat_require_unix = [
|
|||
"chmod",
|
||||
"chown",
|
||||
"chroot",
|
||||
"dd",
|
||||
"groups",
|
||||
"hostid",
|
||||
"id",
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
/* cspell:disable */
|
||||
|
||||
// Note: Conversion tables are just lookup tables.
|
||||
// eg. The ASCII->EBCDIC table stores the EBCDIC code at the index
|
||||
// obtained by treating the ASCII representation as a number.
|
||||
|
|
|
@ -5,23 +5,18 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
/* cspell:disable */
|
||||
|
||||
use crate::conversion_tables::*;
|
||||
|
||||
use std::error::Error;
|
||||
use std::time;
|
||||
|
||||
pub struct ProgUpdate {
|
||||
pub reads_complete: u64,
|
||||
pub reads_partial: u64,
|
||||
pub writes_complete: u64,
|
||||
pub writes_partial: u64,
|
||||
pub bytes_total: u128,
|
||||
pub records_truncated: u32,
|
||||
pub read_stat: ReadStat,
|
||||
pub write_stat: WriteStat,
|
||||
pub duration: time::Duration,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct ReadStat {
|
||||
pub reads_complete: u64,
|
||||
pub reads_partial: u64,
|
||||
|
@ -37,6 +32,7 @@ impl std::ops::AddAssign for ReadStat {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct WriteStat {
|
||||
pub writes_complete: u64,
|
||||
pub writes_partial: u64,
|
||||
|
@ -55,6 +51,7 @@ impl std::ops::AddAssign for WriteStat {
|
|||
type Cbs = usize;
|
||||
|
||||
/// Stores all Conv Flags that apply to the input
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct IConvFlags {
|
||||
pub ctable: Option<&'static ConversionTable>,
|
||||
pub block: Option<Cbs>,
|
||||
|
@ -65,7 +62,7 @@ pub struct IConvFlags {
|
|||
}
|
||||
|
||||
/// Stores all Conv Flags that apply to the output
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct OConvFlags {
|
||||
pub sparse: bool,
|
||||
pub excl: bool,
|
||||
|
@ -76,32 +73,20 @@ pub struct OConvFlags {
|
|||
}
|
||||
|
||||
/// Stores all Flags that apply to the input
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct IFlags {
|
||||
#[allow(dead_code)]
|
||||
pub cio: bool,
|
||||
#[allow(dead_code)]
|
||||
pub direct: bool,
|
||||
#[allow(dead_code)]
|
||||
pub directory: bool,
|
||||
#[allow(dead_code)]
|
||||
pub dsync: bool,
|
||||
#[allow(dead_code)]
|
||||
pub sync: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nocache: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nonblock: bool,
|
||||
#[allow(dead_code)]
|
||||
pub noatime: bool,
|
||||
#[allow(dead_code)]
|
||||
pub noctty: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nofollow: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nolinks: bool,
|
||||
#[allow(dead_code)]
|
||||
pub binary: bool,
|
||||
#[allow(dead_code)]
|
||||
pub text: bool,
|
||||
pub fullblock: bool,
|
||||
pub count_bytes: bool,
|
||||
|
@ -109,33 +94,21 @@ pub struct IFlags {
|
|||
}
|
||||
|
||||
/// Stores all Flags that apply to the output
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct OFlags {
|
||||
pub append: bool,
|
||||
#[allow(dead_code)]
|
||||
pub cio: bool,
|
||||
#[allow(dead_code)]
|
||||
pub direct: bool,
|
||||
#[allow(dead_code)]
|
||||
pub directory: bool,
|
||||
#[allow(dead_code)]
|
||||
pub dsync: bool,
|
||||
#[allow(dead_code)]
|
||||
pub sync: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nocache: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nonblock: bool,
|
||||
#[allow(dead_code)]
|
||||
pub noatime: bool,
|
||||
#[allow(dead_code)]
|
||||
pub noctty: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nofollow: bool,
|
||||
#[allow(dead_code)]
|
||||
pub nolinks: bool,
|
||||
#[allow(dead_code)]
|
||||
pub binary: bool,
|
||||
#[allow(dead_code)]
|
||||
pub text: bool,
|
||||
pub seek_bytes: bool,
|
||||
}
|
||||
|
@ -153,6 +126,7 @@ pub enum StatusLevel {
|
|||
/// Defaults to Reads(N)
|
||||
/// if iflag=count_bytes
|
||||
/// then becomes Bytes(N)
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum CountType {
|
||||
Reads(usize),
|
||||
Bytes(usize),
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
/* cspell:disable */
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
@ -25,7 +23,6 @@ use conversion_tables::*;
|
|||
|
||||
use byte_unit::Byte;
|
||||
use clap::{self, crate_version};
|
||||
use debug_print::debug_println;
|
||||
use gcd::Gcd;
|
||||
use signal_hook::consts::signal;
|
||||
use std::cmp;
|
||||
|
@ -51,7 +48,7 @@ struct Input<R: Read> {
|
|||
src: R,
|
||||
non_ascii: bool,
|
||||
ibs: usize,
|
||||
xfer_stats: Option<StatusLevel>,
|
||||
print_level: Option<StatusLevel>,
|
||||
count: Option<CountType>,
|
||||
cflags: IConvFlags,
|
||||
iflags: IFlags,
|
||||
|
@ -61,7 +58,7 @@ impl Input<io::Stdin> {
|
|||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||
let ibs = parseargs::parse_ibs(matches)?;
|
||||
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
||||
let xfer_stats = parseargs::parse_status_level(matches)?;
|
||||
let print_level = parseargs::parse_status_level(matches)?;
|
||||
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
||||
let iflags = parseargs::parse_iflags(matches)?;
|
||||
let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?;
|
||||
|
@ -71,7 +68,7 @@ impl Input<io::Stdin> {
|
|||
src: io::stdin(),
|
||||
non_ascii,
|
||||
ibs,
|
||||
xfer_stats,
|
||||
print_level,
|
||||
count,
|
||||
cflags,
|
||||
iflags,
|
||||
|
@ -88,31 +85,31 @@ impl Input<io::Stdin> {
|
|||
}
|
||||
|
||||
#[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;
|
||||
|
||||
if oflags.direct {
|
||||
if iflags.direct {
|
||||
flag |= libc::O_DIRECT;
|
||||
}
|
||||
if oflags.directory {
|
||||
if iflags.directory {
|
||||
flag |= libc::O_DIRECTORY;
|
||||
}
|
||||
if oflags.dsync {
|
||||
if iflags.dsync {
|
||||
flag |= libc::O_DSYNC;
|
||||
}
|
||||
if oflags.noatime {
|
||||
if iflags.noatime {
|
||||
flag |= libc::O_NOATIME;
|
||||
}
|
||||
if oflags.noctty {
|
||||
if iflags.noctty {
|
||||
flag |= libc::O_NOCTTY;
|
||||
}
|
||||
if oflags.nofollow {
|
||||
if iflags.nofollow {
|
||||
flag |= libc::O_NOFOLLOW;
|
||||
}
|
||||
if oflags.nonblock {
|
||||
if iflags.nonblock {
|
||||
flag |= libc::O_NONBLOCK;
|
||||
}
|
||||
if oflags.sync {
|
||||
if iflags.sync {
|
||||
flag |= libc::O_SYNC;
|
||||
}
|
||||
|
||||
|
@ -127,13 +124,13 @@ impl Input<File> {
|
|||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||
let ibs = parseargs::parse_ibs(matches)?;
|
||||
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
|
||||
let xfer_stats = parseargs::parse_status_level(matches)?;
|
||||
let print_level = parseargs::parse_status_level(matches)?;
|
||||
let cflags = parseargs::parse_conv_flag_input(matches)?;
|
||||
let iflags = parseargs::parse_iflags(matches)?;
|
||||
let skip = parseargs::parse_skip_amt(&ibs, &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 opts = OpenOptions::new();
|
||||
opts.read(true);
|
||||
|
@ -155,7 +152,7 @@ impl Input<File> {
|
|||
src,
|
||||
non_ascii,
|
||||
ibs,
|
||||
xfer_stats,
|
||||
print_level,
|
||||
count,
|
||||
cflags,
|
||||
iflags,
|
||||
|
@ -198,28 +195,27 @@ impl<R: Read> Input<R> {
|
|||
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;
|
||||
let mut bytes_total = 0;
|
||||
|
||||
while base_idx < buf.len() {
|
||||
let next_blk = cmp::min(base_idx + self.ibs, buf.len());
|
||||
|
||||
match self.read(&mut buf[base_idx..next_blk])? {
|
||||
for chunk in buf.chunks_mut(self.ibs) {
|
||||
match self.read(chunk)? {
|
||||
rlen if rlen == self.ibs => {
|
||||
base_idx += rlen;
|
||||
bytes_total += rlen;
|
||||
reads_complete += 1;
|
||||
}
|
||||
rlen if rlen > 0 => {
|
||||
base_idx += rlen;
|
||||
bytes_total += rlen;
|
||||
reads_partial += 1;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
buf.truncate(base_idx);
|
||||
buf.truncate(bytes_total);
|
||||
Ok(ReadStat {
|
||||
reads_complete,
|
||||
reads_partial,
|
||||
// Records are not truncated when filling.
|
||||
records_truncated: 0,
|
||||
})
|
||||
}
|
||||
|
@ -234,35 +230,19 @@ impl<R: Read> Input<R> {
|
|||
|
||||
while base_idx < 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])? {
|
||||
0 => break,
|
||||
rlen if rlen < plen => {
|
||||
rlen if rlen < target_len => {
|
||||
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());
|
||||
}
|
||||
_ => {
|
||||
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;
|
||||
}
|
||||
|
@ -356,11 +336,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
|
|||
|
||||
impl Output<File> {
|
||||
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
|
||||
fn open_dst(
|
||||
path: &Path,
|
||||
cflags: &OConvFlags,
|
||||
oflags: &OFlags,
|
||||
) -> Result<File, Box<dyn Error>> {
|
||||
fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
|
||||
let mut opts = OpenOptions::new();
|
||||
opts.write(true)
|
||||
.create(!cflags.nocreat)
|
||||
|
@ -373,15 +349,14 @@ impl Output<File> {
|
|||
opts.custom_flags(libc_flags);
|
||||
}
|
||||
|
||||
let dst = opts.open(path)?;
|
||||
Ok(dst)
|
||||
opts.open(path)
|
||||
}
|
||||
let obs = parseargs::parse_obs(matches)?;
|
||||
let cflags = parseargs::parse_conv_flag_output(matches)?;
|
||||
let oflags = parseargs::parse_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)?;
|
||||
|
||||
if let Some(amt) = seek {
|
||||
|
@ -418,7 +393,6 @@ impl Seek for Output<File> {
|
|||
|
||||
impl Write for Output<File> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
#[inline]
|
||||
fn is_sparse(buf: &[u8]) -> bool {
|
||||
buf.iter().all(|&e| e == 0u8)
|
||||
}
|
||||
|
@ -454,20 +428,17 @@ impl Output<io::Stdout> {
|
|||
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;
|
||||
let mut bytes_total = 0;
|
||||
|
||||
while base_idx < buf.len() {
|
||||
let next_blk = cmp::min(base_idx + self.obs, buf.len());
|
||||
let plen = next_blk - base_idx;
|
||||
|
||||
match self.write(&buf[base_idx..next_blk])? {
|
||||
wlen if wlen < plen => {
|
||||
for chunk in buf.chunks(self.obs) {
|
||||
match self.write(chunk)? {
|
||||
wlen if wlen < chunk.len() => {
|
||||
writes_partial += 1;
|
||||
base_idx += wlen;
|
||||
bytes_total += wlen;
|
||||
}
|
||||
wlen => {
|
||||
writes_complete += 1;
|
||||
base_idx += wlen;
|
||||
bytes_total += wlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -475,7 +446,7 @@ impl Output<io::Stdout> {
|
|||
Ok(WriteStat {
|
||||
writes_complete,
|
||||
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> {
|
||||
let mut writes_complete = 0;
|
||||
let mut writes_partial = 0;
|
||||
let mut base_idx = 0;
|
||||
let mut bytes_total = 0;
|
||||
|
||||
while base_idx < buf.len() {
|
||||
let next_blk = cmp::min(base_idx + self.obs, buf.len());
|
||||
let plen = next_blk - base_idx;
|
||||
|
||||
match self.write(&buf[base_idx..next_blk])? {
|
||||
wlen if wlen < plen => {
|
||||
for chunk in buf.chunks(self.obs) {
|
||||
match self.write(chunk)? {
|
||||
wlen if wlen < chunk.len() => {
|
||||
writes_partial += 1;
|
||||
base_idx += wlen;
|
||||
bytes_total += wlen;
|
||||
}
|
||||
wlen => {
|
||||
writes_complete += 1;
|
||||
base_idx += wlen;
|
||||
bytes_total += wlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -505,7 +473,7 @@ impl Output<File> {
|
|||
Ok(WriteStat {
|
||||
writes_complete,
|
||||
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
|
||||
/// as specified by conv=unblock and cbs=N
|
||||
fn unblock(buf: Vec<u8>, cbs: usize) -> Vec<u8> {
|
||||
// Local Helper Fns ----------------------------------------------------
|
||||
#[inline]
|
||||
fn build_blocks(buf: Vec<u8>, cbs: usize) -> Vec<Vec<u8>> {
|
||||
let mut blocks = Vec::new();
|
||||
let mut curr = buf;
|
||||
let mut next;
|
||||
let mut width;
|
||||
buf.chunks(cbs).fold(Vec::new(), |mut acc, block| {
|
||||
if let Some(last_char_idx) = block.iter().rposition(|&e| e != b' ') {
|
||||
// Find last space
|
||||
acc.extend(&block[..=last_char_idx]);
|
||||
acc.push(b'\n');
|
||||
} else {
|
||||
// The block is filled with only spaces
|
||||
acc.push(b'\n');
|
||||
};
|
||||
|
||||
while !curr.is_empty() {
|
||||
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()
|
||||
acc
|
||||
})
|
||||
}
|
||||
|
||||
fn conv_block_unblock_helper<R: Read>(
|
||||
|
@ -587,19 +526,15 @@ fn conv_block_unblock_helper<R: Read>(
|
|||
rstat: &mut ReadStat,
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
// Local Predicate Fns -------------------------------------------------
|
||||
#[inline]
|
||||
fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool {
|
||||
!i.non_ascii && i.cflags.block.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn should_conv_then_block<R: Read>(i: &Input<R>) -> bool {
|
||||
i.non_ascii && i.cflags.block.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn should_unblock_then_conv<R: Read>(i: &Input<R>) -> bool {
|
||||
!i.non_ascii && i.cflags.unblock.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn should_conv_then_unblock<R: Read>(i: &Input<R>) -> bool {
|
||||
i.non_ascii && i.cflags.unblock.is_some()
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
// Local Helper Fns ----------------------------------------------------
|
||||
#[inline]
|
||||
fn apply_ct(buf: &mut [u8], ct: &ConversionTable) {
|
||||
fn apply_conversion(buf: &mut [u8], ct: &ConversionTable) {
|
||||
for idx in 0..buf.len() {
|
||||
buf[idx] = ct[buf[idx] as usize];
|
||||
}
|
||||
|
@ -617,7 +551,7 @@ fn conv_block_unblock_helper<R: Read>(
|
|||
if conv_only(i) {
|
||||
// no block/unblock
|
||||
let ct = i.cflags.ctable.unwrap();
|
||||
apply_ct(&mut buf, ct);
|
||||
apply_conversion(&mut buf, ct);
|
||||
|
||||
Ok(buf)
|
||||
} 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 {
|
||||
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();
|
||||
|
||||
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();
|
||||
|
@ -653,7 +587,7 @@ fn conv_block_unblock_helper<R: Read>(
|
|||
let mut buf = unblock(buf, cbs);
|
||||
|
||||
if let Some(ct) = i.cflags.ctable {
|
||||
apply_ct(&mut buf, ct);
|
||||
apply_conversion(&mut buf, ct);
|
||||
}
|
||||
|
||||
Ok(buf)
|
||||
|
@ -662,7 +596,7 @@ fn conv_block_unblock_helper<R: Read>(
|
|||
let cbs = i.cflags.unblock.unwrap();
|
||||
|
||||
if let Some(ct) = i.cflags.ctable {
|
||||
apply_ct(&mut buf, ct);
|
||||
apply_conversion(&mut buf, ct);
|
||||
}
|
||||
|
||||
let buf = unblock(buf, cbs);
|
||||
|
@ -683,27 +617,19 @@ fn read_helper<R: Read>(
|
|||
bsize: usize,
|
||||
) -> Result<(ReadStat, Vec<u8>), Box<dyn Error>> {
|
||||
// Local Predicate Fns -----------------------------------------------
|
||||
#[inline]
|
||||
fn is_conv<R: Read>(i: &Input<R>) -> bool {
|
||||
i.cflags.ctable.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn is_block<R: Read>(i: &Input<R>) -> bool {
|
||||
i.cflags.block.is_some()
|
||||
}
|
||||
#[inline]
|
||||
fn is_unblock<R: Read>(i: &Input<R>) -> bool {
|
||||
i.cflags.unblock.is_some()
|
||||
}
|
||||
// Local Helper Fns -------------------------------------------------
|
||||
#[inline]
|
||||
fn perform_swab(buf: &mut [u8]) {
|
||||
let mut tmp;
|
||||
|
||||
for base in (1..buf.len()).step_by(2) {
|
||||
tmp = buf[base];
|
||||
buf[base] = buf[base - 1];
|
||||
buf[base - 1] = tmp;
|
||||
buf.swap(base, base - 1);
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
|
@ -733,35 +659,35 @@ fn read_helper<R: Read>(
|
|||
fn print_io_lines(update: &ProgUpdate) {
|
||||
eprintln!(
|
||||
"{}+{} records in",
|
||||
update.reads_complete, update.reads_partial
|
||||
update.read_stat.reads_complete, update.read_stat.reads_partial
|
||||
);
|
||||
if update.records_truncated > 0 {
|
||||
eprintln!("{} truncated records", update.records_truncated);
|
||||
if update.read_stat.records_truncated > 0 {
|
||||
eprintln!("{} truncated records", update.read_stat.records_truncated);
|
||||
}
|
||||
eprintln!(
|
||||
"{}+{} 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 {
|
||||
let btotal_metric = Byte::from_bytes(update.bytes_total)
|
||||
let btotal_metric = Byte::from_bytes(update.write_stat.bytes_total)
|
||||
.get_appropriate_unit(false)
|
||||
.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)
|
||||
.format(0);
|
||||
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)
|
||||
.format(1);
|
||||
|
||||
format!(
|
||||
"{} bytes ({}, {}) copied, {:.1} s, {}/s",
|
||||
update.bytes_total,
|
||||
update.write_stat.bytes_total,
|
||||
btotal_metric,
|
||||
btotal_bin,
|
||||
update.duration.as_secs_f64(),
|
||||
xfer_rate
|
||||
transfer_rate
|
||||
)
|
||||
}
|
||||
fn reprint_prog_line(update: &ProgUpdate) {
|
||||
|
@ -770,65 +696,60 @@ fn reprint_prog_line(update: &ProgUpdate) {
|
|||
fn print_prog_line(update: &ProgUpdate) {
|
||||
eprintln!("{}", make_prog_line(update));
|
||||
}
|
||||
fn print_xfer_stats(update: &ProgUpdate) {
|
||||
fn print_transfer_stats(update: &ProgUpdate) {
|
||||
print_io_lines(update);
|
||||
print_prog_line(update);
|
||||
}
|
||||
|
||||
/// Generate a progress updater that tracks progress, receives updates, and responds to signals.
|
||||
fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, xfer_stats: Option<StatusLevel>) -> impl Fn() {
|
||||
/// Generate a progress updater that tracks progress, receives updates, and responds to progress update requests (signals).
|
||||
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 {
|
||||
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 || {
|
||||
const SIGUSR1_USIZE: usize = signal::SIGUSR1 as usize;
|
||||
|
||||
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 signals 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{}",
|
||||
register_signal_handlers(sigval.clone()).unwrap_or_else(|e| {
|
||||
if Some(StatusLevel::None) != print_level {
|
||||
eprintln!(
|
||||
"Internal dd Warning: Unable to register signal handler \n\t{}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
// Wait for update
|
||||
let update = match (rx.recv(), xfer_stats) {
|
||||
let update = match (rx.recv(), print_level) {
|
||||
(Ok(update), Some(StatusLevel::Progress)) => {
|
||||
reprint_prog_line(&update);
|
||||
|
||||
update
|
||||
}
|
||||
(Ok(update), _) => update,
|
||||
(Err(_), _) =>
|
||||
// recv only fails permanently
|
||||
{
|
||||
break
|
||||
(Err(_), _) => {
|
||||
// recv only fails permanently, so we break here to
|
||||
// avoid recv'ing on a broken pipe
|
||||
break;
|
||||
}
|
||||
};
|
||||
// Handle signals
|
||||
#[allow(clippy::single_match)]
|
||||
match sigval.load(Ordering::Relaxed) {
|
||||
SIGUSR1_USIZE => {
|
||||
print_xfer_stats(&update);
|
||||
}
|
||||
// SIGINFO_USIZE => ...
|
||||
_ => { /* no signals recv'd */ }
|
||||
#[cfg(target_os = "linux")]
|
||||
if let SIGUSR1_USIZE = sigval.load(Ordering::Relaxed) {
|
||||
print_transfer_stats(&update);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
/// the least common multiple is a good representation of these interests.
|
||||
/// https://en.wikipedia.org/wiki/Least_common_multiple#Using_the_greatest_common_divisor
|
||||
#[inline]
|
||||
fn calc_bsize(ibs: usize, obs: usize) -> usize {
|
||||
let gcd = Gcd::gcd(ibs, obs);
|
||||
// calculate the lcm from gcd
|
||||
|
@ -878,12 +798,10 @@ fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteS
|
|||
match count {
|
||||
Some(CountType::Reads(n)) => {
|
||||
let n = (*n).try_into().unwrap();
|
||||
// debug_assert!(rstat.reads_complete + rstat.reads_partial >= n);
|
||||
rstat.reads_complete + rstat.reads_partial <= n
|
||||
}
|
||||
Some(CountType::Bytes(n)) => {
|
||||
let n = (*n).try_into().unwrap();
|
||||
// debug_assert!(wstat.bytes_total >= n);
|
||||
wstat.bytes_total <= n
|
||||
}
|
||||
None => true,
|
||||
|
@ -891,8 +809,8 @@ fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteS
|
|||
}
|
||||
|
||||
/// 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,
|
||||
// and should be fixed in the future.
|
||||
/// 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 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>> {
|
||||
let mut rstat = ReadStat {
|
||||
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 (tx, rx) = mpsc::channel();
|
||||
thread::spawn(gen_prog_updater(rx, i.xfer_stats));
|
||||
thread::spawn(gen_prog_updater(rx, i.print_level));
|
||||
tx
|
||||
};
|
||||
|
||||
|
@ -934,12 +852,8 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
|
|||
};
|
||||
// Update Prog
|
||||
prog_tx.send(ProgUpdate {
|
||||
reads_complete: rstat.reads_complete,
|
||||
reads_partial: rstat.reads_partial,
|
||||
writes_complete: wstat.writes_complete,
|
||||
writes_partial: wstat.writes_partial,
|
||||
bytes_total: wstat.bytes_total,
|
||||
records_truncated: rstat.records_truncated,
|
||||
read_stat: rstat,
|
||||
write_stat: wstat,
|
||||
duration: start.elapsed(),
|
||||
})?;
|
||||
}
|
||||
|
@ -950,15 +864,11 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
|
|||
o.fdatasync()?;
|
||||
}
|
||||
|
||||
match i.xfer_stats {
|
||||
match i.print_level {
|
||||
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
||||
_ => print_xfer_stats(&ProgUpdate {
|
||||
reads_complete: rstat.reads_complete,
|
||||
reads_partial: rstat.reads_partial,
|
||||
writes_complete: wstat.writes_complete,
|
||||
writes_partial: wstat.writes_partial,
|
||||
bytes_total: wstat.bytes_total,
|
||||
records_truncated: rstat.records_truncated,
|
||||
_ => print_transfer_stats(&ProgUpdate {
|
||||
read_stat: rstat,
|
||||
write_stat: wstat,
|
||||
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
|
||||
// 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.
|
||||
/// 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 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>> {
|
||||
let mut rstat = ReadStat {
|
||||
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 (tx, rx) = mpsc::channel();
|
||||
thread::spawn(gen_prog_updater(rx, i.xfer_stats));
|
||||
thread::spawn(gen_prog_updater(rx, i.print_level));
|
||||
tx
|
||||
};
|
||||
|
||||
|
@ -1009,12 +919,8 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<d
|
|||
};
|
||||
// Update Prog
|
||||
prog_tx.send(ProgUpdate {
|
||||
reads_complete: rstat.reads_complete,
|
||||
reads_partial: rstat.reads_partial,
|
||||
writes_complete: wstat.writes_complete,
|
||||
writes_partial: wstat.writes_partial,
|
||||
bytes_total: wstat.bytes_total,
|
||||
records_truncated: rstat.records_truncated,
|
||||
read_stat: rstat,
|
||||
write_stat: wstat,
|
||||
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()?;
|
||||
}
|
||||
|
||||
match i.xfer_stats {
|
||||
match i.print_level {
|
||||
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
|
||||
_ => print_xfer_stats(&ProgUpdate {
|
||||
reads_complete: rstat.reads_complete,
|
||||
reads_partial: rstat.reads_partial,
|
||||
writes_complete: wstat.writes_complete,
|
||||
writes_partial: wstat.writes_partial,
|
||||
bytes_total: wstat.bytes_total,
|
||||
records_truncated: rstat.records_truncated,
|
||||
_ => print_transfer_stats(&ProgUpdate {
|
||||
read_stat: rstat,
|
||||
write_stat: wstat,
|
||||
duration: start.elapsed(),
|
||||
}),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// The compiler does not like Clippy's suggestion to use &str in place of &String here.
|
||||
#[allow(clippy::ptr_arg)]
|
||||
fn append_dashes_if_not_present(mut acc: Vec<String>, s: &String) -> Vec<String> {
|
||||
if Some("--") != s.get(0..=1) {
|
||||
acc.push(format!("--{}", s));
|
||||
fn append_dashes_if_not_present(mut acc: Vec<String>, mut s: String) -> Vec<String> {
|
||||
if !s.starts_with("--") && !s.starts_with("-") {
|
||||
s.insert_str(0, "--");
|
||||
}
|
||||
acc.push(s);
|
||||
acc
|
||||
}
|
||||
|
||||
|
@ -1074,13 +975,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let dashed_args = args
|
||||
.collect_str(InvalidEncodingHandling::Ignore)
|
||||
.accept_any()
|
||||
.iter()
|
||||
.into_iter()
|
||||
.fold(Vec::new(), append_dashes_if_not_present);
|
||||
|
||||
let matches = uu_app()
|
||||
// TODO: usage, after_help
|
||||
//.usage(...)
|
||||
//.after_help(...)
|
||||
//.after_help(TODO: Add note about multiplier strings here.)
|
||||
.get_matches_from(dashed_args);
|
||||
|
||||
let result = match (
|
||||
|
@ -1121,7 +1020,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
match result {
|
||||
Ok(_) => RTN_SUCCESS,
|
||||
Err(e) => {
|
||||
debug_println!("dd exiting with error:\n\t{}", e);
|
||||
eprintln!("dd exiting with error:\n\t{}", e);
|
||||
RTN_FAILURE
|
||||
}
|
||||
}
|
||||
|
@ -1211,7 +1110,7 @@ Printing performance stats is also triggered by the INFO signal (where supported
|
|||
clap::Arg::with_name(options::CONV)
|
||||
.long(options::CONV)
|
||||
.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:
|
||||
\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 '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\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.
|
||||
|
@ -1264,10 +1163,6 @@ General-Flags
|
|||
\t 'noctty' do not assign a controlling tty.
|
||||
\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(
|
||||
|
|
|
@ -1,20 +1,7 @@
|
|||
/* cspell:disable */
|
||||
|
||||
use super::*;
|
||||
|
||||
const NL: u8 = '\n' as u8;
|
||||
const SPACE: u8 = ' ' as u8;
|
||||
|
||||
macro_rules! rs (
|
||||
() =>
|
||||
{
|
||||
ReadStat {
|
||||
reads_complete: 0,
|
||||
reads_partial: 0,
|
||||
records_truncated: 0,
|
||||
}
|
||||
};
|
||||
);
|
||||
const NL: u8 = b'\n';
|
||||
const SPACE: u8 = b' ';
|
||||
|
||||
macro_rules! make_block_test (
|
||||
( $test_id:ident, $test_name:expr, $src:expr, $block:expr, $spec:expr ) =>
|
||||
|
@ -25,7 +12,7 @@ macro_rules! make_block_test (
|
|||
src: $src,
|
||||
non_ascii: false,
|
||||
ibs: 512,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: IConvFlags {
|
||||
ctable: None,
|
||||
|
@ -57,7 +44,7 @@ macro_rules! make_unblock_test (
|
|||
src: $src,
|
||||
non_ascii: false,
|
||||
ibs: 512,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: IConvFlags {
|
||||
ctable: None,
|
||||
|
@ -82,7 +69,7 @@ macro_rules! make_unblock_test (
|
|||
|
||||
#[test]
|
||||
fn block_test_no_nl() {
|
||||
let mut rs = rs!();
|
||||
let mut rs = ReadStat::default();
|
||||
let buf = vec![0u8, 1u8, 2u8, 3u8];
|
||||
let res = block(buf, 4, &mut rs);
|
||||
|
||||
|
@ -91,7 +78,7 @@ fn block_test_no_nl() {
|
|||
|
||||
#[test]
|
||||
fn block_test_no_nl_short_record() {
|
||||
let mut rs = rs!();
|
||||
let mut rs = ReadStat::default();
|
||||
let buf = vec![0u8, 1u8, 2u8, 3u8];
|
||||
let res = block(buf, 8, &mut rs);
|
||||
|
||||
|
@ -103,17 +90,18 @@ fn block_test_no_nl_short_record() {
|
|||
|
||||
#[test]
|
||||
fn block_test_no_nl_trunc() {
|
||||
let mut rs = rs!();
|
||||
let mut rs = ReadStat::default();
|
||||
let buf = vec![0u8, 1u8, 2u8, 3u8, 4u8];
|
||||
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!(rs.records_truncated, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_test_nl_gt_cbs_trunc() {
|
||||
let mut rs = rs!();
|
||||
let mut rs = ReadStat::default();
|
||||
let buf = vec![
|
||||
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!(
|
||||
res,
|
||||
vec![
|
||||
// Commented lines should be truncated and appear for reference only.
|
||||
vec![0u8, 1u8, 2u8, 3u8],
|
||||
// vec![4u8, SPACE, SPACE, SPACE],
|
||||
vec![0u8, 1u8, 2u8, 3u8],
|
||||
|
@ -134,7 +123,7 @@ fn block_test_nl_gt_cbs_trunc() {
|
|||
|
||||
#[test]
|
||||
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 res = block(buf, 8, &mut rs);
|
||||
|
||||
|
@ -149,7 +138,7 @@ fn block_test_surrounded_nl() {
|
|||
|
||||
#[test]
|
||||
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 res = block(buf, 8, &mut rs);
|
||||
|
||||
|
@ -165,7 +154,7 @@ fn block_test_multiple_nl_same_cbs_block() {
|
|||
|
||||
#[test]
|
||||
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 res = block(buf, 8, &mut rs);
|
||||
|
||||
|
@ -181,7 +170,7 @@ fn block_test_multiple_nl_diff_cbs_block() {
|
|||
|
||||
#[test]
|
||||
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 res = block(buf, 4, &mut rs);
|
||||
|
||||
|
@ -190,7 +179,7 @@ fn block_test_end_nl_diff_cbs_block() {
|
|||
|
||||
#[test]
|
||||
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 res = block(buf, 4, &mut rs);
|
||||
|
||||
|
@ -199,7 +188,7 @@ fn block_test_end_nl_same_cbs_block() {
|
|||
|
||||
#[test]
|
||||
fn block_test_double_end_nl() {
|
||||
let mut rs = rs!();
|
||||
let mut rs = ReadStat::default();
|
||||
let buf = vec![0u8, 1u8, 2u8, NL, NL];
|
||||
let res = block(buf, 4, &mut rs);
|
||||
|
||||
|
@ -211,7 +200,7 @@ fn block_test_double_end_nl() {
|
|||
|
||||
#[test]
|
||||
fn block_test_start_nl() {
|
||||
let mut rs = rs!();
|
||||
let mut rs = ReadStat::default();
|
||||
let buf = vec![NL, 0u8, 1u8, 2u8, 3u8];
|
||||
let res = block(buf, 4, &mut rs);
|
||||
|
||||
|
@ -223,7 +212,7 @@ fn block_test_start_nl() {
|
|||
|
||||
#[test]
|
||||
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 res = block(buf, 8, &mut rs);
|
||||
|
||||
|
@ -239,13 +228,14 @@ fn block_test_double_surrounded_nl_no_trunc() {
|
|||
|
||||
#[test]
|
||||
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 res = block(buf, 4, &mut rs);
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
vec![
|
||||
// Commented section should be truncated and appear for reference only.
|
||||
vec![0u8, 1u8, 2u8, 3u8],
|
||||
vec![SPACE, SPACE, SPACE, SPACE],
|
||||
vec![4u8, 5u8, 6u8, 7u8 /*, 8u8*/],
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* cspell:disable */
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! make_sync_test (
|
||||
|
@ -11,7 +9,7 @@ macro_rules! make_sync_test (
|
|||
src: $src,
|
||||
non_ascii: false,
|
||||
ibs: $ibs,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: IConvFlags {
|
||||
ctable: None,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* cspell:disable */
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! make_conv_test (
|
||||
|
@ -11,7 +9,7 @@ macro_rules! make_conv_test (
|
|||
src: $src,
|
||||
non_ascii: false,
|
||||
ibs: 512,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: icf!($ctable),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -36,7 +34,7 @@ macro_rules! make_icf_test (
|
|||
src: $src,
|
||||
non_ascii: false,
|
||||
ibs: 512,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: $icf,
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -141,7 +139,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
|
|||
.unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 128,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: icf!(Some(&ASCII_TO_EBCDIC)),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -163,7 +161,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
|
|||
src: File::open(&tmp_fname_ae).unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 256,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: icf!(Some(&EBCDIC_TO_ASCII)),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* cspell:disable */
|
||||
|
||||
use super::*;
|
||||
|
||||
mod block_unblock_tests;
|
||||
|
@ -39,24 +37,6 @@ const DEFAULT_IFLAGS: IFlags = IFlags {
|
|||
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> {
|
||||
src: R,
|
||||
}
|
||||
|
@ -102,7 +82,7 @@ macro_rules! make_spec_test (
|
|||
src: $src,
|
||||
non_ascii: false,
|
||||
ibs: 512,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* cspell:disable */
|
||||
|
||||
use super::*;
|
||||
|
||||
const DST_PLACEHOLDER: Vec<u8> = Vec::new();
|
||||
|
@ -52,7 +50,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 521,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -72,7 +70,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 1031,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -92,7 +90,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 1024,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: Some(CountType::Reads(32)),
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -112,7 +110,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 531,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: Some(CountType::Bytes(32 * 1024)),
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -132,7 +130,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 1024,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: Some(CountType::Reads(16)),
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -152,7 +150,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 531,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: Some(CountType::Bytes(12345)),
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -172,7 +170,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 1024,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: Some(CountType::Reads(32)),
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -192,7 +190,7 @@ make_io_test!(
|
|||
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
|
||||
non_ascii: false,
|
||||
ibs: 521,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: Some(CountType::Bytes(32 * 1024)),
|
||||
cflags: icf!(),
|
||||
iflags: DEFAULT_IFLAGS,
|
||||
|
@ -215,7 +213,7 @@ make_io_test!(
|
|||
},
|
||||
non_ascii: false,
|
||||
ibs: 521,
|
||||
xfer_stats: None,
|
||||
print_level: None,
|
||||
count: None,
|
||||
cflags: icf!(),
|
||||
iflags: IFlags {
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
/* cspell:disable */
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests;
|
||||
|
||||
|
@ -24,9 +22,8 @@ pub enum ParseError {
|
|||
MultipleExclNoCreat,
|
||||
FlagNoMatch(String),
|
||||
ConvFlagNoMatch(String),
|
||||
NoMatchingMultiplier(String),
|
||||
ByteStringContainsNoValue(String),
|
||||
MultiplierStringWouldOverflow(String),
|
||||
MultiplierStringParseFailure(String),
|
||||
MultiplierStringOverflow(String),
|
||||
BlockUnblockWithoutCBS,
|
||||
StatusLevelNotRecognized(String),
|
||||
Unimplemented(String),
|
||||
|
@ -56,13 +53,10 @@ impl std::fmt::Display for ParseError {
|
|||
Self::ConvFlagNoMatch(arg) => {
|
||||
write!(f, "Unrecognized conv=CONV -> {}", arg)
|
||||
}
|
||||
Self::NoMatchingMultiplier(arg) => {
|
||||
Self::MultiplierStringParseFailure(arg) => {
|
||||
write!(f, "Unrecognized byte multiplier -> {}", arg)
|
||||
}
|
||||
Self::ByteStringContainsNoValue(arg) => {
|
||||
write!(f, "Unrecognized byte value -> {}", arg)
|
||||
}
|
||||
Self::MultiplierStringWouldOverflow(arg) => {
|
||||
Self::MultiplierStringOverflow(arg) => {
|
||||
write!(
|
||||
f,
|
||||
"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> {
|
||||
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()))
|
||||
}
|
||||
|
||||
/// Parse bytes using str::parse, then map error if needed.
|
||||
fn parse_bytes_only(s: &str) -> Result<usize, ParseError> {
|
||||
match s.parse() {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(_) => Err(ParseError::ByteStringContainsNoValue(s.to_string())),
|
||||
}
|
||||
s.parse()
|
||||
.map_err(|_| ParseError::MultiplierStringParseFailure(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> {
|
||||
match s.find(char::is_alphabetic) {
|
||||
Some(idx) => {
|
||||
let base = parse_bytes_only(&s[..idx])?;
|
||||
let mult = parse_multiplier(&s[idx..])?;
|
||||
if let Some(idx) = s.rfind('c') {
|
||||
parse_bytes_only(&s[..idx])
|
||||
} else if let Some(idx) = s.rfind('w') {
|
||||
let partial = parse_bytes_only(&s[..idx])?;
|
||||
|
||||
if let Some(bytes) = base.checked_mul(mult) {
|
||||
Ok(bytes)
|
||||
} else {
|
||||
Err(ParseError::MultiplierStringWouldOverflow(s.to_string()))
|
||||
partial
|
||||
.checked_mul(2)
|
||||
.ok_or_else(|| ParseError::MultiplierStringOverflow(s.to_string()))
|
||||
} else {
|
||||
uucore::parse_size::parse_size(s).map_err(|e| match e {
|
||||
uucore::parse_size::ParseSizeError::ParseFailure(s) => {
|
||||
ParseError::MultiplierStringParseFailure(s)
|
||||
}
|
||||
}
|
||||
_ => parse_bytes_only(s),
|
||||
uucore::parse_size::ParseSizeError::SizeTooBig(s) => {
|
||||
ParseError::MultiplierStringOverflow(s)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
} 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)
|
||||
} else {
|
||||
Ok(512)
|
||||
|
@ -364,7 +337,7 @@ pub fn parse_ibs(matches: &Matches) -> Result<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)?;
|
||||
Ok(Some(bytes))
|
||||
} 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> {
|
||||
match matches.value_of("status") {
|
||||
match matches.value_of(options::STATUS) {
|
||||
Some(s) => {
|
||||
let st = s.parse()?;
|
||||
Ok(Some(st))
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* cspell:disable */
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::StatusLevel;
|
||||
|
@ -7,7 +5,7 @@ use crate::StatusLevel;
|
|||
#[cfg(not(target_os = "linux"))]
|
||||
#[test]
|
||||
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
|
||||
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();
|
||||
|
||||
match parse_iflags(&matches) {
|
||||
Ok(_) => unfailed.push(format!("iflag={}", flag)),
|
||||
Ok(_) => succeeded.push(format!("iflag={}", flag)),
|
||||
Err(_) => { /* expected behaviour :-) */ }
|
||||
}
|
||||
match parse_oflags(&matches) {
|
||||
Ok(_) => unfailed.push(format!("oflag={}", flag)),
|
||||
Ok(_) => succeeded.push(format!("oflag={}", flag)),
|
||||
Err(_) => { /* expected behaviour :-) */ }
|
||||
}
|
||||
}
|
||||
|
||||
if !unfailed.is_empty() {
|
||||
if !succeeded.is_empty() {
|
||||
panic!(
|
||||
"The following flags did not panic as expected: {:?}",
|
||||
unfailed
|
||||
succeeded
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unimplemented_flags_should_error() {
|
||||
let mut unfailed = Vec::new();
|
||||
let mut succeeded = Vec::new();
|
||||
|
||||
// The following flags are not implemented
|
||||
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();
|
||||
|
||||
match parse_iflags(&matches) {
|
||||
Ok(_) => unfailed.push(format!("iflag={}", flag)),
|
||||
Ok(_) => succeeded.push(format!("iflag={}", flag)),
|
||||
Err(_) => { /* expected behaviour :-) */ }
|
||||
}
|
||||
match parse_oflags(&matches) {
|
||||
Ok(_) => unfailed.push(format!("oflag={}", flag)),
|
||||
Ok(_) => succeeded.push(format!("oflag={}", flag)),
|
||||
Err(_) => { /* expected behaviour :-) */ }
|
||||
}
|
||||
}
|
||||
|
||||
if !unfailed.is_empty() {
|
||||
if !succeeded.is_empty() {
|
||||
panic!(
|
||||
"The following flags did not panic as expected: {:?}",
|
||||
unfailed
|
||||
succeeded
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -105,6 +103,176 @@ fn test_status_level_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]
|
||||
fn test_status_level_progress() {
|
||||
let args = vec![
|
||||
|
@ -374,16 +542,6 @@ test_byte_parser!(
|
|||
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]
|
||||
#[should_panic]
|
||||
fn test_overflow_panic() {
|
||||
|
@ -395,7 +553,7 @@ fn test_overflow_panic() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_neg_panic() {
|
||||
let bs_str = format!("{}KiB", -1);
|
||||
let bs_str = format!("{}", -1);
|
||||
|
||||
parse_bytes_with_opt_multiplier(&bs_str).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* cspell:disable */
|
||||
|
||||
use crate::common::util::*;
|
||||
|
||||
use std::fs::{File, OpenOptions};
|
||||
|
|
Loading…
Reference in a new issue