From 367e8db2909333b864619c76e511fb1bf97355da Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Sun, 9 Aug 2026 11:06:26 -0700 Subject: [PATCH 1/2] parse: Make REG_LITERAL match the empty pattern With REG_LITERAL, compiling an empty pattern succeeds but the resulting regex never matches anything: tre_regcomp(&re, "", REG_EXTENDED | REG_LITERAL); tre_regexec(&re, "abc", 1, m, 0); /* REG_NOMATCH; expected match */ The PARSE_ATOM empty-expression branch is skipped in literal mode, so an empty pattern falls through without producing an atom. Emit an EMPTY literal node in that case, matching the behavior of the non-literal parser (and of an empty pattern in other regex engines). A variant of this fix has been carried in GNU R's vendored copy of TRE (where it backs fixed = TRUE matching) since 2009. --- lib/tre-parse.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/tre-parse.c b/lib/tre-parse.c index fa20655..edf97c7 100644 --- a/lib/tre-parse.c +++ b/lib/tre-parse.c @@ -1645,6 +1645,22 @@ tre_parse(tre_parse_ctx_t *ctx) break; } +#ifdef REG_LITERAL + /* In literal mode the branch above is skipped, so an empty + pattern would otherwise fall through without producing an + atom. Emit an empty expression, which matches the empty + string. */ + if ((ctx->cflags & REG_LITERAL) && ctx->re >= ctx->re_end) + { + DPRINT(("tre_parse: literal empty: '%.*" STRF "'\n", + REST(ctx->re))); + result = tre_ast_new_literal(ctx->mem, EMPTY, -1); + if (!result) + return REG_ESPACE; + break; + } +#endif /* REG_LITERAL */ + DPRINT(("tre_parse: literal: '%.*" STRF "'\n", REST(ctx->re))); /* Note that we can't use an tre_isalpha() test here, since there From 0f5673d6184c5bbf23329f7df820ecbce4989545 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Sun, 9 Aug 2026 11:50:09 -0700 Subject: [PATCH 2/2] tests: Add REG_LITERAL cases to retest The suite had no REG_LITERAL coverage; these cases cover the empty-pattern fix in this branch and literal handling of metacharacters. --- tests/retest.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/retest.c b/tests/retest.c index 16ccb40..e3fe8ef 100644 --- a/tests/retest.c +++ b/tests/retest.c @@ -633,6 +633,23 @@ main(int argc, char **argv) test_exec("xxxxxx", 0, REG_NOMATCH); #endif + /* + * Literal (REG_LITERAL) patterns. + */ + + /* An empty literal pattern matches the empty string. */ + test_comp("", REG_EXTENDED | REG_LITERAL, 0); + test_exec("abc", 0, REG_OK, 0, 0, END); + test_exec("", 0, REG_OK, 0, 0, END); + + /* Metacharacters lose their meaning in literal mode. */ + test_comp("a.c", REG_EXTENDED | REG_LITERAL, 0); + test_exec("xa.cy", 0, REG_OK, 1, 4, END); + test_exec("abc", 0, REG_NOMATCH); + test_comp("(a|b)*", REG_EXTENDED | REG_LITERAL, 0); + test_exec("x(a|b)*y", 0, REG_OK, 1, 7, END); + test_exec("ababab", 0, REG_NOMATCH); + #ifdef TRE_APPROX /* * Approximate matching tests.