Skip to content

dd: quote the operand value in an error, as GNU does - #14294

Open
arbelonson-source wants to merge 1 commit into
uutils:mainfrom
arbelonson-source:fix/dd-quote-operand-values
Open

dd: quote the operand value in an error, as GNU does#14294
arbelonson-source wants to merge 1 commit into
uutils:mainfrom
arbelonson-source:fix/dd-quote-operand-values

Conversation

@arbelonson-source

Copy link
Copy Markdown
Contributor

Fixes #13868.

dd puts the value it rejected straight into the message, so a byte the terminal will not show is sent to the terminal as itself. GNU spells it out, and quotes the value even when it is ordinary:

$ dd status=$'\1'          # ours, before      (an unprintable byte reaches the tty)
dd: invalid status level: 
$ dd status=$'\1'          # GNU 9.11
dd: invalid status level: '\001'

$ dd status=bogus          # ours, before
dd: invalid status level: bogus
$ dd status=bogus          # GNU 9.11
dd: invalid status level: 'bogus'

Two styles, and which goes where

GNU uses two quoting styles in dd, and which one applies to which message is visible from the outside. The choices — status=, iflag=, oflag=, conv= — take plain quoting, where an escape stays inside the quotes:

$ dd status=a$'\t'b   →  dd: invalid status level: 'a\tb'
$ dd status=a\'b      →  dd: invalid status level: 'a\'b'
$ dd status='a b'     →  dd: invalid status level: 'a b'

while numbers and operand names take shell syntax, where an unprintable byte ends the quoted run rather than being escaped inside it, and a quote in the value switches which quotes are used:

$ dd bs=$'\1'         →  dd: invalid number: ''$'\001'
$ dd bs=a\'b          →  dd: invalid number: "a'b"
$ dd $'\1'=1          →  dd: unrecognized operand ''$'\001''=1'

Both styles already exist in uucore::quoting_style (C { quotes: Single } and SHELL_ESCAPE_QUOTE) and both already match GNU byte for byte — ls --quoting-style=locale and --quoting-style=shell-escape-always agree with GNU on all of these today. So this is a matter of asking for them.

The quotes move out of the Fluent messages and into the quoting, since that is what supplies them now; fr-FR.ftl gets the same mechanical change, and its rendered output is unchanged for ordinary values.

Not covered

Two differences turned up next to this one that are unrelated to quoting and unchanged by this PR — happy to file them if they are not already known:

  • dd skip=8E ibs=8 reports invalid number: '18446744073709551615', the clamped internal value, where GNU echoes the operand as typed ('8E'). GNU also accepts skip=9223372036854775807 and fails later at skip time, where we reject it during parsing.
  • dd bs=0x warns '0x' is a zero multiplier and continues; GNU errors with invalid number: '0x'.

Testing

  • 177-case differential sweep against GNU 9.11 under LC_ALL=C, over every value-carrying operand (status, iflag, oflag, conv, bs, ibs, obs, cbs, count, skip, seek) crossed with unprintable bytes, tab, newline, DEL, backslash, single and double quote, space, empty, non-ASCII, and shell metacharacters, plus unrecognized operands: 174 match, and the 3 that do not are the two pre-existing differences listed above, verified identical on main.
  • New test_operand_value_is_quoted_like_gnu in test_dd.rs, covering both styles.
  • cargo test --features dd --test tests -- test_dd: 145 passed, 0 failed (144 pre-existing, 1 new); cargo test -p uu_dd: 93 passed.
  • moz-fluent-lint --config .github/fluent_linter_config.yml src/uu/dd/locales: no errors.
  • cargo fmt --check and cargo clippy -p uu_dd --all-targets: clean.

Disclosure

Prepared with AI assistance (Claude Opus 5, via Claude Code), per the AI policy in CONTRIBUTING.md. GNU's behaviour was established by running the installed GNU binary as a black box; I did not read GNU coreutils source. All testing was run locally.

`dd` puts the value it rejected into the message unquoted, so a byte the
terminal will not show is sent to the terminal as itself:

    $ dd status=$'\1'
    dd: invalid status level: <the byte>

GNU spells it out instead, and quotes the value even when it is ordinary:

    $ dd status=$'\1'
    dd: invalid status level: '\001'
    $ dd status=bogus
    dd: invalid status level: 'bogus'

GNU uses two quoting styles here, and which one goes where is visible from
the outside. The choices -- `status=`, `iflag=`, `oflag=`, `conv=` -- take
plain quoting, where an escape stays inside the quotes:

    dd: invalid status level: 'a\'b'

while numbers and operand names take shell syntax, where an unprintable
byte ends the quoted run and a quote in the value switches which quotes
are used:

    dd: invalid number: ''$'\001'
    dd: invalid number: "a'b"
    dd: unrecognized operand ''$'\001''=1'

Both are already in `uucore::quoting_style`, so this is a matter of asking
for them. The quotes move out of the message and into the quoting, which
is what supplies them for every value now.

Fixes uutils#13868.
@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/tail/symlink (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/cut/bounded-memory (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/date/date-locale-hour (passes in this run but fails in the 'main' branch)
Note: The gnu test tests/seq/seq-epipe is now being skipped but was previously passing.
Congrats! The gnu test tests/tail/tail-n0f is now passing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

minorbug(dd): status= errors not quoted

1 participant