Skip to content
Open
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
14 changes: 7 additions & 7 deletions src/uu/dd/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,21 @@ dd-error-not-directory = setting flags for '{ $file }': Not a directory
dd-error-failed-discard-cache = failed to discard cache for: { $file }

# Parse errors
dd-error-unrecognized-operand = unrecognized operand '{ $operand }'
dd-error-unrecognized-operand = unrecognized operand { $operand }
dd-error-multiple-format-table = cannot combine any two of {"{"}ascii,ebcdic,ibm{"}"}
dd-error-multiple-case = cannot combine lcase and ucase
dd-error-multiple-block = cannot combine block and unblock
dd-error-multiple-excl = cannot combine excl and nocreat
dd-error-invalid-flag = invalid input flag: '{ $flag }'
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-invalid-flag = invalid input flag: { $flag }
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-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
dd-error-invalid-number = invalid number: '{ $input }'
dd-error-invalid-number = invalid number: { $input }

# Progress messages
dd-progress-records-in = { $complete }+{ $partial } records in
Expand Down
14 changes: 7 additions & 7 deletions src/uu/dd/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,21 @@ dd-error-not-directory = définir les indicateurs pour '{ $file }' : N'est pas u
dd-error-failed-discard-cache = échec de la suppression du cache pour : { $file }

# Parse errors
dd-error-unrecognized-operand = opérande non reconnue '{ $operand }'
dd-error-unrecognized-operand = opérande non reconnue { $operand }
dd-error-multiple-format-table = impossible de combiner deux options parmi {"{"}ascii,ebcdic,ibm{"}"}
dd-error-multiple-case = impossible de combiner lcase et ucase
dd-error-multiple-block = impossible de combiner block et unblock
dd-error-multiple-excl = impossible de combiner excl et nocreat
dd-error-invalid-flag = indicateur d'entrée invalide : '{ $flag }'
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-invalid-flag = indicateur d'entrée invalide : { $flag }
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-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
dd-error-invalid-number = nombre invalide : '{ $input }'
dd-error-invalid-number = nombre invalide : { $input }

# Progress messages
dd-progress-records-in = { $complete }+{ $partial } enregistrements en entrée
Expand Down
42 changes: 34 additions & 8 deletions src/uu/dd/src/parseargs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,39 @@ use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::UError;
use uucore::parser::parse_size::{ParseSizeError, Parser as SizeParser};
use uucore::quoting_style::{Quotes, QuotingStyle, locale_aware_escape_name};
use uucore::show_warning;
use uucore::translate;

/// A value in single quotes, with the characters a terminal would not show
/// spelled out, as GNU's `quote` does: `status=$'\1'` is reported as `'\001'`
/// rather than as the byte itself.
fn quote(value: &str) -> String {
escape(
value,
QuotingStyle::C {
quotes: Quotes::Single,
},
)
}

/// The same, in the shell syntax GNU quotes numbers and operands with, where
/// an unprintable byte ends the quoted run rather than being escaped inside
/// it: `''$'\001'`.
fn shell_quote(value: &str) -> String {
escape(value, QuotingStyle::SHELL_ESCAPE_QUOTE)
}

fn escape(value: &str, style: QuotingStyle) -> String {
locale_aware_escape_name(value.as_ref(), style)
.into_string()
.expect("escaping a str only ever adds ASCII")
}

/// Parser Errors describe errors with parser input
#[derive(Debug, PartialEq, Eq, Error)]
pub enum ParseError {
#[error("{}", translate!("dd-error-unrecognized-operand", "operand" => .0))]
#[error("{}", translate!("dd-error-unrecognized-operand", "operand" => shell_quote(.0)))]
UnrecognizedOperand(String),
#[error("{}", translate!("dd-error-multiple-format-table"))]
MultipleFmtTable,
Expand All @@ -30,27 +56,27 @@ pub enum ParseError {
MultipleBlockUnblock,
#[error("{}", translate!("dd-error-multiple-excl"))]
MultipleExclNoCreate,
#[error("{}", translate!("dd-error-invalid-flag", "flag" => .0))]
#[error("{}", translate!("dd-error-invalid-flag", "flag" => quote(.0)))]
FlagNoMatch(String),
#[error("{}", translate!("dd-error-invalid-output-flag", "flag" => .0))]
#[error("{}", translate!("dd-error-invalid-output-flag", "flag" => quote(.0)))]
OutputFlagNoMatch(String),
#[error("{}", translate!("dd-error-conv-flag-no-match", "flag" => .0))]
#[error("{}", translate!("dd-error-conv-flag-no-match", "flag" => quote(.0)))]
ConvFlagNoMatch(String),
#[error("{}", translate!("dd-error-multiplier-parse-failure", "input" => .0))]
#[error("{}", translate!("dd-error-multiplier-parse-failure", "input" => shell_quote(.0)))]
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))]
#[error("{}", translate!("dd-error-status-not-recognized", "level" => quote(.0)))]
StatusLevelNotRecognized(String),
#[error("{}", translate!("dd-error-unimplemented", "feature" => .0))]
Unimplemented(String),
#[error("{}", translate!("dd-error-bs-out-of-range", "param" => .0))]
BsOutOfRange(String),
#[error("{}", translate!("dd-error-invalid-number", "input" => .0))]
#[error("{}", translate!("dd-error-invalid-number", "input" => shell_quote(.0)))]
InvalidNumber(String),
#[error("invalid number: '{0}': {1}")]
#[error("invalid number: {}: {}", shell_quote(.0), .1)]
InvalidNumberWithErrMsg(String, String),
}

Expand Down
43 changes: 43 additions & 0 deletions tests/by-util/test_dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,49 @@ fn test_invalid_flag_arg_gnu_compatibility() {
}
}

/// A value that would not survive being printed is spelled out instead, as
/// GNU does, rather than sending the raw byte to the terminal.
#[test]
fn test_operand_value_is_quoted_like_gnu() {
// The choices GNU quotes plainly: single quotes with C escapes inside.
for (operand, message) in [
("status=\u{1}", r"invalid status level: '\001'"),
("status=a\tb", r"invalid status level: 'a\tb'"),
("status=a'b", r"invalid status level: 'a\'b'"),
("status=a\\b", r"invalid status level: 'a\\b'"),
("status=a b", "invalid status level: 'a b'"),
("iflag=\u{1}", r"invalid input flag: '\001'"),
("oflag=\u{1}", r"invalid output flag: '\001'"),
("conv=\u{1}", r"invalid conversion: '\001'"),
] {
new_ucmd!().args(&[operand]).fails().usage_error(message);
}

// Numbers and operands take the shell syntax GNU uses for them, where an
// unprintable byte ends the quoted run rather than being escaped inside
// it, and a quote switches which quotes are used.
new_ucmd!()
.args(&["\u{1}=1"])
.fails()
.usage_error(r"unrecognized operand ''$'\001''=1'");

new_ucmd!()
.args(&["bs=\u{1}"])
.fails()
.stderr_is("dd: invalid number: ''$'\\001'\n");

new_ucmd!()
.args(&["bs=a'b"])
.fails()
.stderr_is("dd: invalid number: \"a'b\"\n");

// An ordinary value is quoted too, which is what it already was.
new_ucmd!()
.args(&["bs=29d"])
.fails()
.stderr_is("dd: invalid number: '29d'\n");
}

#[test]
fn test_invalid_file_arg_gnu_compatibility() {
new_ucmd!()
Expand Down
Loading