1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 20:17:45 +00:00

dd: add Settings.buffered field

Add the `Settings.buffered` field to indicate whether partial output
blocks should be buffered until they are complete.
This commit is contained in:
Jeffrey Finkelstein 2023-03-12 19:02:48 -04:00 committed by jfinkels
parent cd4f455a77
commit 016ae34d50
3 changed files with 49 additions and 34 deletions

View file

@ -76,6 +76,8 @@ struct Settings {
oconv: OConvFlags, oconv: OConvFlags,
oflags: OFlags, oflags: OFlags,
status: Option<StatusLevel>, status: Option<StatusLevel>,
/// Whether the output writer should buffer partial blocks until complete.
buffered: bool,
} }
/// A timer which triggers on a given interval /// A timer which triggers on a given interval
@ -128,6 +130,12 @@ enum Num {
Bytes(u64), Bytes(u64),
} }
impl Default for Num {
fn default() -> Self {
Self::Blocks(0)
}
}
impl Num { impl Num {
fn force_bytes_if(self, force: bool) -> Self { fn force_bytes_if(self, force: bool) -> Self {
match self { match self {

View file

@ -35,41 +35,28 @@ pub enum ParseError {
} }
/// Contains a temporary state during parsing of the arguments /// Contains a temporary state during parsing of the arguments
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Default)]
pub struct Parser { pub struct Parser {
infile: Option<String>, infile: Option<String>,
outfile: Option<String>, outfile: Option<String>,
ibs: usize, /// The block size option specified on the command-line, if any.
obs: usize, bs: Option<usize>,
/// The input block size option specified on the command-line, if any.
ibs: Option<usize>,
/// The output block size option specified on the command-line, if any.
obs: Option<usize>,
cbs: Option<usize>, cbs: Option<usize>,
skip: Num, skip: Num,
seek: Num, seek: Num,
count: Option<Num>, count: Option<Num>,
conv: ConvFlags, conv: ConvFlags,
/// Whether a data-transforming `conv` option has been specified.
is_conv_specified: bool,
iflag: IFlags, iflag: IFlags,
oflag: OFlags, oflag: OFlags,
status: Option<StatusLevel>, status: Option<StatusLevel>,
} }
impl Default for Parser {
fn default() -> Self {
Self {
ibs: 512,
obs: 512,
cbs: None,
infile: None,
outfile: None,
skip: Num::Blocks(0),
seek: Num::Blocks(0),
count: None,
conv: ConvFlags::default(),
iflag: IFlags::default(),
oflag: OFlags::default(),
status: None,
}
}
}
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
pub struct ConvFlags { pub struct ConvFlags {
ascii: bool, ascii: bool,
@ -212,15 +199,34 @@ impl Parser {
fsync: conv.fsync, fsync: conv.fsync,
}; };
// Input and output block sizes.
//
// The `bs` option takes precedence. If either is not
// provided, `ibs` and `obs` are each 512 bytes by default.
let (ibs, obs) = match self.bs {
None => (self.ibs.unwrap_or(512), self.obs.unwrap_or(512)),
Some(bs) => (bs, bs),
};
// Whether to buffer partial output blocks until they are completed.
//
// From the GNU `dd` documentation for the `bs=BYTES` option:
//
// > [...] if no data-transforming 'conv' option is specified,
// > input is copied to the output as soon as it's read, even if
// > it is smaller than the block size.
//
let buffered = self.bs.is_none() || self.is_conv_specified;
let skip = self let skip = self
.skip .skip
.force_bytes_if(self.iflag.skip_bytes) .force_bytes_if(self.iflag.skip_bytes)
.to_bytes(self.ibs as u64); .to_bytes(ibs as u64);
let seek = self let seek = self
.seek .seek
.force_bytes_if(self.oflag.seek_bytes) .force_bytes_if(self.oflag.seek_bytes)
.to_bytes(self.obs as u64); .to_bytes(obs as u64);
let count = self.count.map(|c| c.force_bytes_if(self.iflag.count_bytes)); let count = self.count.map(|c| c.force_bytes_if(self.iflag.count_bytes));
@ -230,8 +236,9 @@ impl Parser {
count, count,
iconv, iconv,
oconv, oconv,
ibs: self.ibs, ibs,
obs: self.obs, obs,
buffered,
infile: self.infile, infile: self.infile,
outfile: self.outfile, outfile: self.outfile,
iflags: self.iflag, iflags: self.iflag,
@ -244,18 +251,17 @@ impl Parser {
match operand.split_once('=') { match operand.split_once('=') {
None => return Err(ParseError::UnrecognizedOperand(operand.to_string())), None => return Err(ParseError::UnrecognizedOperand(operand.to_string())),
Some((k, v)) => match k { Some((k, v)) => match k {
"bs" => { "bs" => self.bs = Some(Self::parse_bytes(k, v)?),
let bs = Self::parse_bytes(k, v)?;
self.ibs = bs;
self.obs = bs;
}
"cbs" => self.cbs = Some(Self::parse_bytes(k, v)?), "cbs" => self.cbs = Some(Self::parse_bytes(k, v)?),
"conv" => self.parse_conv_flags(v)?, "conv" => {
self.is_conv_specified = true;
self.parse_conv_flags(v)?;
}
"count" => self.count = Some(Self::parse_n(v)?), "count" => self.count = Some(Self::parse_n(v)?),
"ibs" => self.ibs = Self::parse_bytes(k, v)?, "ibs" => self.ibs = Some(Self::parse_bytes(k, v)?),
"if" => self.infile = Some(v.to_string()), "if" => self.infile = Some(v.to_string()),
"iflag" => self.parse_input_flags(v)?, "iflag" => self.parse_input_flags(v)?,
"obs" => self.obs = Self::parse_bytes(k, v)?, "obs" => self.obs = Some(Self::parse_bytes(k, v)?),
"of" => self.outfile = Some(v.to_string()), "of" => self.outfile = Some(v.to_string()),
"oflag" => self.parse_output_flags(v)?, "oflag" => self.parse_output_flags(v)?,
"seek" | "oseek" => self.seek = Self::parse_n(v)?, "seek" | "oseek" => self.seek = Self::parse_n(v)?,

View file

@ -358,6 +358,7 @@ fn parse_icf_tokens_remaining() {
fsync: true, fsync: true,
..Default::default() ..Default::default()
}, },
is_conv_specified: true,
..Default::default() ..Default::default()
}) })
); );