/e: Set NUL (\0) as newline (#4862)

* Add compile context arg to `rz_regex_new()`
* `/e`: Set NUL (`\0`) as newline
This commit is contained in:
Khairul Azhar Kasmiran 2025-01-25 19:07:34 +08:00 committed by GitHub
parent 3520054120
commit 37517f7a2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 81 additions and 31 deletions

View file

@ -1538,7 +1538,7 @@ RZ_API void rz_asm_compile_token_patterns(RZ_INOUT RzPVector /*<RzAsmTokenPatter
rz_pvector_foreach (patterns, it) {
RzAsmTokenPattern *pat = *it;
if (!pat->regex) {
pat->regex = rz_regex_new(pat->pattern, RZ_REGEX_EXTENDED, 0);
pat->regex = rz_regex_new(pat->pattern, RZ_REGEX_EXTENDED, 0, NULL);
if (!pat->regex) {
RZ_LOG_WARN("Did not compile regex pattern %s.\n", pat->pattern);
rz_warn_if_reached();

View file

@ -103,7 +103,7 @@ static char *find_next_number(char *str) {
// (e.g. case labels like: case.<switch-address>.<case-number>).
// See: https://github.com/rizinorg/rizin/issues/4238 for more details of this problem.
"(([^\\w.*]|^)(?<number>(0x[a-fA-F0-9]+)|\\d+))");
RzRegex *re = rz_regex_new(search, RZ_REGEX_EXTENDED, RZ_REGEX_DEFAULT);
RzRegex *re = rz_regex_new(search, RZ_REGEX_EXTENDED, RZ_REGEX_DEFAULT, NULL);
RzPVector *match = rz_regex_match_first(re, str, RZ_REGEX_ZERO_TERMINATED, 0, RZ_REGEX_DEFAULT);
if (rz_pvector_empty(match)) {
rz_pvector_free(match);
@ -152,7 +152,7 @@ static bool is_lea(const char *asm_str) {
if (!colored) {
return strlen(asm_str) > 4 && rz_str_startswith_icase(asm_str, "lea") && asm_str[3] == ' ';
}
RzRegex *re = rz_regex_new("(^\x1b\\[\\d{1,3}mlea\x1b\\[0m.+)", RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0);
RzRegex *re = rz_regex_new("(^\x1b\\[\\d{1,3}mlea\x1b\\[0m.+)", RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0, NULL);
if (!re) {
return false;
}

View file

@ -268,7 +268,7 @@ static char *subvar_stack(RzParse *p, RzAnalysisOp *op, RZ_NULLABLE RzAnalysisFu
return tstr;
}
RzRegex *var_re = rz_regex_new(re_str, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0);
RzRegex *var_re = rz_regex_new(re_str, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0, NULL);
if (!var_re) {
return tstr;
}

View file

@ -169,7 +169,7 @@ static char *subvar_stack(RzParse *p, RzAnalysisOp *op, RZ_NULLABLE RzAnalysisFu
group_idx_addend = 3;
}
RzRegex *var_re = rz_regex_new(re_str, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0);
RzRegex *var_re = rz_regex_new(re_str, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0, NULL);
if (!var_re) {
return tstr;
}

View file

@ -318,7 +318,7 @@ static char *subvar_stack(RzParse *p, RzAnalysisOp *op, RZ_NULLABLE RzAnalysisFu
group_idx_addend = 3;
}
RzRegex *var_re = rz_regex_new(re_str, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0);
RzRegex *var_re = rz_regex_new(re_str, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0, NULL);
if (!var_re) {
return tstr;
}

View file

@ -126,7 +126,7 @@ RZ_API int rz_cons_less_str(const char *str, const char *exitkeys) {
if (rx) {
rz_regex_free(rx);
}
rx = rz_regex_new(sreg, RZ_REGEX_EXTENDED | RZ_REGEX_MULTILINE, 0);
rx = rz_regex_new(sreg, RZ_REGEX_EXTENDED | RZ_REGEX_MULTILINE, 0, NULL);
} else { /* we got an empty string */
from = pager_next_match(from, mla, lines_count);
break;

View file

@ -416,7 +416,7 @@ RZ_API RzList /*<RzCoreAsmHit *>*/ *rz_core_asm_strsearch(RzCore *core, const ch
} else if (!regexp) {
matches = strstr(opst, tokens[matchcount]) != NULL;
} else {
rx = rz_regex_new(tokens[matchcount], RZ_REGEX_EXTENDED, 0);
rx = rz_regex_new(tokens[matchcount], RZ_REGEX_EXTENDED, 0, NULL);
RzPVector *tmp_m = rz_regex_match_first(rx, opst, RZ_REGEX_ZERO_TERMINATED, 0, RZ_REGEX_DEFAULT);
matches = (!rz_pvector_empty(tmp_m) && tmp_m != NULL) ? 1 : 0;
rz_regex_free(rx);

View file

@ -208,7 +208,7 @@ RZ_API double GH(rz_get_glibc_version)(RzCore *core, const char *libc_path, ut8
}
const char *pattern = "release version (\\d.\\d\\d)";
RzRegex *re = rz_regex_new(pattern, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0);
RzRegex *re = rz_regex_new(pattern, RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0, NULL);
if (!re) {
return version;
}
@ -406,7 +406,7 @@ static bool GH(is_tcache)(RzCore *core) {
if (rz_config_get_b(core->config, "cfg.debug")) {
RzListIter *iter;
rz_debug_map_sync(core->dbg);
RzRegex *re = rz_regex_new(".*libc[.-]", RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0);
RzRegex *re = rz_regex_new(".*libc[.-]", RZ_REGEX_EXTENDED | RZ_REGEX_CASELESS, 0, NULL);
rz_list_foreach (core->dbg->maps, iter, map) {
// In case the binary is named *libc-*
if (strncmp(map->name, core->bin->file, strlen(map->name)) == 0) {

View file

@ -3575,7 +3575,7 @@ static RZ_OWN char *screen_bottom_address(RzCore *core) {
// capture the address from the line at the bottom
char *regex_str = ((RzCoreVisual *)core->visual)->printidx == RZ_CORE_VISUAL_MODE_CD ? "[0-9abcdefABCDEF]+" : "0x[0-9ABCDEFabcdef]+";
RzRegex *re = rz_regex_new(regex_str, RZ_REGEX_EXTENDED, 0);
RzRegex *re = rz_regex_new(regex_str, RZ_REGEX_EXTENDED, 0, NULL);
RzPVector *matches = rz_regex_match_all_not_grouped(re, lastline, RZ_REGEX_ZERO_TERMINATED, 0, RZ_REGEX_DEFAULT);
if (!matches || rz_pvector_empty(matches)) {
goto exit;

View file

@ -37,6 +37,7 @@ typedef size_t RzRegexSize; ///< Size of a text or regex. This is the size measu
typedef ut32 RzRegexFlags; ///< Regex flag bits.
typedef uint8_t *RzRegexPattern; ///< A regex pattern string.
typedef void RzRegex; ///< A regex expression.
typedef void RzRegexCompContext; ///< A PCRE2 compile context.
typedef struct {
RzRegexSize group_idx; ///< Index of the group. Used to determine name if any was given.
@ -46,7 +47,8 @@ typedef struct {
typedef void RzRegexMatchData; ///< PCRE2 internal match data type
RZ_API RZ_OWN RzRegex *rz_regex_new(RZ_NONNULL const char *pattern, RzRegexFlags cflags, RzRegexFlags jflags);
RZ_API RZ_OWN RzRegex *rz_regex_new(RZ_NONNULL const char *pattern, RzRegexFlags cflags, RzRegexFlags jflags,
RzRegexCompContext *ccontext);
RZ_API void rz_regex_free(RZ_OWN RzRegex *regex);
RZ_API void rz_regex_error_msg(RzRegexStatus errcode, RZ_OUT char *errbuf, RzRegexSize errbuf_size);
RZ_API const ut8 *rz_regex_get_match_name(RZ_NONNULL const RzRegex *regex, ut32 name_idx);
@ -82,5 +84,8 @@ RZ_API RzRegexSize rz_regex_find(RZ_NONNULL const char *pattern, RZ_NONNULL RZ_B
RZ_API RZ_OWN RzStrBuf *rz_regex_full_match_str(RZ_NONNULL const char *pattern, RZ_NONNULL const char *text,
RzRegexSize text_size,
RzRegexFlags cflags, RzRegexFlags mflags, RZ_NONNULL const char *separator);
RZ_API RZ_OWN RzRegexCompContext *rz_regex_compile_context_new();
RZ_API void rz_regex_compile_context_free(RzRegexCompContext *ccontext);
RZ_API void rz_regex_set_nul_as_newline(RZ_NONNULL RzRegexCompContext *ccontext);
#endif /* RZ_REGEX_H */

View file

@ -278,7 +278,7 @@ static int check_fmt(RzMagic *ms, struct rz_magic *m) {
return 0;
}
RzRegex *re = rz_regex_new("%[-0-9\\.]*s", RZ_REGEX_EXTENDED, 0);
RzRegex *re = rz_regex_new("%[-0-9\\.]*s", RZ_REGEX_EXTENDED, 0, NULL);
if (!re) {
return -1;
}
@ -1413,7 +1413,7 @@ static int magiccheck(RzMagic *ms, struct rz_magic *m) {
RzRegex *rx = rz_regex_new(m->value.s,
RZ_REGEX_EXTENDED |
((m->str_flags & STRING_IGNORE_CASE) ? RZ_REGEX_CASELESS : 0),
0);
0, NULL);
if (!rx) {
return -1;
}

View file

@ -16,6 +16,8 @@ RZ_API int rz_search_regexp_update(RzSearch *s, ut64 from, const ut8 *buf, int l
RzRegex *compiled = NULL;
const int old_nhits = s->nhits;
int ret = 0;
RzRegexCompContext *ccontext = rz_regex_compile_context_new();
rz_regex_set_nul_as_newline(ccontext);
rz_list_foreach (s->kws, iter, kw) {
int cflags = RZ_REGEX_EXTENDED;
@ -24,7 +26,7 @@ RZ_API int rz_search_regexp_update(RzSearch *s, ut64 from, const ut8 *buf, int l
cflags |= RZ_REGEX_CASELESS;
}
compiled = rz_regex_new((char *)kw->bin_keyword, cflags, 0);
compiled = rz_regex_new((char *)kw->bin_keyword, cflags, 0, ccontext);
if (!compiled) {
eprintf("Cannot compile '%s' regexp\n", kw->bin_keyword);
return -1;
@ -50,6 +52,7 @@ RZ_API int rz_search_regexp_update(RzSearch *s, ut64 from, const ut8 *buf, int l
}
beach:
rz_regex_compile_context_free(ccontext);
rz_regex_free(compiled);
if (!ret) {
ret = s->nhits - old_nhits;

View file

@ -13,7 +13,7 @@
#include <rz_util.h>
typedef pcre2_general_context RzRegexGeneralContext; ///< General context.
typedef pcre2_compile_context RzRegexCompContext; ///< The context for compiling.
// typedef pcre2_compile_context RzRegexCompContext; ///< The context for compiling.
typedef pcre2_match_context RzRegexMatchContext; ///< The context for matching.
typedef struct {
@ -38,10 +38,12 @@ static void print_pcre2_err(RZ_NULLABLE const char *pattern, RzRegexStatus err_n
* \param jflags The compilation flags for the JIT compiler.
* You can pass RZ_REGEX_JIT_PARTIAL_SOFT or RZ_REGEX_JIT_PARTIAL_HARD if you
* intend to use the pattern for partial matching. Otherwise set it to 0.
* \param ccontext A compile context or NULL.
*
* \return The compiled regex or NULL in case of failure.
*/
RZ_API RZ_OWN RzRegex *rz_regex_new(RZ_NONNULL const char *pattern, RzRegexFlags cflags, RzRegexFlags jflags) {
RZ_API RZ_OWN RzRegex *rz_regex_new(RZ_NONNULL const char *pattern, RzRegexFlags cflags, RzRegexFlags jflags,
RzRegexCompContext *ccontext) {
rz_return_val_if_fail(pattern, NULL);
RzRegexStatus err_num;
@ -73,7 +75,7 @@ RZ_API RZ_OWN RzRegex *rz_regex_new(RZ_NONNULL const char *pattern, RzRegexFlags
cflags | PCRE2_UTF | PCRE2_MATCH_INVALID_UTF,
&err_num,
&err_off,
NULL);
ccontext);
if (!regex) {
print_pcre2_err(pat, err_num, err_off);
free(fixed_pat);
@ -390,7 +392,7 @@ RZ_API RZ_OWN RzPVector /*<RzVector<RzRegexMatch *> *>*/ *rz_regex_match_all(
RZ_API bool rz_regex_contains(RZ_NONNULL const char *pattern, RZ_NONNULL const char *text,
RzRegexSize text_size,
RzRegexFlags cflags, RzRegexFlags mflags) {
RzRegex *re = rz_regex_new(pattern, cflags, 0);
RzRegex *re = rz_regex_new(pattern, cflags, 0, NULL);
if (!re) {
return false;
}
@ -420,7 +422,7 @@ RZ_API RZ_OWN RzStrBuf *rz_regex_full_match_str(RZ_NONNULL const char *pattern,
RzRegexFlags cflags, RzRegexFlags mflags, RZ_NONNULL const char *separator) {
rz_return_val_if_fail(pattern && text && separator, NULL);
RzRegex *re = rz_regex_new(pattern, cflags, 0);
RzRegex *re = rz_regex_new(pattern, cflags, 0, NULL);
RzStrBuf *sbuf = rz_strbuf_new("");
RzPVector *matches = rz_regex_match_all(re, text, text_size, 0, mflags);
if (!matches || !sbuf) {
@ -469,7 +471,7 @@ RZ_API RzRegexSize rz_regex_find(RZ_NONNULL const char *pattern, RZ_NONNULL RZ_B
RzRegexSize text_size, RzRegexSize text_offset,
RzRegexFlags cflags, RzRegexFlags mflags) {
rz_return_val_if_fail(pattern && text, SZT_MAX);
RzRegex *regex = rz_regex_new(pattern, cflags, RZ_REGEX_DEFAULT);
RzRegex *regex = rz_regex_new(pattern, cflags, RZ_REGEX_DEFAULT, NULL);
RzPVector *matches = rz_regex_match_first(regex, text, text_size, text_offset, mflags);
if (rz_pvector_empty(matches)) {
rz_pvector_free(matches);
@ -481,3 +483,30 @@ RZ_API RzRegexSize rz_regex_find(RZ_NONNULL const char *pattern, RZ_NONNULL RZ_B
rz_regex_free(regex);
return off;
}
/**
* \brief Returns a compile context.
*
* \return A PCRE2 compile context, or NULL in case of failure.
*/
RZ_API RZ_OWN RzRegexCompContext *rz_regex_compile_context_new() {
return pcre2_compile_context_create(NULL);
}
/**
* \brief Frees a compile context.
*
* \param A PCRE2 compile context.
*/
RZ_API void rz_regex_compile_context_free(RzRegexCompContext *ccontext) {
pcre2_compile_context_free(ccontext);
}
/**
* \brief Sets the newline convention of a compile context to the NUL character (\0).
*
* \param A PCRE2 compile context.
*/
RZ_API void rz_regex_set_nul_as_newline(RZ_NONNULL RzRegexCompContext *ccontext) {
pcre2_set_newline(ccontext, PCRE2_NEWLINE_NUL);
}

View file

@ -3538,7 +3538,7 @@ RZ_API RzList /*<char *>*/ *rz_str_split_list(char *str, const char *c, int n) {
*/
RZ_API RZ_OWN RzList /*<char *>*/ *rz_str_split_list_regex(RZ_NONNULL char *str, RZ_NONNULL const char *r, int n) {
rz_return_val_if_fail(str && r, NULL);
RzRegex *regex = rz_regex_new(r, RZ_REGEX_EXTENDED, 0);
RzRegex *regex = rz_regex_new(r, RZ_REGEX_EXTENDED, 0, NULL);
RzList *res = str_split_list_common_regex(str, regex, n, false, false);
rz_regex_free(regex);
return res;
@ -3600,7 +3600,7 @@ RZ_API RzList /*<char *>*/ *rz_str_split_duplist_n(const char *_str, const char
RZ_API RZ_OWN RzList /*<char *>*/ *rz_str_split_duplist_n_regex(RZ_NONNULL const char *_str, RZ_NONNULL const char *r, int n, bool trim) {
rz_return_val_if_fail(_str && r, NULL);
char *str = rz_str_dup(_str);
RzRegex *regex = rz_regex_new(r, RZ_REGEX_EXTENDED, 0);
RzRegex *regex = rz_regex_new(r, RZ_REGEX_EXTENDED, 0, NULL);
RzList *res = str_split_list_common_regex(str, regex, n, trim, true);
free(str);
rz_regex_free(regex);

View file

@ -44,3 +44,16 @@ EXPECT=<<EOF
0x000001bd hit0_1 ""Test123 ab""
EOF
RUN
NAME=/e with nul as newline
FILE==
CMDS=<<EOF
w abcd
w bcde @ 0x10
/e /b.*d/~[0-1]
EOF
EXPECT=<<EOF
0x00000001 hit0_0
0x00000010 hit0_1
EOF
RUN

View file

@ -19,7 +19,7 @@ bool exec_regex(RzRegex *regex, const char *str, RzRegexMatch **out) {
}
bool test_rz_regex_all_match(void) {
RzRegex *reg = rz_regex_new("push", RZ_REGEX_EXTENDED, 0);
RzRegex *reg = rz_regex_new("push", RZ_REGEX_EXTENDED, 0, NULL);
mu_assert_notnull(reg, "Regex was NULL");
RzRegexMatch *match = NULL;
mu_assert_true(exec_regex(reg, "push", &match), "Regex match failed");
@ -32,7 +32,7 @@ bool test_rz_regex_all_match(void) {
}
bool test_rz_regex_posix_blank(void) {
RzRegex *reg = rz_regex_new("[[:blank:]]", RZ_REGEX_EXTENDED, 0);
RzRegex *reg = rz_regex_new("[[:blank:]]", RZ_REGEX_EXTENDED, 0, NULL);
mu_assert_notnull(reg, "Regex was NULL");
RzRegexMatch *match = NULL;
mu_assert_true(exec_regex(reg, "push\tpush", &match), "Regex match failed");
@ -45,7 +45,7 @@ bool test_rz_regex_posix_blank(void) {
}
bool test_rz_regex_extend_space(void) {
RzRegex *reg = rz_regex_new("push esi", RZ_REGEX_DEFAULT, 0);
RzRegex *reg = rz_regex_new("push esi", RZ_REGEX_DEFAULT, 0, NULL);
mu_assert_notnull(reg, "Regex was NULL");
RzRegexMatch *match = NULL;
mu_assert_notnull(reg, "Regex was NULL");
@ -59,7 +59,7 @@ bool test_rz_regex_extend_space(void) {
}
bool test_rz_regex_all_to_str(void) {
RzRegex *reg = rz_regex_new("123", RZ_REGEX_EXTENDED, 0);
RzRegex *reg = rz_regex_new("123", RZ_REGEX_EXTENDED, 0, NULL);
mu_assert_notnull(reg, "Regex was NULL");
RzStrBuf *res = rz_regex_full_match_str("(123)", "123 123 123", RZ_REGEX_ZERO_TERMINATED, RZ_REGEX_DEFAULT, RZ_REGEX_DEFAULT, "\n");
char *str = rz_strbuf_drain(res);
@ -81,7 +81,7 @@ bool test_rz_regex_all_to_str(void) {
bool test_rz_reg_exec(void) {
const char *p = "abc|123";
RzRegex *reg = rz_regex_new(p, RZ_REGEX_EXTENDED, 0);
RzRegex *reg = rz_regex_new(p, RZ_REGEX_EXTENDED, 0, NULL);
mu_assert_notnull(reg, "Regex was NULL");
RzRegexMatch *match = NULL;
mu_assert_true(exec_regex(reg, "abc", &match), "Regex match failed");
@ -116,7 +116,7 @@ bool test_rz_reg_exec(void) {
free(match);
rz_regex_free(reg);
const char *p_big = "\\d+(([abc]*d[efg])+|[123]4[567]+)*|[zyx]+(test)+[mnb]";
reg = rz_regex_new(p_big, RZ_REGEX_EXTENDED, 0);
reg = rz_regex_new(p_big, RZ_REGEX_EXTENDED, 0, NULL);
mu_assert_true(exec_regex(reg, "z1abcde123z", &match), "Regex match failed");
mu_assert_notnull(match, "match was not set");
mu_assert_eq(match->start, 1, "Start of match is not 1");
@ -134,7 +134,7 @@ bool test_rz_reg_exec(void) {
bool test_rz_regex_capture(void) {
char *str = "abcd PrefixHello42s xyz";
RzRegex *re = rz_regex_new("[a-zA-Z]*(H[a-z]+)([0-9]*)s", RZ_REGEX_EXTENDED, 0);
RzRegex *re = rz_regex_new("[a-zA-Z]*(H[a-z]+)([0-9]*)s", RZ_REGEX_EXTENDED, 0, NULL);
mu_assert_notnull(re, "regex_new");
RzPVector *matches = rz_regex_match_all_not_grouped(re, str, RZ_REGEX_ZERO_TERMINATED, 0, RZ_REGEX_DEFAULT);
@ -180,7 +180,7 @@ bool test_rz_regex_find(void) {
}
bool test_rz_regex_named_matches(void) {
RzRegex *reg = rz_regex_new("(?<proto>^\\w+)(:\\/\\/)(?<domain>\\w+)\\.(?<tdomain>\\w+)", RZ_REGEX_EXTENDED, 0);
RzRegex *reg = rz_regex_new("(?<proto>^\\w+)(:\\/\\/)(?<domain>\\w+)\\.(?<tdomain>\\w+)", RZ_REGEX_EXTENDED, 0, NULL);
mu_assert_notnull(reg, "Regex was NULL");
mu_assert_streq((char *)rz_regex_get_match_name(reg, 1), "proto", "proto name not set.");
mu_assert_streq((char *)rz_regex_get_match_name(reg, 3), "domain", "domain name not set.");