Repository navigation
fix(builtins): keep printf %q bash-faithful across uutils' always_quote - #2917
Conversation
The weekly Coreutils Args Drift job is red on main. Regeneration against
upstream HEAD succeeds; the run then dies at "Verify build still passes"
with one error, so the patch is never uploaded and no drift PR opens:
error[E0599]: no method named `always_quote` found for enum
`format_support::QuotingStyle` in the current scope
--> crates/bashkit/src/builtins/generated/format/spec.rs:358:56
358 | let style = QuotingStyle::SHELL_ESCAPE.always_quote(has_brace);
Upstream rewrote the `%q` arm of `format/spec.rs` from
`locale_aware_shell_escape(value)` to
`QuotingStyle::SHELL_ESCAPE.always_quote(has_brace)` plus
`locale_aware_escape_name`, so GNU `printf %q` now wraps a brace-bearing
argument in single quotes (`'a{b'`). The handwritten `format_support`
shim has no such method and the drift run cannot get past the build.
This is not the usual rename-straddle: bash disagrees with the new GNU
behaviour. bash 5.2 backslash-escapes braces and never switches to
single quotes — `printf %q 'a{b'` is `a\{b`, `printf %q '{}'` is
`\{\}`. bashkit's printf is bash's, so the shim provides the *name* and
drops the *behaviour*: `QuotingStyle::always_quote` returns `self`
unchanged. That is sound because `locale_aware_escape_name` routes every
shell style to the bash-faithful `shell_quote` and never consults the
flags carried on the style; the shim's variants are unit-only for the
same reason, carrying the names the vendored module uses rather than
uucore's quoting behaviour.
Forwarding the semantics instead would have silently changed a builtin's
output, and the drift PR could not have repaired it: the open-pr job
enforces a generated-only diff, so handwritten `printf.rs` is out of its
reach. Two tests pin the refusal — one that the flag is inert
(`always_quote_does_not_change_how_braces_are_escaped`), one that the
resulting `%q` output is bash's
(`quoted_string_escapes_braces_the_way_bash_does`, values taken from
bash 5.2.21).
Knowledge gains a "Shim refuses upstream behaviour bash does not have"
section next to the existing rename-straddle rule.
Verified: the pre-fix tree reproduces the CI error verbatim, the
post-fix tree builds, and bash_spec_tests plus the printf and
format_support unit tests pass.
Review summary — #2917Verdict: Sound forward-compat shim; no findings. No review submitted ( What the change does: Adds a no-op Context read beyond the diff: Why no findings:
Not verified: Could not build or run tests (no cargo in this shell); bash-version output claim taken from the PR description and checked for internal consistency with |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 295e0c5 | Commit Preview URL Branch Preview URL |
Nov 13 2026, 09:30 AM |
What changed
The weekly
Coreutils Args Driftjob can build again, andprintf %qkeeps bash's spelling while it does.format_support::QuotingStylegains analways_quotesetter that returnsselfunchanged. The shim provides the name upstream's vendored code now calls, and deliberately drops the behaviour it encodes.Why
Coreutils Args Driftis red on main. It regenerates fine, then dies at Verify build still passes — so the patch is never uploaded and no drift PR opens. The job is weekly, so it stays red and the coreutils surfaces stay frozen until someone reads the log. (run 43399264556, dispatched on current main to confirm the 2026-11-09 failure was not just the one #2895 already fixed — it is a new, different break.)Upstream rewrote the
%qarm offormat/spec.rsfromlocale_aware_shell_escape(value)toQuotingStyle::SHELL_ESCAPE.always_quote(has_brace)pluslocale_aware_escape_name, so GNUprintf %qwraps a brace-bearing argument in single quotes. The handwritten shim has no such method.This is not the usual rename-straddle documented in the knowledge file, where the shim mirrors both spellings of one behaviour. Here bash disagrees with the new GNU behaviour outright, so forwarding the semantics would have silently changed a builtin's output — and the drift PR could never have repaired that, because the
open-prjob enforces a generated-only diff and handwrittenprintf.rsis out of its reach.Returning
selfis sound becauselocale_aware_escape_nameroutes every shell style to the bash-faithfulshell_quoteand never consults the flags carried on the style. The shim's variants stay unit-only for the same reason: it carries the names the vendored module uses, not uucore's quoting behaviour.Before / After
Before —
cargo build -p bashkiton the regenerated tree:After — same build, with upstream's new
%qarm spliced into the pinnedspec.rsto stand in for the regeneration:Behaviour is unchanged, and matches bash. Reference, bash 5.2.21:
bash printf %qa{ba\{ba\{b'a{b'a}ba\}ba\}b'a}b'{}\{\}\{\}'{}'a{b ca\{b\ ca\{b\ c'a{b c'Verified e2e: the pre-fix tree reproduces the CI error verbatim; with the fix plus upstream's arm spliced in,
cargo build -p bashkitsucceeds andbash_spec_tests+ the printf/format_support unit tests pass (19 + 23 tests). The splice was reverted; the pinnedgenerated/tree is untouched by this PR, so the drift PR can still own it.Risk
spec.rs, which is exactly the point.%q, the no-op becomes wrong.quoted_string_escapes_braces_the_way_bash_doespins the current bash output, so that would surface as a failing differential expectation rather than silently.Checklist
always_quote_does_not_change_how_braces_are_escaped(the flag is inert) andquoted_string_escapes_braces_the_way_bash_does(the resulting output is bash's, values taken from bash 5.2.21)printf %qoutput is byte-identical to beforeKnowledge:
knowledge/runtimes/coreutils-args-port.mdgains a Shim refuses upstream behaviour bash does not have section next to the existing rename-straddle rule.Generated by Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.