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

View file

@ -35,41 +35,28 @@ pub enum ParseError {
}
/// Contains a temporary state during parsing of the arguments
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Default)]
pub struct Parser {
infile: Option<String>,
outfile: Option<String>,
ibs: usize,
obs: usize,
/// The block size option specified on the command-line, if any.
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>,
skip: Num,
seek: Num,
count: Option<Num>,
conv: ConvFlags,
/// Whether a data-transforming `conv` option has been specified.
is_conv_specified: bool,
iflag: IFlags,
oflag: OFlags,
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)]
pub struct ConvFlags {
ascii: bool,
@ -212,15 +199,34 @@ impl Parser {
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
.skip
.force_bytes_if(self.iflag.skip_bytes)
.to_bytes(self.ibs as u64);
.to_bytes(ibs as u64);
let seek = self
.seek
.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));
@ -230,8 +236,9 @@ impl Parser {
count,
iconv,
oconv,
ibs: self.ibs,
obs: self.obs,
ibs,
obs,
buffered,
infile: self.infile,
outfile: self.outfile,
iflags: self.iflag,
@ -244,18 +251,17 @@ impl Parser {
match operand.split_once('=') {
None => return Err(ParseError::UnrecognizedOperand(operand.to_string())),
Some((k, v)) => match k {
"bs" => {
let bs = Self::parse_bytes(k, v)?;
self.ibs = bs;
self.obs = bs;
}
"bs" => self.bs = 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)?),
"ibs" => self.ibs = Self::parse_bytes(k, v)?,
"ibs" => self.ibs = Some(Self::parse_bytes(k, v)?),
"if" => self.infile = Some(v.to_string()),
"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()),
"oflag" => self.parse_output_flags(v)?,
"seek" | "oseek" => self.seek = Self::parse_n(v)?,

View file

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