Skip to content

refactor(printf): dispatch -printf directives and escapes via constexpr LimitedMaps - #85

Merged
helly25 merged 1 commit into
mainfrom
feat/printf-dispatch
Jun 22, 2026
Merged

refactor(printf): dispatch -printf directives and escapes via constexpr LimitedMaps#85
helly25 merged 1 commit into
mainfrom
feat/printf-dispatch

Conversation

@helly25

@helly25 helly25 commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Converts FormatPrintf's two switch statements to constexpr dispatch tables, per the TODO that switch/case lists be alphabetical and constexpr-dispatched where possible.

What

  • kPrintfEscapes: constexpr LimitedMap<char, char> for the \ escapes (\n/\t/\r/\0/\\).
  • kPrintfDirectives: constexpr LimitedMap<char, PrintfDirective> for the % directives, where PrintfDirective = void(*)(std::string&, const Visit&) and each entry is a captureless lambda. Keyed alphabetically, looked up with .find() - the same pattern as the engine kDispatch for primaries.
  • FormatPrintf now looks up both tables; unknown directives/escapes still emit literally.

Behavior

Unchanged - the existing -printf tests (directives, escapes, owner directives) all pass on default + --config=clang --config=asan (12/12). clang-format + no-em-dash gates clean.

MatchesType / MatchesSize / TypeLetter are the same kind of char-keyed switch and are candidates for a follow-up sweep.

…pr LimitedMaps

Replaces FormatPrintf's two switch statements with constexpr mbo::container::MakeLimitedMap tables: kPrintfEscapes (char -> char) for the backslash escapes and kPrintfDirectives (char -> handler) for the % directives, keyed alphabetically and looked up with .find() - the same pattern engine/evaluate.cc's kDispatch uses for the primaries. The % handlers are captureless lambdas (void(std::string&, const Visit&)) so the table is constexpr-constructible.

Behavior is unchanged: the existing -printf tests pass, and unknown directives/escapes still emit literally. Addresses the TODO that switch/case lists be alphabetical and use constexpr dispatch where possible. MatchesType/MatchesSize/TypeLetter are candidates for a follow-up sweep.
@helly25
helly25 enabled auto-merge (squash) June 22, 2026 21:58
@helly25
helly25 merged commit 89e7ab9 into main Jun 22, 2026
5 checks passed
@helly25
helly25 deleted the feat/printf-dispatch branch June 22, 2026 22:05
helly25 added a commit that referenced this pull request Jul 2, 2026
-grep PATTERN is the line-output companion of the -rxc content predicate: for
each file it prints every content line matching an RE2 regex as path:line:text
(grep's piped form), and returns true iff it printed a line (grep-like exit, so
it composes with -o / -q). Non-regular, unreadable, and binary files yield
nothing, reusing the content predicates' ContentToSearch (8 KiB NUL sniff).

It is self-contained (its own pattern, pre-compiled like -rxc) and composes with
the full find predicate set -- xff -mtime -7 -size +1M -grep TODO -- which is the
value over piping to grep/rg: find-grade selection fused with the match output in
one walk. -content / -rxc stay untouched as file filters.

