continue;
if (prefix && *prefix && !mutt_str_startswith(mdata->group, prefix, CASE_MATCH))
continue;
- if (C_Mask && C_Mask->regex &&
- !((regexec(C_Mask->regex, mdata->group, 0, NULL, 0) == 0) ^ C_Mask->pat_not))
+ if (!mutt_regex_match(C_Mask, mdata->group))
{
continue;
}
{
continue;
}
- if (C_Mask && C_Mask->regex &&
- !((regexec(C_Mask->regex, de->d_name, 0, NULL, 0) == 0) ^ C_Mask->pat_not))
+ if (!mutt_regex_match(C_Mask, de->d_name))
{
continue;
}
mutt_str_replace(&e->env->subject, prot_headers->subject);
FREE(&e->env->disp_subj);
- if (regexec(C_ReplyRegex->regex, e->env->subject, 1, pmatch, 0) == 0)
+ if (mutt_regex_capture(C_ReplyRegex, e->env->subject, 1, pmatch))
e->env->real_subj = e->env->subject + pmatch[0].rm_eo;
else
e->env->real_subj = e->env->subject;
{
regmatch_t pmatch[1];
- if (C_ReplyRegex && C_ReplyRegex->regex &&
- (regexec(C_ReplyRegex->regex, env->subject, 1, pmatch, 0) == 0))
+ if (mutt_regex_capture(C_ReplyRegex, env->subject, 1, pmatch))
{
env->real_subj = env->subject + pmatch[0].rm_eo;
}
assert(re && "Something is wrong with your RE engine.");
char *res = NULL;
- int rc = regexec(re->regex, str, nmatch, match, 0);
- if (rc == 0)
+
+ if (mutt_regex_capture(re, str, nmatch, match))
{
/* Charset */
*charset = str + match[1].rm_so;
if (!(tmp->type & MUTT_FOLDER_HOOK))
continue;
- if ((path && (regexec(tmp->regex.regex, path, 0, NULL, 0) == 0) ^ tmp->regex.pat_not) ||
- (desc && (regexec(tmp->regex.regex, desc, 0, NULL, 0) == 0) ^ tmp->regex.pat_not))
+ if (mutt_regex_match(&tmp->regex, path) || mutt_regex_match(&tmp->regex, desc))
{
if (mutt_parse_rc_line(tmp->command, token, err) == MUTT_CMD_ERROR)
{
{
if (tmp->type & type)
{
- if (regexec(tmp->regex.regex, pat, 0, NULL, 0) == 0)
+ if (mutt_regex_match(&tmp->regex, pat))
return tmp->command;
}
}
TAILQ_FOREACH(tmp, &Hooks, entries)
{
- if ((tmp->type & hook) &&
- ((match && (regexec(tmp->regex.regex, match, 0, NULL, 0) == 0)) ^
- tmp->regex.pat_not))
+ if ((tmp->type & hook) && mutt_regex_match(&tmp->regex, match))
{
mutt_list_insert_tail(matches, mutt_str_strdup(tmp->command));
}
if (!(hook->command && (hook->type & MUTT_ACCOUNT_HOOK)))
continue;
- if ((regexec(hook->regex.regex, url, 0, NULL, 0) == 0) ^ hook->regex.pat_not)
+ if (mutt_regex_match(&hook->regex, url))
{
inhook = true;
/* apply filemask filter. This should really be done at menu setup rather
* than at scan, since it's so expensive to scan. But that's big changes
* to browser.c */
- if (C_Mask && C_Mask->regex &&
- !((regexec(C_Mask->regex, relpath, 0, NULL, 0) == 0) ^ C_Mask->pat_not))
+ if (!mutt_regex_match(C_Mask, relpath))
{
return;
}
if (!e || !e->subject)
continue;
- if (C_ReplyRegex && C_ReplyRegex->regex &&
- (regexec(C_ReplyRegex->regex, e->subject, 1, pmatch, 0) == 0))
+ if (mutt_regex_capture(C_ReplyRegex, e->subject, 1, pmatch))
{
e->real_subj = e->subject + pmatch[0].rm_eo;
continue;
{
if (l->type != type)
continue;
- if (regexec(l->regex.regex, cs, 0, NULL, 0) == 0)
+ if (mutt_regex_match(&l->regex, cs))
return l->replacement;
}
return NULL;
size_t dirlen = mutt_str_strlen(dir);
if ((len + dirlen) >= buflen)
{
- mutt_debug(LL_DEBUG3, "result too big for the buffer %ld >= %ld\n", len + dirlen, buflen);
+ mutt_debug(LL_DEBUG3, "result too big for the buffer %ld >= %ld\n",
+ len + dirlen, buflen);
return false;
}
*
* @authors
* Copyright (C) 2017 Richard Russon <rich@flatcap.org>
+ * Copyright (C) 2019 Simon Symeonidis <lethaljellybean@gmail.com>
*
* @copyright
* This program is free software: you can redistribute it and/or modify it under
struct RegexListNode *np = NULL;
STAILQ_FOREACH(np, rl, entries)
{
- if (!np->regex || !np->regex->regex)
- continue;
- if (regexec(np->regex->regex, str, 0, NULL, 0) == 0)
+ if (mutt_regex_match(np->regex, str))
{
mutt_debug(LL_DEBUG5, "%s matches %s\n", str, np->regex->pattern);
return true;
nmatch = np->nmatch;
}
- if (regexec(np->regex->regex, src, np->nmatch, pmatch, 0) == 0)
+ if (mutt_regex_capture(np->regex, src, np->nmatch, pmatch))
{
tlen = 0;
switcher ^= 1;
}
/* Does this pattern match? */
- if (regexec(np->regex->regex, str, (size_t) np->nmatch, pmatch, 0) == 0)
+ if (mutt_regex_capture(np->regex, str, (size_t) np->nmatch, pmatch))
{
mutt_debug(LL_DEBUG5, "%s matches %s\n", str, np->regex->pattern);
mutt_debug(LL_DEBUG5, "%d subs\n", (int) np->regex->regex->re_nsub);
return nremoved;
}
+
+/**
+ * mutt_regex_capture - match a regex against a string, with provided options
+ * @param regex Regex to execute
+ * @param str String to apply regex on
+ * @param nmatch Length of matches
+ * @param matches regmatch_t to hold match indices
+ * @param flags Type flags, e.g. #DT_REGEX_MATCH_CASE
+ * @retval bool true if str match, false if str does not match
+ */
+bool mutt_regex_capture(const struct Regex *regex, const char *str,
+ size_t nmatch, regmatch_t matches[])
+{
+ if (!regex || !str || !regex->regex)
+ return false;
+
+ int rc = regexec(regex->regex, str, nmatch, matches, 0);
+ return ((rc == 0) ^ regex->pat_not);
+}
+
+/**
+ * mutt_regex_match - Shorthand to mutt_regex_capture()
+ * @param regex Regex which is desired to match against
+ * @param str String to search with given regex
+ * @retval bool true if str match, false if str does not match
+ */
+bool mutt_regex_match(const struct Regex *regex, const char *str)
+{
+ return mutt_regex_capture(regex, str, 0, NULL);
+}
struct ReplaceListNode *mutt_replacelist_new(void);
int mutt_replacelist_remove(struct ReplaceList *rl, const char *pat);
+bool mutt_regex_match (const struct Regex *regex, const char *str);
+bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t num, regmatch_t matches[]);
+
#endif /* MUTT_LIB_REGEX_H */
memset(dest, 0, destlen);
- if (C_GecosMask && C_GecosMask->regex)
+ if (mutt_regex_capture(C_GecosMask, pw->pw_gecos, 1, pat_match))
{
- if (regexec(C_GecosMask->regex, pw->pw_gecos, 1, pat_match, 0) == 0)
- {
- mutt_str_strfcpy(dest, pw->pw_gecos + pat_match[0].rm_so,
- MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
- }
+ mutt_str_strfcpy(dest, pw->pw_gecos + pat_match[0].rm_so,
+ MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
}
else if ((p = strchr(pw->pw_gecos, ',')))
mutt_str_strfcpy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
while ((line = mutt_file_read_line(line, &linelen, fp_in, &lineno, 0)))
{
- if (regexec(C_PgpGoodSign->regex, line, 0, NULL, 0) == 0)
+ if (mutt_regex_match(C_PgpGoodSign, line))
{
mutt_debug(LL_DEBUG2, "\"%s\" matches regex\n", line);
rc = 0;
while ((line = mutt_file_read_line(line, &linelen, fp_in, &lineno, 0)))
{
- if (regexec(C_PgpDecryptionOkay->regex, line, 0, NULL, 0) == 0)
+ if (mutt_regex_match(C_PgpDecryptionOkay, line))
{
mutt_debug(LL_DEBUG2, "\"%s\" matches regex\n", line);
rc = 0;
case 'n':
if (C_MarkOld && (folder->ff->nd->last_cached >= folder->ff->nd->first_message) &&
- (folder->ff->nd->last_cached <= folder->ff->nd->last_message))
+ (folder->ff->nd->last_cached <= folder->ff->nd->last_message))
{
snprintf(fmt, sizeof(fmt), "%%%sd", prec);
snprintf(buf, buflen, fmt, folder->ff->nd->last_message - folder->ff->nd->last_cached);
if (m->mtime.tv_sec >= mtime)
{
- mutt_debug(LL_DEBUG2, "nm: check unnecessary (db=%lu mailbox=%lu)\n", mtime, m->mtime.tv_sec);
+ mutt_debug(LL_DEBUG2, "nm: check unnecessary (db=%lu mailbox=%lu)\n", mtime,
+ m->mtime.tv_sec);
return 0;
}
if (!pmatch)
pmatch = pmatch_internal;
- if (C_QuoteRegex && C_QuoteRegex->regex &&
- (regexec(C_QuoteRegex->regex, line, 1, pmatch, 0) == 0))
+ if (mutt_regex_capture(C_QuoteRegex, line, 1, pmatch))
{
- if (C_Smileys && C_Smileys->regex &&
- (regexec(C_Smileys->regex, line, 1, smatch, 0) == 0))
+ if (mutt_regex_capture(C_Smileys, line, 1, smatch))
{
if (smatch[0].rm_so > 0)
{
char c = line[smatch[0].rm_so];
line[smatch[0].rm_so] = 0;
- if (regexec(C_QuoteRegex->regex, line, 1, pmatch, 0) == 0)
+ if (mutt_regex_capture(C_QuoteRegex, line, 1, pmatch))
is_quote = true;
line[smatch[0].rm_so] = c;
(*last)--;
goto out;
}
- if (C_QuoteRegex && C_QuoteRegex->regex &&
- (regexec(C_QuoteRegex->regex, (char *) fmt, 1, pmatch, 0) == 0))
+
+ if (mutt_regex_capture(C_QuoteRegex, (char *) fmt, 1, pmatch))
{
(*line_info)[n].quote =
classify_quote(quote_list, (char *) fmt + pmatch[0].rm_so,
{
fgets(inputline, 1024, fp_att);
if (!mutt_is_quote_line(inputline, NULL) &&
- (regexec(C_AbortNoattachRegex->regex, inputline, 0, NULL, 0) == 0))
+ mutt_regex_match(C_AbortNoattachRegex, inputline))
{
found = true;
break;
REGEX_OBJS = test/regex/mutt_regex_compile.o \
test/regex/mutt_regex_free.o \
+ test/regex/mutt_regex_match.o \
test/regex/mutt_regexlist_add.o \
test/regex/mutt_regexlist_free.o \
test/regex/mutt_regexlist_match.o \
NEOMUTT_TEST_ITEM(test_mutt_pattern_comp) \
NEOMUTT_TEST_ITEM(test_mutt_regex_compile) \
NEOMUTT_TEST_ITEM(test_mutt_regex_free) \
+ NEOMUTT_TEST_ITEM(test_mutt_regex_match) \
NEOMUTT_TEST_ITEM(test_mutt_regexlist_add) \
NEOMUTT_TEST_ITEM(test_mutt_regexlist_free) \
NEOMUTT_TEST_ITEM(test_mutt_regexlist_match) \
--- /dev/null
+#define TEST_NO_MAIN
+
+#include "acutest.h"
+#include "mutt/buffer.h"
+#include "mutt/regex3.h"
+#include "config/common.h"
+
+static bool test_simple_cases(void)
+{
+ log_line(__func__);
+
+ struct Buffer *buf = mutt_buffer_new();
+ { /* handle edge cases */
+ struct Regex *rx = regex_new("hello bob", 0, buf);
+
+ const bool failed =
+ !TEST_CHECK(mutt_regex_match(NULL, NULL) == false) ||
+ !TEST_CHECK(mutt_regex_match(NULL, "bob the string") == false) ||
+ !TEST_CHECK(mutt_regex_match(rx, NULL) == false);
+ regex_free(&rx);
+
+ if (failed)
+ return false;
+ }
+
+ { /* handle normal cases */
+ struct Regex *rx = regex_new("hell", 0, buf);
+
+ const bool failed =
+ !TEST_CHECK(mutt_regex_match(rx, "hello there")) ||
+ !TEST_CHECK(mutt_regex_match(rx, "hell is not a greeting")) ||
+ !TEST_CHECK(mutt_regex_match(rx, "a demonic elavator is a hellevator"));
+ regex_free(&rx);
+
+ if (failed)
+ return false;
+ }
+
+ { /* test more elaborate regex */
+ const char *input = "bob bob bob mary bob jonny bob jon jon joe bob";
+ struct Regex *rx = regex_new("bob", 0, buf);
+
+ const bool result = mutt_regex_capture(rx, input, 0, NULL);
+ const bool failed = !TEST_CHECK(result);
+ regex_free(&rx);
+
+ if (failed)
+ return false;
+ }
+
+ { /* test passing simple flags */
+ const char *input = "BOB";
+ struct Regex *rx = regex_new("bob", 0, buf);
+ const bool failed = !TEST_CHECK(mutt_regex_capture(rx, input, 0, 0));
+ regex_free(&rx);
+
+ if (failed)
+ return false;
+ }
+
+ mutt_buffer_free(&buf);
+ return true;
+}
+
+static bool test_old_implementation(void)
+{
+ log_line(__func__);
+
+ // These tests check that the wrapper has the same behavior as
+ // prior, similar implementations
+
+ const char *bob_line = "definitely bob haha";
+ const char *not_bob_line = "john dave marty nothing else here";
+
+ struct Buffer *buf = mutt_buffer_new();
+ {
+ // from: if (regexec(C_PgpGoodSign->regex, bob_line, 0, NULL, 0) == 0)
+ // to: if (mutt_regex_match(C_PgpGoodSign, bob_line))
+
+ struct Regex *rx = regex_new("bob", 0, buf);
+ const bool old = regexec(rx->regex, bob_line, 0, NULL, 0) == 0;
+ const bool new = mutt_regex_match(rx, bob_line);
+ const bool failed = !TEST_CHECK(old == new);
+
+ regex_free(&rx);
+ if (failed)
+ return false;
+ }
+
+ {
+ // from: if (regexec(C_QuoteRegex->regex, bob_line, 1, pmatch, 0) == 0)
+ // to: if (mutt_regex_capture(QuoteRegex, bob_line, 1, pmatch))
+
+ const int nmatch = 1;
+ regmatch_t pmatch_1[nmatch], pmatch_2[nmatch];
+ struct Regex *rx = regex_new("bob", 0, buf);
+ const bool old = regexec(rx->regex, bob_line, nmatch, pmatch_1, 0) == 0;
+ const bool new = mutt_regex_capture(rx, bob_line, 1, pmatch_2);
+ const bool failed_common_behavior = !TEST_CHECK(old == new);
+
+ regex_free(&rx);
+ if (failed_common_behavior)
+ return false;
+ }
+
+ {
+ // from: if (C_QuoteRegex && C_QuoteRegex->regex &&
+ // (regexec(C_QuoteRegex->regex, bob_line, 1, pmatch, 0) == 0))
+ // to: if (mutt_regex_capture(QuoteRegex, bob_line, 1, pmatch))
+
+ const int nmatch = 1;
+ regmatch_t pmatch_1[nmatch], pmatch_2[nmatch];
+
+ struct Regex *rx = regex_new("bob", 0, buf);
+ const bool old = rx && rx->regex &&
+ (regexec(rx->regex, bob_line, nmatch, pmatch_1, 0) == 0);
+ const bool new = mutt_regex_capture(rx, bob_line, 1, pmatch_2);
+ regex_free(&rx);
+
+ const bool failed_common_behavior = !TEST_CHECK(old == new);
+ if (failed_common_behavior)
+ return false;
+
+ const bool failed_pmatch = !TEST_CHECK(pmatch_1->rm_so == pmatch_2->rm_so &&
+ pmatch_2->rm_eo == pmatch_2->rm_eo);
+ if (failed_pmatch)
+ return false;
+ }
+
+ {
+ // from: if ((tmp->type & hook) &&
+ // ((match && (regexec(tmp->regex.regex, match, 0, NULL, 0) == 0)) ^
+ // tmp->regex.pat_not))
+ // to: if ((tmp->type & hook) && mutt_regex_match(&tmp->regex, match))
+
+ struct Regex *rx = regex_new("!bob", DT_REGEX_ALLOW_NOT, buf);
+ const bool old =
+ (regexec(rx->regex, not_bob_line, 0, NULL, DT_REGEX_ALLOW_NOT) == 0) ^ rx->pat_not;
+ const bool new = mutt_regex_match(rx, not_bob_line);
+ regex_free(&rx);
+
+ const bool failed_common_behavior = !TEST_CHECK(old == new);
+ if (failed_common_behavior)
+ return false;
+
+ // make sure that bob is *NOT* found
+ const bool bob_found = !TEST_CHECK(new == true);
+ if (bob_found)
+ return false;
+ }
+
+ {
+ // from: if (C_Mask && C_Mask->regex &&
+ // !((regexec(C_Mask->regex, mdata->group, 0, NULL, 0) == 0)
+ // ^ C_Mask->pat_not))
+ // to: if(mutt_regex_match(C_Mask, de->d_name))
+
+ struct Regex *rx = regex_new("!bob", DT_REGEX_ALLOW_NOT, buf);
+ const bool old = rx && rx->regex &&
+ !((regexec(rx->regex, not_bob_line, 0, NULL, 0) == 0) ^ rx->pat_not);
+ const bool new = mutt_regex_match(rx, bob_line);
+ regex_free(&rx);
+
+ const bool failed_common_behavior = !TEST_CHECK(old == new);
+ if (failed_common_behavior)
+ return false;
+ }
+
+ {
+ // from: if (C_Mask && C_Mask->regex &&
+ // !((regexec(C_Mask->regex, mdata->group, 0, NULL, 0) == 0) ^ C_Mask->pat_not))
+ // to: if (!mutt_regex_match(C_Mask, mdata->group))
+ struct Regex *rx = regex_new("!bob", DT_REGEX_ALLOW_NOT, buf);
+ const bool old =
+ (rx && rx->regex) && !((regexec(rx->regex, line, 0, NULL, 0) == 0) ^ rx->pat_not);
+ const bool new = !mutt_regex_match(rx, line);
+ regex_free(&rx);
+
+ const bool failed_common_behavior = !TEST_CHECK(old == new);
+ if (failed_common_behavior)
+ return false;
+ }
+
+ {
+ // if ((regexec(hook->regex.regex, url, 0, NULL, 0) == 0) ^ hook->regex.pat_not)
+ // if (mutt_regex_match(&hook->regex, url))
+
+ struct Regex *rx = regex_new("bob", 0, buf);
+ const bool old = (regexec(rx->regex, bob_line, 0, NULL, 0) == 0) ^ rx->pat_not;
+ const bool new = mutt_regex_match(rx, bob_line);
+ regex_free(&rx);
+
+ const bool failed_common_behavior = !TEST_CHECK(old == new);
+ if (failed_common_behavior)
+ return false;
+ }
+
+ mutt_buffer_free(&buf);
+ return true;
+}
+
+void test_mutt_regex_match(void)
+{
+ bool (*tests[])(void) = { test_simple_cases, test_old_implementation };
+
+ const size_t num_tests = sizeof(tests) / sizeof(tests[0]);
+
+ for (size_t i = 0; i < num_tests; ++i)
+ TEST_CHECK(tests[i]());
+}