LIBMUTT= libmutt.a
LIBMUTTOBJS= mutt/base64.o mutt/buffer.o mutt/charset.o mutt/date.o mutt/debug.o mutt/exit.o \
mutt/file.o mutt/hash.o mutt/list.o mutt/mapping.o mutt/mbyte.o mutt/md5.o \
- mutt/memory.o mutt/message.o mutt/sha1.o mutt/signal.o mutt/string.o
+ mutt/memory.o mutt/message.o mutt/regex.o mutt/sha1.o mutt/signal.o mutt/string.o
CLEANFILES+= $(LIBMUTT) $(LIBMUTTOBJS)
MUTTLIBS+= $(LIBMUTT)
ALLOBJS+= $(LIBMUTTOBJS)
#include "mutt_charset.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "mx.h"
#include "opcodes.h"
#include "options.h"
#include "keymap.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "options.h"
#include "pattern.h"
#include "protos.h"
#include "mutt/file.h"
#include "mutt/memory.h"
#include "mutt/message.h"
+#include "mutt/regex3.h"
#include "mutt/string2.h"
#include "mutt.h"
#include "account.h"
#include "keymap.h"
#include "mutt_account.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "opcodes.h"
#include "options.h"
#include "protos.h"
#include "mbyte.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "opcodes.h"
#include "options.h"
#include "pager.h"
#include <signal.h>
#include "mutt/mutt.h"
#include "where.h"
-#include "mutt_regex.h"
#ifdef MAIN_C
/* so that global vars get included */
WHERE char *NmQueryWindowCurrentSearch;
#endif
+WHERE struct Regex Mask;
+WHERE struct Regex QuoteRegexp;
+WHERE struct Regex ReplyRegexp;
+WHERE struct Regex Smileys;
+WHERE struct Regex GecosMask;
+
#ifdef MAIN_C
const char *const BodyTypes[] = {
"x-unknown", "audio", "application", "image", "message",
#include "header.h"
#include "mbyte.h"
#include "mutt_charset.h"
-#include "mutt_regex.h"
#include "parameter.h"
#include "protos.h"
#include "tags.h"
#include "globals.h"
#include "header.h"
#include "mailbox.h"
-#include "mutt_regex.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
#include "pattern.h"
#include "globals.h"
#include "imap/imap.h"
#include "mutt_account.h"
-#include "mutt_regex.h"
#include "options.h"
#include "protos.h"
#ifdef ENABLE_NLS
#include "mutt_curses.h"
#include "mutt_idna.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "mx.h"
#include "myvar.h"
#include "ncrypt/ncrypt.h"
mutt_list_insert_tail(head, mutt_str_strdup(str));
}
-static struct RegexList *new_regex_list(void)
-{
- return mutt_mem_calloc(1, sizeof(struct RegexList));
-}
-
-int mutt_add_to_regex_list(struct RegexList **list, const char *s, int flags,
- struct Buffer *err)
-{
- struct RegexList *t = NULL, *last = NULL;
- struct Regex *rx = NULL;
-
- if (!s || !*s)
- return 0;
-
- rx = mutt_compile_regex(s, flags);
- if (!rx)
- {
- snprintf(err->data, err->dsize, "Bad regex: %s\n", s);
- return -1;
- }
-
- /* check to make sure the item is not already on this list */
- for (last = *list; last; last = last->next)
- {
- if (mutt_str_strcasecmp(rx->pattern, last->regex->pattern) == 0)
- {
- /* already on the list, so just ignore it */
- last = NULL;
- break;
- }
- if (!last->next)
- break;
- }
-
- if (!*list || last)
- {
- t = new_regex_list();
- t->regex = rx;
- if (last)
- {
- last->next = t;
- last = last->next;
- }
- else
- *list = last = t;
- }
- else /* duplicate */
- mutt_free_regex(&rx);
-
- return 0;
-}
-
-static int remove_from_replace_list(struct ReplaceList **list, const char *pat)
-{
- struct ReplaceList *cur = NULL, *prev = NULL;
- int nremoved = 0;
-
- /* Being first is a special case. */
- cur = *list;
- if (!cur)
- return 0;
- if (cur->regex && (mutt_str_strcmp(cur->regex->pattern, pat) == 0))
- {
- *list = cur->next;
- mutt_free_regex(&cur->regex);
- FREE(&cur->template);
- FREE(&cur);
- return 1;
- }
-
- prev = cur;
- for (cur = prev->next; cur;)
- {
- if (mutt_str_strcmp(cur->regex->pattern, pat) == 0)
- {
- prev->next = cur->next;
- mutt_free_regex(&cur->regex);
- FREE(&cur->template);
- FREE(&cur);
- cur = prev->next;
- nremoved++;
- }
- else
- cur = cur->next;
- }
-
- return nremoved;
-}
-
-static struct ReplaceList *new_replace_list(void)
-{
- return mutt_mem_calloc(1, sizeof(struct ReplaceList));
-}
-
-static int add_to_replace_list(struct ReplaceList **list, const char *pat,
- const char *templ, struct Buffer *err)
-{
- struct ReplaceList *t = NULL, *last = NULL;
- struct Regex *rx = NULL;
- int n;
- const char *p = NULL;
-
- if (!pat || !*pat || !templ)
- return 0;
-
- rx = mutt_compile_regex(pat, REG_ICASE);
- if (!rx)
- {
- snprintf(err->data, err->dsize, _("Bad regex: %s"), pat);
- return -1;
- }
-
- /* check to make sure the item is not already on this list */
- for (last = *list; last; last = last->next)
- {
- if (mutt_str_strcasecmp(rx->pattern, last->regex->pattern) == 0)
- {
- /* Already on the list. Formerly we just skipped this case, but
- * now we're supporting removals, which means we're supporting
- * re-adds conceptually. So we probably want this to imply a
- * removal, then do an add. We can achieve the removal by freeing
- * the template, and leaving t pointed at the current item.
- */
- t = last;
- FREE(&t->template);
- break;
- }
- if (!last->next)
- break;
- }
-
- /* If t is set, it's pointing into an extant ReplaceList* that we want to
- * update. Otherwise we want to make a new one to link at the list's end.
- */
- if (!t)
- {
- t = new_replace_list();
- t->regex = rx;
- rx = NULL;
- if (last)
- last->next = t;
- else
- *list = t;
- }
- else
- mutt_free_regex(&rx);
-
- /* Now t is the ReplaceList* that we want to modify. It is prepared. */
- t->template = mutt_str_strdup(templ);
-
- /* Find highest match number in template string */
- t->nmatch = 0;
- for (p = templ; *p;)
- {
- if (*p == '%')
- {
- n = atoi(++p);
- if (n > t->nmatch)
- t->nmatch = n;
- while (*p && isdigit((int) *p))
- p++;
- }
- else
- p++;
- }
-
- if (t->nmatch > t->regex->regex->re_nsub)
- {
- snprintf(err->data, err->dsize, "%s", _("Not enough subexpressions for "
- "template"));
- remove_from_replace_list(list, pat);
- return -1;
- }
-
- t->nmatch++; /* match 0 is always the whole expr */
-
- return 0;
-}
-
/**
* finish_source - 'finish' command: stop processing current config file
* @param tmp Temporary space shared by all command handlers
#include "group.h"
#include "mutt_commands.h"
#include "mutt_options.h"
-#include "mutt_regex.h"
+#include "mutt/regex3.h"
#include "mx.h"
#include "options.h"
#include "protos.h"
** .pp
** Also see the $$smart_wrap variable.
*/
- { "mask", DT_REGEX, R_NONE, UL &Mask, UL "!^\\.[^.]" },
+ { "mask", DT_REGEX | DT_REGEX_MATCH_CASE | DT_REGEX_ALLOW_NOT, R_NONE, UL &Mask, UL "!^\\.[^.]" },
/*
** .pp
** A regular expression used in the file browser, optionally preceded by
#include "mutt_curses.h"
#include "mutt_idna.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "mutt_socket.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
#include "mbyte.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "opcodes.h"
#include "options.h"
#include "pattern.h"
#include <stddef.h>
#include <stdio.h>
-struct ReplaceList;
-struct RegexList;
-struct State;
struct ListHead;
-struct TagHead;
struct Mapping;
/* On OS X 10.5.x, wide char functions are inlined by default breaking
#define MUTT_SPAM 1
#define MUTT_NOSPAM 2
-void mutt_free_regex_list(struct RegexList **list);
-void mutt_free_replace_list(struct ReplaceList **list);
bool mutt_matches_ignore(const char *s);
-/* add an element to a list */
-int mutt_remove_from_regex_list(struct RegexList **l, const char *str);
-
void mutt_init(int skip_sys_rc, struct ListHead *commands);
/* flag to mutt_pattern_comp() */
* -# @subpage md5
* -# @subpage memory
* -# @subpage message
+ * -# @subpage regex
* -# @subpage sha1
* -# @subpage signal
* -# @subpage string
#include "md5.h"
#include "memory.h"
#include "message.h"
+#include "regex3.h"
#include "sha1.h"
#include "signal2.h"
#include "string2.h"
--- /dev/null
+/**
+ * @file
+ * Manage regular expressions
+ *
+ * @authors
+ * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page regex Manage regular expressions
+ *
+ * Manage regular expressions.
+ *
+ * | Function | Description
+ * | :------------------------ | :------------------------------------------
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "buffer.h"
+#include "debug.h"
+#include "mbyte.h"
+#include "memory.h"
+#include "message.h"
+#include "regex3.h"
+#include "string2.h"
+
+struct Regex *mutt_compile_regex(const char *s, int flags)
+{
+ struct Regex *pp = mutt_mem_calloc(1, sizeof(struct Regex));
+ pp->pattern = mutt_str_strdup(s);
+ pp->regex = mutt_mem_calloc(1, sizeof(regex_t));
+ if (REGCOMP(pp->regex, NONULL(s), flags) != 0)
+ mutt_free_regex(&pp);
+
+ return pp;
+}
+
+/**
+ * mutt_regex_create - Create an Regex from a string
+ * @param str Regular expression
+ * @param flags Type flags, e.g. #DT_REGEX_MATCH_CASE
+ * @param err Buffer for error messages
+ * @retval ptr New Regex object
+ * @retval NULL Error
+ */
+struct Regex *mutt_regex_create(const char *str, int flags, struct Buffer *err)
+{
+ if (!str)
+ return NULL;
+
+ int rflags = 0;
+ struct Regex *reg = mutt_mem_calloc(1, sizeof(struct Regex));
+
+ reg->regex = mutt_mem_calloc(1, sizeof(regex_t));
+ reg->pattern = mutt_str_strdup(str);
+
+ /* Should we use smart case matching? */
+ if (((flags & DT_REGEX_MATCH_CASE) == 0) && mutt_mb_is_lower(str))
+ rflags |= REG_ICASE;
+
+ /* Is a prefix of '!' allowed? */
+ if (((flags & DT_REGEX_ALLOW_NOT) != 0) && (str[0] == '!'))
+ {
+ reg->not = true;
+ str++;
+ }
+
+ int rc = REGCOMP(reg->regex, str, rflags);
+ if ((rc != 0) && err)
+ {
+ regerror(rc, reg->regex, err->data, err->dsize);
+ mutt_free_regex(®);
+ return NULL;
+ }
+
+ return reg;
+}
+
+void mutt_free_regex(struct Regex **pp)
+{
+ FREE(&(*pp)->pattern);
+ regfree((*pp)->regex);
+ FREE(&(*pp)->regex);
+ FREE(pp);
+}
+
+int mutt_add_to_regex_list(struct RegexList **list, const char *s, int flags,
+ struct Buffer *err)
+{
+ struct RegexList *t = NULL, *last = NULL;
+ struct Regex *rx = NULL;
+
+ if (!s || !*s)
+ return 0;
+
+ rx = mutt_compile_regex(s, flags);
+ if (!rx)
+ {
+ snprintf(err->data, err->dsize, "Bad regex: %s\n", s);
+ return -1;
+ }
+
+ /* check to make sure the item is not already on this list */
+ for (last = *list; last; last = last->next)
+ {
+ if (mutt_str_strcasecmp(rx->pattern, last->regex->pattern) == 0)
+ {
+ /* already on the list, so just ignore it */
+ last = NULL;
+ break;
+ }
+ if (!last->next)
+ break;
+ }
+
+ if (!*list || last)
+ {
+ t = new_regex_list();
+ t->regex = rx;
+ if (last)
+ {
+ last->next = t;
+ last = last->next;
+ }
+ else
+ *list = last = t;
+ }
+ else /* duplicate */
+ mutt_free_regex(&rx);
+
+ return 0;
+}
+
+void mutt_free_regex_list(struct RegexList **list)
+{
+ struct RegexList *p = NULL;
+
+ if (!list)
+ return;
+ while (*list)
+ {
+ p = *list;
+ *list = (*list)->next;
+ mutt_free_regex(&p->regex);
+ FREE(&p);
+ }
+}
+
+bool mutt_match_regex_list(const char *s, struct RegexList *l)
+{
+ if (!s)
+ return false;
+
+ for (; l; l = l->next)
+ {
+ if (regexec(l->regex->regex, s, (size_t) 0, (regmatch_t *) 0, (int) 0) == 0)
+ {
+ mutt_debug(5, "%s matches %s\n", s, l->regex->pattern);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+struct RegexList *new_regex_list(void)
+{
+ return mutt_mem_calloc(1, sizeof(struct RegexList));
+}
+
+int mutt_remove_from_regex_list(struct RegexList **l, const char *str)
+{
+ struct RegexList *p = NULL, *last = NULL;
+ int rc = -1;
+
+ if (mutt_str_strcmp("*", str) == 0)
+ {
+ mutt_free_regex_list(l); /* ``unCMD *'' means delete all current entries */
+ rc = 0;
+ }
+ else
+ {
+ p = *l;
+ last = NULL;
+ while (p)
+ {
+ if (mutt_str_strcasecmp(str, p->regex->pattern) == 0)
+ {
+ mutt_free_regex(&p->regex);
+ if (last)
+ last->next = p->next;
+ else
+ (*l) = p->next;
+ FREE(&p);
+ rc = 0;
+ }
+ else
+ {
+ last = p;
+ p = p->next;
+ }
+ }
+ }
+ return rc;
+}
+
+int add_to_replace_list(struct ReplaceList **list, const char *pat,
+ const char *templ, struct Buffer *err)
+{
+ struct ReplaceList *t = NULL, *last = NULL;
+ struct Regex *rx = NULL;
+ int n;
+ const char *p = NULL;
+
+ if (!pat || !*pat || !templ)
+ return 0;
+
+ rx = mutt_compile_regex(pat, REG_ICASE);
+ if (!rx)
+ {
+ snprintf(err->data, err->dsize, _("Bad regex: %s"), pat);
+ return -1;
+ }
+
+ /* check to make sure the item is not already on this list */
+ for (last = *list; last; last = last->next)
+ {
+ if (mutt_str_strcasecmp(rx->pattern, last->regex->pattern) == 0)
+ {
+ /* Already on the list. Formerly we just skipped this case, but
+ * now we're supporting removals, which means we're supporting
+ * re-adds conceptually. So we probably want this to imply a
+ * removal, then do an add. We can achieve the removal by freeing
+ * the template, and leaving t pointed at the current item.
+ */
+ t = last;
+ FREE(&t->template);
+ break;
+ }
+ if (!last->next)
+ break;
+ }
+
+ /* If t is set, it's pointing into an extant ReplaceList* that we want to
+ * update. Otherwise we want to make a new one to link at the list's end.
+ */
+ if (!t)
+ {
+ t = new_replace_list();
+ t->regex = rx;
+ rx = NULL;
+ if (last)
+ last->next = t;
+ else
+ *list = t;
+ }
+ else
+ mutt_free_regex(&rx);
+
+ /* Now t is the ReplaceList* that we want to modify. It is prepared. */
+ t->template = mutt_str_strdup(templ);
+
+ /* Find highest match number in template string */
+ t->nmatch = 0;
+ for (p = templ; *p;)
+ {
+ if (*p == '%')
+ {
+ n = atoi(++p);
+ if (n > t->nmatch)
+ t->nmatch = n;
+ while (*p && isdigit((int) *p))
+ p++;
+ }
+ else
+ p++;
+ }
+
+ if (t->nmatch > t->regex->regex->re_nsub)
+ {
+ snprintf(err->data, err->dsize, "%s", _("Not enough subexpressions for "
+ "template"));
+ remove_from_replace_list(list, pat);
+ return -1;
+ }
+
+ t->nmatch++; /* match 0 is always the whole expr */
+
+ return 0;
+}
+
+/**
+ * mutt_apply_replace - Apply replacements to a buffer
+ *
+ * Note this function uses a fixed size buffer of LONG_STRING and so
+ * should only be used for visual modifications, such as disp_subj.
+ */
+char *mutt_apply_replace(char *dbuf, size_t dlen, char *sbuf, struct ReplaceList *rlist)
+{
+ struct ReplaceList *l = NULL;
+ static regmatch_t *pmatch = NULL;
+ static int nmatch = 0;
+ static char twinbuf[2][LONG_STRING];
+ int switcher = 0;
+ char *p = NULL;
+ int n;
+ size_t cpysize, tlen;
+ char *src = NULL, *dst = NULL;
+
+ if (dbuf && dlen)
+ dbuf[0] = '\0';
+
+ if (sbuf == NULL || *sbuf == '\0' || (dbuf && !dlen))
+ return dbuf;
+
+ twinbuf[0][0] = '\0';
+ twinbuf[1][0] = '\0';
+ src = twinbuf[switcher];
+ dst = src;
+
+ mutt_str_strfcpy(src, sbuf, LONG_STRING);
+
+ for (l = rlist; l; l = l->next)
+ {
+ /* If this pattern needs more matches, expand pmatch. */
+ if (l->nmatch > nmatch)
+ {
+ mutt_mem_realloc(&pmatch, l->nmatch * sizeof(regmatch_t));
+ nmatch = l->nmatch;
+ }
+
+ if (regexec(l->regex->regex, src, l->nmatch, pmatch, 0) == 0)
+ {
+ tlen = 0;
+ switcher ^= 1;
+ dst = twinbuf[switcher];
+
+ mutt_debug(5, "%s matches %s\n", src, l->regex->pattern);
+
+ /* Copy into other twinbuf with substitutions */
+ if (l->template)
+ {
+ for (p = l->template; *p && (tlen < LONG_STRING - 1);)
+ {
+ if (*p == '%')
+ {
+ p++;
+ if (*p == 'L')
+ {
+ p++;
+ cpysize = MIN(pmatch[0].rm_so, LONG_STRING - tlen - 1);
+ strncpy(&dst[tlen], src, cpysize);
+ tlen += cpysize;
+ }
+ else if (*p == 'R')
+ {
+ p++;
+ cpysize = MIN(strlen(src) - pmatch[0].rm_eo, LONG_STRING - tlen - 1);
+ strncpy(&dst[tlen], &src[pmatch[0].rm_eo], cpysize);
+ tlen += cpysize;
+ }
+ else
+ {
+ n = strtoul(p, &p, 10); /* get subst number */
+ while (isdigit((unsigned char) *p)) /* skip subst token */
+ p++;
+ for (int i = pmatch[n].rm_so;
+ (i < pmatch[n].rm_eo) && (tlen < LONG_STRING - 1); i++)
+ dst[tlen++] = src[i];
+ }
+ }
+ else
+ dst[tlen++] = *p++;
+ }
+ }
+ dst[tlen] = '\0';
+ mutt_debug(5, "subst %s\n", dst);
+ }
+ src = dst;
+ }
+
+ if (dbuf)
+ mutt_str_strfcpy(dbuf, dst, dlen);
+ else
+ dbuf = mutt_str_strdup(dst);
+ return dbuf;
+}
+
+void mutt_free_replace_list(struct ReplaceList **list)
+{
+ struct ReplaceList *p = NULL;
+
+ if (!list)
+ return;
+ while (*list)
+ {
+ p = *list;
+ *list = (*list)->next;
+ mutt_free_regex(&p->regex);
+ FREE(&p->template);
+ FREE(&p);
+ }
+}
+
+/**
+ * mutt_match_spam_list - Does a string match a spam pattern
+ * @param s String to check
+ * @param l List of spam patterns
+ * @param text Buffer to save match
+ * @param textsize Buffer length
+ * @retval true if \a s matches a pattern in \a l
+ * @retval false otherwise
+ *
+ * Match a string against the patterns defined by the 'spam' command and output
+ * the expanded format into `text` when there is a match. If textsize<=0, the
+ * match is performed but the format is not expanded and no assumptions are made
+ * about the value of `text` so it may be NULL.
+ */
+bool mutt_match_spam_list(const char *s, struct ReplaceList *l, char *text, int textsize)
+{
+ static regmatch_t *pmatch = NULL;
+ static int nmatch = 0;
+ int tlen = 0;
+ char *p = NULL;
+
+ if (!s)
+ return false;
+
+ for (; l; l = l->next)
+ {
+ /* If this pattern needs more matches, expand pmatch. */
+ if (l->nmatch > nmatch)
+ {
+ mutt_mem_realloc(&pmatch, l->nmatch * sizeof(regmatch_t));
+ nmatch = l->nmatch;
+ }
+
+ /* Does this pattern match? */
+ if (regexec(l->regex->regex, s, (size_t) l->nmatch, (regmatch_t *) pmatch, (int) 0) == 0)
+ {
+ mutt_debug(5, "%s matches %s\n", s, l->regex->pattern);
+ mutt_debug(5, "%d subs\n", (int) l->regex->regex->re_nsub);
+
+ /* Copy template into text, with substitutions. */
+ for (p = l->template; *p && tlen < textsize - 1;)
+ {
+ /* backreference to pattern match substring, eg. %1, %2, etc) */
+ if (*p == '%')
+ {
+ char *e = NULL; /* used as pointer to end of integer backreference in strtol() call */
+ int n;
+
+ p++; /* skip over % char */
+ n = strtol(p, &e, 10);
+ /* Ensure that the integer conversion succeeded (e!=p) and bounds check. The upper bound check
+ * should not strictly be necessary since add_to_spam_list() finds the largest value, and
+ * the static array above is always large enough based on that value. */
+ if (e != p && n >= 0 && n <= l->nmatch && pmatch[n].rm_so != -1)
+ {
+ /* copy as much of the substring match as will fit in the output buffer, saving space for
+ * the terminating nul char */
+ int idx;
+ for (idx = pmatch[n].rm_so;
+ (idx < pmatch[n].rm_eo) && (tlen < textsize - 1); ++idx)
+ text[tlen++] = s[idx];
+ }
+ p = e; /* skip over the parsed integer */
+ }
+ else
+ {
+ text[tlen++] = *p++;
+ }
+ }
+ /* tlen should always be less than textsize except when textsize<=0
+ * because the bounds checks in the above code leave room for the
+ * terminal nul char. This should avoid returning an unterminated
+ * string to the caller. When textsize<=0 we make no assumption about
+ * the validity of the text pointer. */
+ if (tlen < textsize)
+ {
+ text[tlen] = '\0';
+ mutt_debug(5, "\"%s\"\n", text);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+struct ReplaceList *new_replace_list(void)
+{
+ return mutt_mem_calloc(1, sizeof(struct ReplaceList));
+}
+
+int remove_from_replace_list(struct ReplaceList **list, const char *pat)
+{
+ struct ReplaceList *cur = NULL, *prev = NULL;
+ int nremoved = 0;
+
+ /* Being first is a special case. */
+ cur = *list;
+ if (!cur)
+ return 0;
+ if (cur->regex && (mutt_str_strcmp(cur->regex->pattern, pat) == 0))
+ {
+ *list = cur->next;
+ mutt_free_regex(&cur->regex);
+ FREE(&cur->template);
+ FREE(&cur);
+ return 1;
+ }
+
+ prev = cur;
+ for (cur = prev->next; cur;)
+ {
+ if (mutt_str_strcmp(cur->regex->pattern, pat) == 0)
+ {
+ prev->next = cur->next;
+ mutt_free_regex(&cur->regex);
+ FREE(&cur->template);
+ FREE(&cur);
+ cur = prev->next;
+ nremoved++;
+ }
+ else
+ cur = cur->next;
+ }
+
+ return nremoved;
+}
/**
* @file
- * Constants/structs for handling (lists of) regular expressions
+ * Manage regular expressions
*
* @authors
- * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
*
* @copyright
* This program is free software: you can redistribute it and/or modify it under
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
- * A (more) generic interface to regular expression matching
- */
-
#ifndef _MUTT_REGEX_H
#define _MUTT_REGEX_H
#include <regex.h>
-#include "where.h"
+#include <stdbool.h>
-/* this is a non-standard option supported by Solaris 2.5.x which allows
- * patterns of the form \<...\>
- */
+struct Buffer;
+
+/* ... DT_REGEX */
+#define DT_REGEX_MATCH_CASE 0x010 /**< Case-sensitive matching */
+#define DT_REGEX_ALLOW_NOT 0x020 /**< Regex can begin with '!' */
+
+/* This is a non-standard option supported by Solaris 2.5.x
+ * which allows patterns of the form \<...\> */
#ifndef REG_WORDS
#define REG_WORDS 0
#endif
{
char *pattern; /**< printable version */
regex_t *regex; /**< compiled expression */
- bool not : 1; /**< do not match */
+ bool not; /**< do not match */
};
/**
struct ReplaceList *next;
};
-WHERE struct Regex Mask;
-WHERE struct Regex QuoteRegexp;
-WHERE struct Regex ReplyRegexp;
-WHERE struct Regex Smileys;
-WHERE struct Regex GecosMask;
+struct Regex * mutt_compile_regex(const char *s, int flags);
+struct Regex * mutt_regex_create(const char *str, int flags, struct Buffer *err);
+void mutt_free_regex(struct Regex **pp);
+
+int mutt_add_to_regex_list(struct RegexList **list, const char *s, int flags, struct Buffer *err);
+void mutt_free_regex_list(struct RegexList **list);
+bool mutt_match_regex_list(const char *s, struct RegexList *l);
+struct RegexList * new_regex_list(void);
+int mutt_remove_from_regex_list(struct RegexList **l, const char *str);
+
+int add_to_replace_list(struct ReplaceList **list, const char *pat, const char *templ, struct Buffer *err);
+char * mutt_apply_replace(char *dbuf, size_t dlen, char *sbuf, struct ReplaceList *rlist);
+void mutt_free_replace_list(struct ReplaceList **list);
+bool mutt_match_spam_list(const char *s, struct ReplaceList *l, char *text, int textsize);
+struct ReplaceList *new_replace_list(void);
+int remove_from_replace_list(struct ReplaceList **list, const char *pat);
#endif /* _MUTT_REGEX_H */
#include "mime.h"
#include "mutt_charset.h"
#include "mutt_curses.h"
-#include "mutt_regex.h"
#include "mx.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
}
}
-int mutt_remove_from_regex_list(struct RegexList **l, const char *str)
-{
- struct RegexList *p = NULL, *last = NULL;
- int rc = -1;
-
- if (mutt_str_strcmp("*", str) == 0)
- {
- mutt_free_regex_list(l); /* ``unCMD *'' means delete all current entries */
- rc = 0;
- }
- else
- {
- p = *l;
- last = NULL;
- while (p)
- {
- if (mutt_str_strcasecmp(str, p->regex->pattern) == 0)
- {
- mutt_free_regex(&p->regex);
- if (last)
- last->next = p->next;
- else
- (*l) = p->next;
- FREE(&p);
- rc = 0;
- }
- else
- {
- last = p;
- p = p->next;
- }
- }
- }
- return rc;
-}
-
/**
* mutt_matches_ignore - Does the string match the ignore list
*
*p = '_';
}
-/**
- * mutt_apply_replace - Apply replacements to a buffer
- *
- * Note this function uses a fixed size buffer of LONG_STRING and so
- * should only be used for visual modifications, such as disp_subj.
- */
-char *mutt_apply_replace(char *dbuf, size_t dlen, char *sbuf, struct ReplaceList *rlist)
-{
- struct ReplaceList *l = NULL;
- static regmatch_t *pmatch = NULL;
- static int nmatch = 0;
- static char twinbuf[2][LONG_STRING];
- int switcher = 0;
- char *p = NULL;
- int n;
- size_t cpysize, tlen;
- char *src = NULL, *dst = NULL;
-
- if (dbuf && dlen)
- dbuf[0] = '\0';
-
- if (sbuf == NULL || *sbuf == '\0' || (dbuf && !dlen))
- return dbuf;
-
- twinbuf[0][0] = '\0';
- twinbuf[1][0] = '\0';
- src = twinbuf[switcher];
- dst = src;
-
- mutt_str_strfcpy(src, sbuf, LONG_STRING);
-
- for (l = rlist; l; l = l->next)
- {
- /* If this pattern needs more matches, expand pmatch. */
- if (l->nmatch > nmatch)
- {
- mutt_mem_realloc(&pmatch, l->nmatch * sizeof(regmatch_t));
- nmatch = l->nmatch;
- }
-
- if (regexec(l->regex->regex, src, l->nmatch, pmatch, 0) == 0)
- {
- tlen = 0;
- switcher ^= 1;
- dst = twinbuf[switcher];
-
- mutt_debug(5, "%s matches %s\n", src, l->regex->pattern);
-
- /* Copy into other twinbuf with substitutions */
- if (l->template)
- {
- for (p = l->template; *p && (tlen < LONG_STRING - 1);)
- {
- if (*p == '%')
- {
- p++;
- if (*p == 'L')
- {
- p++;
- cpysize = MIN(pmatch[0].rm_so, LONG_STRING - tlen - 1);
- strncpy(&dst[tlen], src, cpysize);
- tlen += cpysize;
- }
- else if (*p == 'R')
- {
- p++;
- cpysize = MIN(strlen(src) - pmatch[0].rm_eo, LONG_STRING - tlen - 1);
- strncpy(&dst[tlen], &src[pmatch[0].rm_eo], cpysize);
- tlen += cpysize;
- }
- else
- {
- n = strtoul(p, &p, 10); /* get subst number */
- while (isdigit((unsigned char) *p)) /* skip subst token */
- p++;
- for (int i = pmatch[n].rm_so;
- (i < pmatch[n].rm_eo) && (tlen < LONG_STRING - 1); i++)
- dst[tlen++] = src[i];
- }
- }
- else
- dst[tlen++] = *p++;
- }
- }
- dst[tlen] = '\0';
- mutt_debug(5, "subst %s\n", dst);
- }
- src = dst;
- }
-
- if (dbuf)
- mutt_str_strfcpy(dbuf, dst, dlen);
- else
- dbuf = mutt_str_strdup(dst);
- return dbuf;
-}
-
/**
* mutt_expando_format - Expand expandos (%x) in a string
* @param[out] buf Buffer in which to save string
return vstring;
}
-struct Regex *mutt_compile_regex(const char *s, int flags)
-{
- struct Regex *pp = mutt_mem_calloc(1, sizeof(struct Regex));
- pp->pattern = mutt_str_strdup(s);
- pp->regex = mutt_mem_calloc(1, sizeof(regex_t));
- if (REGCOMP(pp->regex, NONULL(s), flags) != 0)
- mutt_free_regex(&pp);
-
- return pp;
-}
-
-void mutt_free_regex(struct Regex **pp)
-{
- FREE(&(*pp)->pattern);
- regfree((*pp)->regex);
- FREE(&(*pp)->regex);
- FREE(pp);
-}
-
-void mutt_free_regex_list(struct RegexList **list)
-{
- struct RegexList *p = NULL;
-
- if (!list)
- return;
- while (*list)
- {
- p = *list;
- *list = (*list)->next;
- mutt_free_regex(&p->regex);
- FREE(&p);
- }
-}
-
-void mutt_free_replace_list(struct ReplaceList **list)
-{
- struct ReplaceList *p = NULL;
-
- if (!list)
- return;
- while (*list)
- {
- p = *list;
- *list = (*list)->next;
- mutt_free_regex(&p->regex);
- FREE(&p->template);
- FREE(&p);
- }
-}
-
-bool mutt_match_regex_list(const char *s, struct RegexList *l)
-{
- if (!s)
- return false;
-
- for (; l; l = l->next)
- {
- if (regexec(l->regex->regex, s, (size_t) 0, (regmatch_t *) 0, (int) 0) == 0)
- {
- mutt_debug(5, "%s matches %s\n", s, l->regex->pattern);
- return true;
- }
- }
-
- return false;
-}
-
-/**
- * mutt_match_spam_list - Does a string match a spam pattern
- * @param s String to check
- * @param l List of spam patterns
- * @param text Buffer to save match
- * @param textsize Buffer length
- * @retval true if \a s matches a pattern in \a l
- * @retval false otherwise
- *
- * Match a string against the patterns defined by the 'spam' command and output
- * the expanded format into `text` when there is a match. If textsize<=0, the
- * match is performed but the format is not expanded and no assumptions are made
- * about the value of `text` so it may be NULL.
- */
-bool mutt_match_spam_list(const char *s, struct ReplaceList *l, char *text, int textsize)
-{
- static regmatch_t *pmatch = NULL;
- static int nmatch = 0;
- int tlen = 0;
- char *p = NULL;
-
- if (!s)
- return false;
-
- for (; l; l = l->next)
- {
- /* If this pattern needs more matches, expand pmatch. */
- if (l->nmatch > nmatch)
- {
- mutt_mem_realloc(&pmatch, l->nmatch * sizeof(regmatch_t));
- nmatch = l->nmatch;
- }
-
- /* Does this pattern match? */
- if (regexec(l->regex->regex, s, (size_t) l->nmatch, (regmatch_t *) pmatch, (int) 0) == 0)
- {
- mutt_debug(5, "%s matches %s\n", s, l->regex->pattern);
- mutt_debug(5, "%d subs\n", (int) l->regex->regex->re_nsub);
-
- /* Copy template into text, with substitutions. */
- for (p = l->template; *p && tlen < textsize - 1;)
- {
- /* backreference to pattern match substring, eg. %1, %2, etc) */
- if (*p == '%')
- {
- char *e = NULL; /* used as pointer to end of integer backreference in strtol() call */
- int n;
-
- p++; /* skip over % char */
- n = strtol(p, &e, 10);
- /* Ensure that the integer conversion succeeded (e!=p) and bounds check. The upper bound check
- * should not strictly be necessary since add_to_spam_list() finds the largest value, and
- * the static array above is always large enough based on that value. */
- if (e != p && n >= 0 && n <= l->nmatch && pmatch[n].rm_so != -1)
- {
- /* copy as much of the substring match as will fit in the output buffer, saving space for
- * the terminating nul char */
- int idx;
- for (idx = pmatch[n].rm_so;
- (idx < pmatch[n].rm_eo) && (tlen < textsize - 1); ++idx)
- text[tlen++] = s[idx];
- }
- p = e; /* skip over the parsed integer */
- }
- else
- {
- text[tlen++] = *p++;
- }
- }
- /* tlen should always be less than textsize except when textsize<=0
- * because the bounds checks in the above code leave room for the
- * terminal nul char. This should avoid returning an unterminated
- * string to the caller. When textsize<=0 we make no assumption about
- * the validity of the text pointer. */
- if (tlen < textsize)
- {
- text[tlen] = '\0';
- mutt_debug(5, "\"%s\"\n", text);
- }
- return true;
- }
- }
-
- return false;
-}
-
void mutt_encode_path(char *dest, size_t dlen, const char *src)
{
char *p = mutt_str_strdup(src);
#include "mime.h"
#include "mutt_charset.h"
#include "mutt_curses.h"
-#include "mutt_regex.h"
#include "ncrypt.h"
#include "options.h"
#include "parameter.h"
#include "mbyte.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
#include "mx.h"
#include "ncrypt/ncrypt.h"
#include "opcodes.h"
#include "mailbox.h"
#include "mime.h"
#include "mutt_charset.h"
-#include "mutt_regex.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
#include "parameter.h"
#include <wctype.h>
#include "mutt/mutt.h"
#include "mutt.h"
+#include "mx.h"
#include "pattern.h"
#include "address.h"
#include "body.h"
#include "mailbox.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
-#include "mutt_regex.h"
-#include "mx.h"
#include "ncrypt/ncrypt.h"
#include "opcodes.h"
#include "options.h"
struct EnterState;
struct Envelope;
struct Header;
+struct ListHead;
struct Parameter;
-struct Regex;
-struct ReplaceList;
struct RegexList;
struct State;
-struct ListHead;
struct stat;
struct passwd;
const char *mutt_fqdn(short may_hide_host);
-struct Regex *mutt_compile_regex(const char *s, int flags);
-
void mutt_account_hook(const char *url);
void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv);
void mutt_adv_mktemp(char *s, size_t l);
void mutt_forward_trailer(struct Context *ctx, struct Header *cur, FILE *fp);
void mutt_free_color(int fg, int bg);
void mutt_free_enter_state(struct EnterState **esp);
-void mutt_free_regex(struct Regex **pp);
void mutt_help(int menu);
void mutt_check_lookup_list(struct Body *b, char *type, size_t len);
void mutt_make_attribution(struct Context *ctx, struct Header *cur, FILE *out);
void mutt_version(void);
void mutt_view_attachments(struct Header *hdr);
void mutt_write_address_list(struct Address *adr, FILE *fp, int linelen, int display);
-int mutt_add_to_regex_list(struct RegexList **list, const char *s, int flags, struct Buffer *err);
bool mutt_addr_is_user(struct Address *addr);
int mutt_addwch(wchar_t wc);
int mutt_alias_complete(char *s, size_t buflen);
int mutt_alloc_color(int fg, int bg);
int mutt_combine_color(int fg_attr, int bg_attr);
int mutt_any_key_to_continue(const char *s);
-char *mutt_apply_replace(char *dbuf, size_t dlen, char *sbuf, struct ReplaceList *rlist);
int mutt_buffy_check(bool force);
int mutt_buffy_notify(void);
int mutt_builtin_editor(const char *path, struct Header *msg, struct Header *cur);
bool mutt_is_subscribed_list(struct Address *addr);
bool mutt_is_text_part(struct Body *b);
int mutt_lookup_mime_type(struct Body *att, const char *path);
-bool mutt_match_regex_list(const char *s, struct RegexList *l);
-bool mutt_match_spam_list(const char *s, struct ReplaceList *l, char *text, int textsize);
int mutt_multi_choice(char *prompt, char *letters);
bool mutt_needs_mailcap(struct Body *m);
int mutt_num_postponed(int force);
#include "mime.h"
#include "mutt_curses.h"
#include "mutt_idna.h"
-#include "mutt_regex.h"
#include "ncrypt/ncrypt.h"
#include "options.h"
#include "parameter.h"