Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/uu/dd/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ dd-error-invalid-output-flag = invalid output flag: '{ $flag }'
dd-error-conv-flag-no-match = invalid conversion: '{ $flag }'
dd-error-multiplier-parse-failure = invalid number: '{ $input }'
dd-error-multiplier-overflow = Multiplier string would overflow on current system -> { $input }
dd-error-block-without-cbs = conv=block or conv=unblock specified without cbs=N
dd-error-status-not-recognized = invalid status level: '{ $level }'
dd-error-unimplemented = feature not implemented on this system -> { $feature }
dd-error-bs-out-of-range = { $param }=N cannot fit into memory
Expand Down
1 change: 0 additions & 1 deletion src/uu/dd/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ dd-error-invalid-output-flag = indicateur de sortie invalide : '{ $flag }'
dd-error-conv-flag-no-match = conversion invalide : '{ $flag }'
dd-error-multiplier-parse-failure = nombre invalide : '{ $input }'
dd-error-multiplier-overflow = La chaîne de multiplicateur déborderait sur le système actuel -> { $input }
dd-error-block-without-cbs = conv=block ou conv=unblock spécifié sans cbs=N
dd-error-status-not-recognized = niveau d'état invalide : '{ $level }'
dd-error-unimplemented = fonctionnalité non implémentée sur ce système -> { $feature }
dd-error-bs-out-of-range = { $param }=N ne peut pas tenir en mémoire
Expand Down
37 changes: 10 additions & 27 deletions src/uu/dd/src/parseargs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ pub enum ParseError {
MultiplierStringParseFailure(String),
#[error("{}", translate!("dd-error-multiplier-overflow", "input" => .0))]
MultiplierStringOverflow(String),
#[error("{}", translate!("dd-error-block-without-cbs"))]
BlockUnblockWithoutCBS,
#[error("{}", translate!("dd-error-status-not-recognized", "level" => .0))]
StatusLevelNotRecognized(String),
#[error("{}", translate!("dd-error-unimplemented", "feature" => .0))]
Expand Down Expand Up @@ -183,6 +181,12 @@ impl Parser {
_ => return Err(ParseError::MultipleFmtTable),
};

let block_requested = conv.block || conv.ebcdic || conv.ibm;
let unblock_requested = conv.unblock || conv.ascii;
if self.cbs.is_some() && block_requested && unblock_requested {
return Err(ParseError::MultipleBlockUnblock);
}

let case = match (conv.ucase, conv.lcase) {
(false, false) => None,
(true, false) => Some(Case::Upper),
Expand All @@ -197,31 +201,10 @@ impl Parser {
return Err(ParseError::MultipleExclNoCreate);
}

// The GNU docs state that
// - ascii implies unblock
// - ebcdic and ibm imply block
// This has a side effect in how it's implemented in GNU, because this errors:
// conv=block,unblock
// but these don't:
// conv=ascii,block,unblock
// conv=block,ascii,unblock
// conv=block,unblock,ascii
// conv=block conv=unblock conv=ascii
let block = if let Some(cbs) = self.cbs {
match conversion {
Some(Conversion::Ascii) => Some(Block::Unblock(cbs)),
Some(_) => Some(Block::Block(cbs)),
None => match (conv.block, conv.unblock) {
(false, false) => None,
(true, false) => Some(Block::Block(cbs)),
(false, true) => Some(Block::Unblock(cbs)),
(true, true) => return Err(ParseError::MultipleBlockUnblock),
},
}
} else if conv.block || conv.unblock {
return Err(ParseError::BlockUnblockWithoutCBS);
} else {
None
let block = match self.cbs {
Some(cbs) if block_requested => Some(Block::Block(cbs)),
Some(cbs) if unblock_requested => Some(Block::Unblock(cbs)),
_ => None,
};

let iconv = IConvFlags {
Expand Down
2 changes: 1 addition & 1 deletion src/uu/dd/src/parseargs/unit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ fn icf_case_error() {

#[test]
fn icf_block_error() {
let args = ["conv=block,unblock"];
let args = ["conv=block,unblock", "cbs=1"];
assert!(Parser::new().parse(args).is_err());
}

Expand Down
69 changes: 69 additions & 0 deletions tests/by-util/test_dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2643,3 +2643,72 @@ dd: unrecognized operand 'bsx=1'
.stderr_does_not_contain("╭─");
}
}

// Conversion conflicts are reported in a fixed order: ascii/ebcdic/ibm,
// block/unblock, lcase/ucase, then excl/nocreat. ascii implies unblock;
// ebcdic and ibm imply block, so with cbs an explicit flag in the other
// direction is a block/unblock conflict as well.
#[test]
fn test_block_unblock_conflicts() {
for conv in [
// block/unblock is reported before the later pairs.
"block,unblock,lcase,ucase",
"block,unblock,excl,nocreat",
// The implied direction conflicts with the explicit flag.
"ascii,block",
"ebcdic,unblock",
"ibm,unblock",
"ascii,block,unblock",
] {
new_ucmd!()
.args(&[
format!("conv={conv}"),
"cbs=1".into(),
"if=/dev/null".into(),
])
.fails_with_code(1)
.stderr_only("dd: cannot combine block and unblock\n");
}
}

#[test]
fn test_implied_block_direction_agrees_with_explicit_flag() {
for conv in ["ascii,unblock", "ebcdic,block", "ibm,block"] {
new_ucmd!()
.args(&[format!("conv={conv}"), "cbs=1".into(), "status=none".into()])
.pipe_in("")
.succeeds()
.no_output();
}
}

// Without cbs, block and unblock have no record length to work with and are
// ignored rather than rejected.
#[test]
Comment thread
ChihweiLHBird marked this conversation as resolved.
fn test_block_unblock_without_cbs_is_ignored() {
for conv in ["block", "unblock", "block,unblock"] {
new_ucmd!()
.args(&[format!("conv={conv}"), "status=none".into()])
.pipe_in("ab\ncd\n")
.succeeds()
.stdout_only("ab\ncd\n");
}
}

// Without cbs, ascii and ebcdic still convert the bytes; only the implied
// unblock or block step is skipped, so the trailing space survives and no
// newline is added.
#[test]
fn test_ascii_ebcdic_without_cbs_still_convert() {
let cases: [(&str, &[u8], &[u8]); 2] = [
("ascii", b"\xc1\xc2\x40", b"AB "),
("ebcdic", b"AB ", b"\xc1\xc2\x40"),
];
for (conv, input, expected) in cases {
new_ucmd!()
.args(&[format!("conv={conv}"), "status=none".into()])
.pipe_in(input)
.succeeds()
.stdout_only_bytes(expected);
}
}
Loading