The -grep name, rejected earlier for a *matcher* predicate (grep's regex-flavor
baggage), is unambiguous for a pattern-less-flavor output action; the flavor will
live in --regextype (#85), not the name.

xff-style extension (--config=find rejects it). Deferred to follow-ups: the
-grep=FORMAT custom template with {line}/{text} fields, --regextype=EXACT|RE2
mode selection, {match}/{column} (-o), context (-A/-B/-C), and count (-c, #92).

Tests: evaluate_test (emit shape + line numbers, regex not literal, no-match is
false+silent, binary skip); run_test (path:line:text across the walk); grep_test.sh
end-to-end (regex, binary skip, composition, find-style rejection). Self-doc:
registry descriptor + kHelpText + design.md. bazel test //... green (39).
helly25 added a commit that referenced this pull request Jul 2, 2026
The first -grep enhancement and the answer to "exact vs regex": a --regextype
global selects how -grep reads its pattern. RE2 (the default) is the regex engine;
EXACT matches the pattern as a literal substring per line (so '.' is a literal dot
and an unparseable-as-regex pattern like 'foo(bar' is a fine literal). The
grep-flavor MATCH and the PCRE engine are reserved for #85 and refused now with a
clear usage error (exit 2), as is any unknown value.

Wiring: run.cc ResolveGrepLiteral (RE2->false, EXACT->true, MATCH/PCRE/unknown->
InvalidArgument, refused before the walk) threads EvalContext::grep_literal;
EvalGrep branches to a literal StrContains matcher instead of the pre-compiled RE2
one, so EXACT never depends on the pattern being a valid regex. -content/-rxc and
find's own -regextype primary are untouched (dash-count convention: --regextype is
the xff global superset; #85 unifies them).

Self-doc: --regextype in globals.cc + kHelpText; design.md -grep note updated to
"shipped RE2|EXACT". Tests: evaluate_test (EXACT literal dot + regex-metachar-as-
literal), run_test (EXACT vs RE2 default, reserved value is a usage error),
grep_test.sh e2e. bazel test //... green (39).
helly25 added a commit that referenced this pull request Jul 6, 2026
Record the architecture decision that heavy/special libs (libarchive #83, pcre2 #85, future ones)
are build-time composable extras behind Bazel flags: default lean core (RE2, no archive), an
extended binary composed from the same tree via bool_flag + config_setting + select() + -DXFF_WITH_*
defines, a .bazelrc --config=full convenience, runtime feature-reporting + graceful "not built in"
errors, and a NOTICE assembled from the enabled set. Distinct from the #73 --feature runtime gates.

Also record the #85 pcre2 finding + plan: pcre2 is in the BCR (upstream-maintained, 10.47), a clean
BSD-3-Clause dep (no new license type); add a PCRE2-backed regex::Matcher gated by the //xff:pcre
extra, RE2 stays the default, PCRE2 opt-in via -regextype with match/backtrack limits (ReDoS).
helly25 added a commit that referenced this pull request Jul 6, 2026
…time extras (#276)

* docs(TODO): record #83 archive decision - libarchive + NOTICE plan

Archive diving (--archive) will use libarchive via its BCR module, as a read-only vfs::FileSystem
backend (entries as virtual paths, so the predicate/action set incl. -grep works unchanged). Two
build variants planned: minimal (tar+gz+bz2) and extended (+xz/zstd/zip). NOTICE obligations are all
permissive: net-new types BSD-2-Clause / Zlib / bzip2-1.0.6 / 0BSD; the dual-licensed zstd and
mbedtls are pinned to their permissive arms (BSD-3 / Apache-2.0), lz4's library (BSD-2) not its GPL
CLI, so no copyleft. Third-party notices maintained + scaled to the enabled codec set.

* docs(TODO): composable build-time extras + pcre2 (#85) plan

Record the architecture decision that heavy/special libs (libarchive #83, pcre2 #85, future ones)
are build-time composable extras behind Bazel flags: default lean core (RE2, no archive), an
extended binary composed from the same tree via bool_flag + config_setting + select() + -DXFF_WITH_*
defines, a .bazelrc --config=full convenience, runtime feature-reporting + graceful "not built in"
errors, and a NOTICE assembled from the enabled set. Distinct from the #73 --feature runtime gates.

Also record the #85 pcre2 finding + plan: pcre2 is in the BCR (upstream-maintained, 10.47), a clean
BSD-3-Clause dep (no new license type); add a PCRE2-backed regex::Matcher gated by the //xff:pcre
extra, RE2 stays the default, PCRE2 opt-in via -regextype with match/backtrack limits (ReDoS).
helly25 added a commit that referenced this pull request Jul 8, 2026
…s to #83/#85

#297 is the code-SOT self-registration license lib (core notices, LICENSE
genrule, drift guard). The dual binary (xff_minimal/xff_full via alias, manual),
the stub/real xff/archive + xff/pcre modules (@libarchive/@pcre2, self-register
their notices), the full-fat NOTICE + full-config drift check, and the CI dual
build are deferred to #83/#85 with the agreed design captured. Under self-
registration a minimal binary's core-only NOTICE is correct; the extras' notices
arrive with their real modules.
helly25 added a commit that referenced this pull request Jul 8, 2026
* feat(license): code-SOT notice lib with self-registration (#115 follow-up)

Invert the notice handling per the agreed design: the code is the SOT and the
repo files are checked against it (not the reverse).

- New xff/license lib: a Notice registry + a Registrar helper. Components
  self-register - core deps (Abseil/RE2/mbo) from license.cc (always linked);
  a build-extra will register from its own TU (so it appears exactly when
  linked). Notices() returns the set sorted by component (deterministic across
  static-init order). NoticeText() assembles the xff attribution header + the
  sorted components; this is the SOT for the repo NOTICE.
- LICENSE stays canonical: LicenseText() is generated from //:LICENSE via a
  genrule (byte-exact, no leading-newline artifact) and embedded so a single
  binary reproduces it.
- --help=notice / --help=license dump the compiled-in subset from the lib.
  Retire #296's files->.cc genrule and xff/cli/notices.*; help_cc now deps
  //xff/license:license_cc.
- license_test guards drift: the committed NOTICE == NoticeText() and LICENSE
  == LicenseText() (read from runfiles), plus the core deps are present + sorted.
  (No extra is linked yet, so NoticeText is the full set; #83 makes the compare
  full-fat once libarchive self-registers.)

TODO(license.h): move to C++23 #embed + reproduce each dependency's own license
file verbatim (RE2 BSD-3 etc.), not just Apache + the manifest.

* chore: spell the author name Boerger (international) not Börger

The copyright name lives in the license SOT (NoticeText in xff/license/license.cc)
and the committed NOTICE; change both to the international spelling 'Boerger'.

* docs(todo): record license self-reg shipped + defer dual-binary/extras to #83/#85

#297 is the code-SOT self-registration license lib (core notices, LICENSE
genrule, drift guard). The dual binary (xff_minimal/xff_full via alias, manual),
the stub/real xff/archive + xff/pcre modules (@libarchive/@pcre2, self-register
their notices), the full-fat NOTICE + full-config drift check, and the CI dual
build are deferred to #83/#85 with the agreed design captured. Under self-
registration a minimal binary's core-only NOTICE is correct; the extras' notices
arrive with their real modules.
helly25 added a commit that referenced this pull request Jul 9, 2026
… Grammar (#85) (#308)

Prep for the PCRE2 backend. Matcher no longer wraps RE2 directly: it owns a
RegexBackend (backend.h) behind a unique_ptr and forwards each op, and Compile
takes a Grammar {kRe2, kPcre2} (default kRe2). The RE2 code moves into a private
Re2Backend; the public regex.h no longer includes re2/re2.h.

kPcre2 currently returns an Unimplemented error ("PCRE2 not built into this
binary") - which is exactly the future lean-build behavior. This backend seam is
what the dual binary will gate on (lean = RE2 only + PCRE2 stub, full = real
PCRE2) and what the runtime -regextype selection dispatches through, with no API
change either way.

No behavior change: kRe2 is the default, all existing Matcher callers are
unaffected (Compile's third arg is defaulted).

Test: regex_test gains the explicit-kRe2 default and the kPcre2 Unimplemented-stub
cases; full //xff/... suite green (the regex.h header change touches nothing else).
helly25 added a commit that referenced this pull request Jul 9, 2026
The self-registration seam for the composable-extras architecture (mirrors the
license Notice registrars). backend.h gains a Pcre2Factory typedef, a
RegisterPcre2Backend entry point, and a Pcre2Registrar for static-init
self-registration. regex.cc holds the single factory slot (a Meyers static, so a
registrar in another TU can write it during static init); Compile(kPcre2) now
consults the slot -- registered (full build) -> real backend, unregistered (lean
build) -> the same Unimplemented "not built in" error, never a silent RE2
fallback. regex.h exposes Pcre2Available() for the help presence line.

Linkage is presence: the real PCRE2 backend (built only into the full binary,
from its own removable target under third_party/pcre2) self-registers via a
Pcre2Registrar; a lean build links no such TU, so nothing registers.

No behavior change in this (lean) build: nothing is registered, so
Pcre2Available() is false and -regextype=pcre still errors.

Test: regex_test asserts Pcre2Available() is false and Compile(kPcre2) is
Unimplemented with no backend registered.
helly25 added a commit that referenced this pull request Jul 9, 2026
…pointer (#85) (#310)

A std::function is the idiomatic type-erased callable: it also accepts capturing
lambdas and functors, not just plain functions, so the future third_party/pcre2
registrant is not constrained to a free function. The factory is invoked once per
pattern compile, so type-erasure cost is irrelevant. The slot becomes a default-
empty std::function (moved on register), Compile(kPcre2) tests it with !factory,
and Pcre2Available() is static_cast<bool>.

Test: regex_test unchanged and green (Pcre2Available() false / kPcre2
Unimplemented with nothing registered).
helly25 added a commit that referenced this pull request Jul 9, 2026
…ilt-in error (#85) (#311)

--regextype now accepts PCRE2 alongside RE2/EXACT. Because PCRE2 is a build-time
extra, ResolveGrepLiteral (which runs unconditionally, so it guards the whole run,
not just -grep) errors when --regextype=PCRE2 is used on a binary that does not
link the backend (regex::Pcre2Available() is false) - a clean usage error (exit
2), never a silent RE2 fallback. MATCH stays reserved; unknown values still error.
The actual grammar threading to the compile sites lands with the real backend
(the threading is dead code until a backend registers, and Pcre2Available() is
only true once that ships). --regextype display/summary updated to RE2|PCRE2|EXACT.

Also records two design decisions in TODO.md: #83 archive-mode scope (recurse into
any archive found, uniform archive==directory, opt-in, read-only) and #121 (a
future shell-glob third grammar on the RegexBackend abstraction).

Test: grep_test + run_test assert --regextype=PCRE2 is a usage error 'not built
into this binary' on this lean build; MATCH stays reserved; RE2/EXACT unchanged.
helly25 added a commit that referenced this pull request Jul 9, 2026
full_binary_test data-deps xff_full, so leaving it non-manual meant
`bazel test //...` built xff_full anyway - defeating xff_full's `manual`
tag (and, once #85 links pcre2, it would drag the extras into the lean
cells). Anything depending on the manual full binary must be manual too.
It now runs explicitly (`bazel test //xff/cli:full_binary_test`, verified
green) or in the full CI cell; the argv[0] `_full`-strip stays covered in
default CI by config_test.
helly25 added a commit that referenced this pull request Jul 10, 2026
…flag (#85, #115) (#312)

* Add composable-extras scaffolding: xff_full dual binary + //xff:pcre flag (#85, #115)

The PCRE2 backend (#85) and archive diving (#83) are composable build-time
extras: the stock binary stays a lean core, and a full binary linking the
extras is built from the same tree. This lands the Bazel + argv[0] scaffolding
(the real PCRE2 backend follows in the next PR).

- //xff:pcre bool_flag + :pcre_enabled config_setting (mirrors //xff:archive),
  default off, so an extra links only when turned on.
- Dual binary in //xff/cli: `xff` (lean, what //... and every test builds) and
  `xff_full` (tags=["manual"], same core + a select() on its deps for the extra
  backends). No alias: an alias's runfile takes the resolved target's basename,
  which would break every bashtest's hardcoded xff/cli/xff lookup; two named
  cc_binary targets keep the `xff` artifact named `xff` (zero test churn) and
  let the user pick which binary to run.
- .bazelrc `--config=full` turns the extras on; `--config=full --//xff:pcre=false`
  drops one from an otherwise-full build.
- DefaultStyleForProgram strips a trailing `_full` suffix, so `xff_full` resolves
  to the xff style (and `find_full` -> find, `rg_full` -> rg) rather than falling
  through to the xff default and silently losing the base semantics.
- full_binary_test.sh drives the real xff_full: it resolves to the xff style
  (accepts the xff-only -grep) and, without the PCRE2 extra linked, still rejects
  --regextype=PCRE2 with a usage error.

Realigns the TODO.md extras section (define/#ifdef -> self-registration; the
alias sketch -> the shipped two-binary model) and marks #115 scaffolding done.

* Prefix extras build flags with xff_; share main() via a cc_library

Two follow-ups on the composable-extras scaffolding, folded into the same PR:

- Prefix the build-setting flags with the `xff_` package prefix, matching the
  library-flag convention in STYLE_CPP.md: `//xff:archive`/`archive_enabled` ->
  `//xff:xff_archive`/`xff_archive_enabled`, and the new pcre pair likewise
  (`//xff:xff_pcre`/`xff_pcre_enabled`). Updates .bazelrc + all comments/docs.
- Replace the `_XFF_CORE_DEPS` Starlark list with a real `//xff/cli:main_cc`
  cc_library holding main.cc + the core deps; `xff` and `xff_full` are now thin
  binaries over it (no srcs of their own). main.cc compiles once, neither binary
  carries its own main(), and xff_full differs only by the extras it adds - so
  they cannot drift. Verified main() links from the library and self-registration
  (license notices) still fires through it.

* Tag full_binary_test manual (it depends on the manual xff_full)

full_binary_test data-deps xff_full, so leaving it non-manual meant
`bazel test //...` built xff_full anyway - defeating xff_full's `manual`
tag (and, once #85 links pcre2, it would drag the extras into the lean
cells). Anything depending on the manual full binary must be manual too.
It now runs explicitly (`bazel test //xff/cli:full_binary_test`, verified
green) or in the full CI cell; the argv[0] `_full`-strip stays covered in
default CI by config_test.

* Rename the extras bazel config full -> xff_full

Match the config name to the binary it builds, and carry the xff_ prefix
consistently with the flags: `bazel build --config=xff_full //xff/cli:xff_full`.
Updates .bazelrc (common:xff_full) and every --config=full / build:full
reference in the BUILD files, TODO.md, and full_binary_test.sh.
helly25 added a commit that referenced this pull request Jul 10, 2026
…hers (#85) (#313)

Resolve the regex grammar once from --regextype at parse time and carry it on
Command.grammar, so every pattern matcher (-regex/-iregex/-rxc/-irxc/-grep and
the -capture extraction regex) compiles under it, and the ApplyCaseMode
case-insensitive recompile reuses it. Previously the parser always compiled RE2
regardless of --regextype.

- parser: GrammarFromGlobals maps --regextype (PCRE2 -> kPcre2, else kRe2, last
  wins); ExprParser carries grammar_ and threads it through MakePredicate ->
  CompileNodeRegex -> Matcher::Compile; ApplyCaseModeToNode takes the grammar too.
- ast: Command gains a `grammar` field (default kRe2).

This is the plumbing for #85's PCRE2 backend. It is safe to land before the
backend: with no backend linked, Matcher::Compile(kPcre2) is Unimplemented and
run.cc's ResolveGrepLiteral already rejects --regextype=PCRE2 (exit 2) before the
walk, so the grammar is RE2 in every reachable path today. GrammarFromGlobals is
deliberately lenient (unknown/unavailable -> RE2); ResolveGrepLiteral stays the
single validating reader. parser_test asserts Command.grammar tracks --regextype.
helly25 added a commit that referenced this pull request Jul 10, 2026
… literal path (#85) (#314)

EXACT becomes a first-class match engine (Grammar::kExact) behind the RegexBackend
seam, not a -grep-only bool - the first non-RE2 engine, proving the multi-engine
architecture with a trivial core (always-linked, no dependency) backend.

- regex: ExactBackend implements RegexBackend via std::string comparison -
  FullMatch = equality, PartialMatch = substring, FindFirst = first occurrence span,
  Rewrite = literal find/replace (no backreferences); case_insensitive folds ASCII
  case on both sides. Compile(kExact) never fails (no pattern to compile).
- parser: GrammarFromGlobals maps --regextype=EXACT -> kExact, so EXACT now reaches
  every pattern predicate (-regex/-rxc/-grep), not just -grep.
- engine: unify -grep. The old ctx.grep_literal StrContains path is exactly what
  ExactBackend.PartialMatch/FindFirst do, so -grep now runs through the pre-compiled
  matcher like -rxc; drop the grep_literal EvalContext field. Behavior is preserved
  (the existing EXACT -grep test still passes) and EXACT now respects case mode
  uniformly (the old StrContains path ignored -i/smart-case).
- run: ResolveGrepLiteral (returned the literal bool) becomes ValidateRegextype
  (validates the selector only; PCRE2-not-built / MATCH-reserved / unknown are still
  usage errors before the walk). The grammar is resolved by the parser.
- help: --regextype summary reframed as "match engine: RE2 / EXACT / PCRE2".

Tests: regex_test (kExact ops + ci + literal span), parser_test (EXACT -> kExact),
evaluate_test (-grep and -rxc under EXACT). bazel test //xff/... -> 72/72.
helly25 added a commit that referenced this pull request Jul 10, 2026
#121) (#315)

FnmatchBackend is the second core engine behind the RegexBackend seam: a flat
POSIX shell wildcard (`*`/`?`/`[...]`, where `*` matches any character including
`/` - no FNM_PATHNAME, i.e. the -name/-path matching offered as a --regextype).

- regex: Grammar::kFnmatch + FnmatchBackend. fnmatch is a whole-string test, so
  FullMatch runs it anchored; PartialMatch wraps the pattern in `*...*` to match
  anywhere (`**` collapses to `*` in POSIX fnmatch, so the always-wrap is safe);
  FindFirst reports the whole text as the span (no sub-span in fnmatch);
  FullMatchCaptures is the whole match only; Rewrite is a no-op (a glob has no
  rewrite semantics). case_insensitive sets FNM_CASEFOLD (the _GNU_SOURCE guard
  mirrors the evaluator). No pattern to compile, so Compile(kFnmatch) never fails.
- parser: GrammarFromGlobals maps --regextype=FNMATCH -> kFnmatch, so it reaches
  every pattern predicate (-regex/-rxc/-grep).
- run: ValidateRegextype accepts FNMATCH (a core engine, always available).
- help: --regextype summary lists FNMATCH (glob).

Tests: regex_test (whole-string wildcard, `*...*` PartialMatch + whole-text span,
FNM_CASEFOLD), parser_test (FNMATCH -> kFnmatch), evaluate_test (-rxc under
FNMATCH). bazel test //xff/... -> 72/72.
helly25 added a commit that referenced this pull request Jul 10, 2026
#85, #121) (#316)

GLOB is the path-segment-aware shell glob (`*`/`?` stop at `/`, `**` crosses
directories - the shell / gitignore semantics), the fourth core engine behind the
RegexBackend seam. It reuses the escape-aware glob->RE2 translation already in the
gitignore engine, now factored into a shared user-lib.

- new //xff/glob: `glob::GlobToRegex(pattern)` translates a shell glob to an RE2
  pattern (`*`->`[^/]*`, `?`->`[^/]`, `**` segment->cross-directory, `[!]`->`[^]`,
  RE2 metacharacters + `\`-escapes handled). Extracted verbatim from ignore.cc.
- ignore: now calls glob::GlobToRegex (behavior unchanged; ignore_test still green).
- regex: Grammar::kGlob compiles the translated pattern as RE2 and reuses Re2Backend,
  so FullMatch/PartialMatch/FindFirst (a real span)/Rewrite all come from RE2 - no
  fnmatch whole-string limitation. A shared `compile_re2` lambda backs kRe2 and kGlob.
- parser/run/help: --regextype=GLOB -> kGlob (reaches -regex/-rxc/-grep); a core
  engine, always available; the summary lists all five engines.

Tests: glob_test (the translation), regex_test (path-aware `*`/`**`, RE2-backed span
+ partial), parser_test (GLOB -> kGlob), evaluate_test (-rxc path-aware). //xff/... -> 73/73.
helly25 added a commit that referenced this pull request Jul 10, 2026
…#85) (#317)

The last regextype engine and the first composable build extra: a real PCRE2
backend that only //xff/cli:xff_full links, completing #85.

- third_party/pcre2/ (removable dir): Pcre2Backend implements xff/regex's
  RegexBackend via the PCRE2 C API (compile / match / ovector / substitute).
  FullMatch is ANCHORED|ENDANCHORED (whole-string); FindFirst/captures read the
  ovector; Rewrite translates the RE2 `\1` contract to PCRE2 `$1`; case_insensitive
  is PCRE2_CASELESS. ReDoS-guarded by a match + depth limit on the match context.
  Each match allocates its own match_data, so a shared const backend is thread-safe.
- Self-registration (alwayslink), exactly like the core engines: a Pcre2Registrar
  hands the factory to xff/regex (Pcre2Available() flips true, Matcher::Compile(kPcre2)
  works), and a license::Registrar adds the BSD-3 notice. The core never references PCRE2.
- MODULE.bazel: bazel_dep(pcre2 10.47). Linked into xff_full via
  select({"//xff:xff_pcre_enabled": ["//third_party/pcre2:pcre2_backend"]}); both the
  backend lib and its test are `manual`, so a plain //... build never fetches @pcre2.
- backend.h becomes an exported header of //xff/regex (the extension seam), and
  //xff/regex + //xff/license are now visible to //third_party.
- CI: a `full` cell runs the whole suite + the manual full targets under
  --config=xff_full (the only cell that links the extras), wired into the `done` gate.

Tests: pcre2_backend_test (unit - backreferences, lookahead, captures, RE2-style
rewrite, anchoring, case-fold, invalid-pattern), and full_binary_test is now
config-aware (asserts PCRE2 works when linked, errors when not). Lean //... stays
73/73 and never touches @pcre2; --config=xff_full is 75/75.
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