coreutils icon indicating copy to clipboard operation
coreutils copied to clipboard

printf: `"%q" $'\001'\'$'\001'` produces incorrect output

Open egmontkob opened this issue 2 weeks ago • 1 comments

I've randomly stumbled across https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=992161, a bugreport against GNU Coreutils that they fixed in v9.6.

However, it turns out Rust Coreutils also produces incorrect output for the same command (a different incorrect output than GNU Coreutils used to).

At current main HEAD:

prompt$ ./target/release/coreutils printf "%q\n" $'\001'\'$'\001'
"'$'\001''''$'\001"

The last cmdline parameter is a 3-byte string: a 0x01 byte, an apostrophe, and one more 0x01.

The output is supposed to be a string which, if placed in a shell command line in unquoted context, produces the same string.

However, this output expands to 17 bytes (tested in busybox ash, bash, dash, ksh, mksh, zsh):

prompt$ printf "%s" "'$'\001''''$'\001" | od -t x1
0000000 27 24 27 5c 30 30 31 27 27 27 27 24 27 5c 30 30
0000020 31
0000021

The string, after shell expansion, obviously starts with an apostrophe which is already wrong, and also has multiple literal apostrophes in the middle. Even if the $'\001' construct was interpreted inside double quotes (which it is not), the missing trailing ' in the second such string would also pose a problem.

The first command's output, if copy-pasted into the second command's corresponding parameter, needs to output the same 3 bytes mentioned above, e.g.:

# bash 5.2.37's builtin printf
prompt$ printf "%q\n" $'\001'\'$'\001'
$'\001\'\001'
prompt$ printf "%s" $'\001\'\001' | od -t x1
0000000 01 27 01
0000003
# GNU Coreutils current main HEAD's printf
prompt$ ./src/printf "%q\n" $'\001'\'$'\001'
''$'\001'\'''$'\001'
prompt$ printf "%s" ''$'\001'\'''$'\001' | od -t x1
0000000 01 27 01
0000003

egmontkob avatar Dec 11 '25 21:12 egmontkob

i added self.exit_dollar() but does NOT fix the overall issue because the fundamental quoting style is wrong.

input bytes:
0000000    01  27  01                                                    
0000003

=== Rust printf output: ===
"'$'\001''''$'\001"
           22  27  24  27  5c  30  30  31  27  27  27  27  24  27  5c  30
           30  31  22                                                    


=== Rust output when evaluated: ===
0000000    27  24  27  5c  30  30  31  27  27  27  27  24  27  5c  30  30
0000020    31                                                            
0000021

=== Bash printf output: ===
$'\001\'\001'

=== Bash output when evaluated: ===
0000000    01  27  01                                                    
0000003

=== Additional test cases ===

--- Input:            61  27  62                                                     ---
Rust: "a'b"
Bash: a\'b
MISMATCH!

--- Input:            61  22  62                                                     ---
Rust: 'a"b'
Bash: a\"b
MISMATCH!

--- Input:            61  01  62                                                     ---
Rust: 'a'$'\001''b'
Bash: $'a\001b'
MISMATCH!

--- Input:            01                                                             ---
Rust: ''$'\001'
Bash: $'\001'
MISMATCH!

--- Input:            61  27  62                                                     ---
Rust: "a'b"
Bash: a\'b
MISMATCH!

--- Input:            01  27  01                                                     ---
Rust: "'$'\001''''$'\001"
Bash: $'\001\'\001'
MISMATCH!

naoNao89 avatar Dec 11 '25 23:12 naoNao89

ls --quoting-style=shell-escape suffers from the same issue (elaborated within the PR's discussion).

egmontkob avatar Dec 12 '25 08:12 egmontkob