Skip to content

Add %{field} escape to -printf / -printfln / -fprintf / -fprintfln - #234

Merged
helly25 merged 1 commit into
mainfrom
feat/printf-field-escape
Jul 4, 2026
Merged

Add %{field} escape to -printf / -printfln / -fprintf / -fprintfln#234
helly25 merged 1 commit into
mainfrom
feat/printf-field-escape

Conversation

@helly25

@helly25 helly25 commented Jul 3, 2026

Copy link
Copy Markdown
Owner

-printf's format is a %-directive language and treats a bare {...} literally (printf formats legitimately contain braces, e.g. JSON), so the rich brace field vocabulary was unreachable from these per-branch actions - unlike the whole-run --format global. This bridges the gap with an xff %{NAME} / %{NAME:qualifier} escape.

%{...} renders through the same fields::Template engine as --format braces, so the full vocabulary comes along:

xff . -type f -printf '%{relpath}\t%{size:h}\t%{mtime:%Y-%m-%d}\n'
xff . -type l -printf '%f -> %{target}\n'
xff --define=B=/backup . -type f -printf '%{relpath} vs %{def.B}/%{relpath}\n'

%{relpath}, %{core}, %{suffix}, %{target}, %{def.NAME}, %{env.NAME}, %{capture.N}, time qualifiers, the s/PAT/REPL/ rewrite, path-component qualifiers - all work.

Rules

  • %% stays a literal percent; a bare {...} stays literal (find-compat, no breakage); an unterminated %{ is emitted literally, matching the field template's own lenient handling.
  • The escape is an xff extension inside otherwise find-native actions, so EnforceStyle rejects a %{...} format under --config=find (a plain %p format stays fine). -printfln / -fprintfln are xff already.
  • The symlink read backing %{target} is skipped unless the format actually uses %{.

FormatPrintf now takes the EvalContext (for captures/defines/env/target), like RenderExecArgv.

Self-doc + tests: -printf summary notes the escape; --help / --man / --markdown regenerate from the registry. run_test pins the escape rules (expand / %% / bare-literal / unterminated); parser_test pins the strict-find rejection; a dogfood xff_golden case pins the find(reject)-vs-xff(expand) divergence. Full bazel test //xff/... green (49); both clang-format versions + buildifier + pre-commit clean.

-printf's format is a % directive language and treats a bare `{...}` literally
(printf formats legitimately contain braces, e.g. JSON), so the rich brace field
vocabulary was unreachable from these per-branch actions (unlike the whole-run
--format global). Bridge it with an xff `%{NAME}` / `%{NAME:qualifier}` escape,
rendered by the same fields::Template engine as --format: %{relpath}, %{core},
%{suffix}, %{target}, %{def.NAME}, %{env.NAME}, %{size:h}, time qualifiers, the
s/// rewrite, etc.

Rules: %% stays a literal percent; a bare `{...}` stays literal (find-compat, no
breakage); an unterminated `%{` is emitted literally, matching the field template's
own lenient handling. The escape is an xff extension in otherwise find-native
actions, so EnforceStyle rejects a %{...} format under --config=find (plain %
formats stay fine); -printfln / -fprintfln are xff already. The symlink read that
backs %{target} is skipped unless the format actually uses %{.

FormatPrintf now takes the EvalContext (for captures/defines/env/target), like
RenderExecArgv. Self-doc: -printf summary notes the escape; --help / --man /
--markdown regenerate from the registry. Tests: run_test pins the escape rules
(expand, %%, bare-literal, unterminated); parser_test pins the strict-find
rejection; an xff_golden case pins the find(reject)-vs-xff(expand) divergence.
@helly25
helly25 enabled auto-merge (squash) July 3, 2026 23:42
@helly25
helly25 merged commit 8617de1 into main Jul 4, 2026
8 checks passed
@helly25
helly25 deleted the feat/printf-field-escape branch July 4, 2026 00:05
helly25 added a commit that referenced this pull request Jul 5, 2026
Re-pins the mbo git_override to the main commit merging helly25/mbo#234 (which added the
empty-time_format git-style header and ignore_missing_final_newline), and wires the two
xff-side bits:

- -diff now sets time_format="" so the unified/context header omits the per-file mtime
  (`--- a/one.txt`), making the output reproducible. The diff golden tests drop the
  mtime-strip sed (normalize) and the fixture drops its `touch` - the git-style header is
  verbatim, so the committed goldens match directly.
- --diff-ignore gains an `eofnl` token (-> mbo ignore_missing_final_newline): a file with
  and one without a final newline compare equal. No `lead`/`eol` token - leading whitespace
  is subsumed by `change`/`ws`, and CRLF-vs-LF by `trail` (a `\r` is trailing whitespace).

Tests: run_test + diff_test.sh gain eofnl cases (equal with the token, differ without);
the diff goldens pass with no normalization. `bazel test //xff/...` green (66 tests).
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.

1 participant