From: Richard Russon Date: Sun, 24 Dec 2017 16:52:54 +0000 (+0000) Subject: move functions to muttlib X-Git-Tag: neomutt-20180223~58^2~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=97287737e06b83cf1409ec682d4c714fdc9cfba5;p=neomutt move functions to muttlib --- diff --git a/Makefile.autosetup b/Makefile.autosetup index f53bfe7eb..89b229559 100644 --- a/Makefile.autosetup +++ b/Makefile.autosetup @@ -94,7 +94,7 @@ ALLOBJS+= $(NEOMUTTOBJS) 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) diff --git a/browser.c b/browser.c index 828feab6f..be42a3046 100644 --- a/browser.c +++ b/browser.c @@ -53,7 +53,6 @@ #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" diff --git a/color.c b/color.c index 708bf5277..d4bf03550 100644 --- a/color.c +++ b/color.c @@ -34,7 +34,6 @@ #include "keymap.h" #include "mutt_curses.h" #include "mutt_menu.h" -#include "mutt_regex.h" #include "options.h" #include "pattern.h" #include "protos.h" diff --git a/conn/ssl_gnutls.c b/conn/ssl_gnutls.c index 0afc6a95f..5147d5397 100644 --- a/conn/ssl_gnutls.c +++ b/conn/ssl_gnutls.c @@ -47,6 +47,7 @@ #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" @@ -55,7 +56,6 @@ #include "keymap.h" #include "mutt_account.h" #include "mutt_menu.h" -#include "mutt_regex.h" #include "opcodes.h" #include "options.h" #include "protos.h" diff --git a/curs_lib.c b/curs_lib.c index 2737381fd..b895f129d 100644 --- a/curs_lib.c +++ b/curs_lib.c @@ -47,7 +47,6 @@ #include "mbyte.h" #include "mutt_curses.h" #include "mutt_menu.h" -#include "mutt_regex.h" #include "opcodes.h" #include "options.h" #include "pager.h" diff --git a/globals.h b/globals.h index 5432663e1..b21f26cb8 100644 --- a/globals.h +++ b/globals.h @@ -26,7 +26,6 @@ #include #include "mutt/mutt.h" #include "where.h" -#include "mutt_regex.h" #ifdef MAIN_C /* so that global vars get included */ @@ -321,6 +320,12 @@ WHERE int NmQueryWindowCurrentPosition; 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", diff --git a/hcache/hcache.c b/hcache/hcache.c index 9d5ded3df..db8aa97c5 100644 --- a/hcache/hcache.c +++ b/hcache/hcache.c @@ -58,7 +58,6 @@ #include "header.h" #include "mbyte.h" #include "mutt_charset.h" -#include "mutt_regex.h" #include "parameter.h" #include "protos.h" #include "tags.h" diff --git a/hook.c b/hook.c index 86dff6035..05d08a2a8 100644 --- a/hook.c +++ b/hook.c @@ -36,7 +36,6 @@ #include "globals.h" #include "header.h" #include "mailbox.h" -#include "mutt_regex.h" #include "ncrypt/ncrypt.h" #include "options.h" #include "pattern.h" diff --git a/imap/browse.c b/imap/browse.c index e9d02d74f..d4124a70b 100644 --- a/imap/browse.c +++ b/imap/browse.c @@ -47,7 +47,6 @@ #include "globals.h" #include "imap/imap.h" #include "mutt_account.h" -#include "mutt_regex.h" #include "options.h" #include "protos.h" #ifdef ENABLE_NLS diff --git a/init.c b/init.c index 2ab74487d..65f647db5 100644 --- a/init.c +++ b/init.c @@ -56,7 +56,6 @@ #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" @@ -796,185 +795,6 @@ static void add_to_stailq(struct ListHead *head, const char *str) 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 diff --git a/init.h b/init.h index e2f39270f..26bfd27c3 100644 --- a/init.h +++ b/init.h @@ -35,7 +35,7 @@ #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" @@ -1756,7 +1756,7 @@ struct Option MuttVars[] = { ** .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 diff --git a/main.c b/main.c index 8079de446..dab9fdc26 100644 --- a/main.c +++ b/main.c @@ -49,7 +49,6 @@ #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" diff --git a/menu.c b/menu.c index 76a111d74..699405214 100644 --- a/menu.c +++ b/menu.c @@ -35,7 +35,6 @@ #include "mbyte.h" #include "mutt_curses.h" #include "mutt_menu.h" -#include "mutt_regex.h" #include "opcodes.h" #include "options.h" #include "pattern.h" diff --git a/mutt.h b/mutt.h index 6451849a1..06b0d092b 100644 --- a/mutt.h +++ b/mutt.h @@ -30,11 +30,7 @@ #include #include -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 @@ -303,13 +299,8 @@ enum QuadOptionVars #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() */ diff --git a/mutt/mutt.h b/mutt/mutt.h index 7da7f9cba..ee2723376 100644 --- a/mutt/mutt.h +++ b/mutt/mutt.h @@ -42,6 +42,7 @@ * -# @subpage md5 * -# @subpage memory * -# @subpage message + * -# @subpage regex * -# @subpage sha1 * -# @subpage signal * -# @subpage string @@ -64,6 +65,7 @@ #include "md5.h" #include "memory.h" #include "message.h" +#include "regex3.h" #include "sha1.h" #include "signal2.h" #include "string2.h" diff --git a/mutt/regex.c b/mutt/regex.c new file mode 100644 index 000000000..f2601b2d2 --- /dev/null +++ b/mutt/regex.c @@ -0,0 +1,548 @@ +/** + * @file + * Manage regular expressions + * + * @authors + * Copyright (C) 2017 Richard Russon + * + * @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 . + */ + +/** + * @page regex Manage regular expressions + * + * Manage regular expressions. + * + * | Function | Description + * | :------------------------ | :------------------------------------------ + */ + +#include "config.h" +#include +#include +#include +#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; +} diff --git a/mutt_regex.h b/mutt/regex3.h similarity index 54% rename from mutt_regex.h rename to mutt/regex3.h index 7e5117b71..49afca86e 100644 --- a/mutt_regex.h +++ b/mutt/regex3.h @@ -1,9 +1,9 @@ /** * @file - * Constants/structs for handling (lists of) regular expressions + * Manage regular expressions * * @authors - * Copyright (C) 1996-2000 Michael R. Elkins + * Copyright (C) 2017 Richard Russon * * @copyright * This program is free software: you can redistribute it and/or modify it under @@ -20,19 +20,20 @@ * this program. If not, see . */ -/* - * A (more) generic interface to regular expression matching - */ - #ifndef _MUTT_REGEX_H #define _MUTT_REGEX_H #include -#include "where.h" +#include -/* 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 @@ -62,7 +63,7 @@ struct Regex { char *pattern; /**< printable version */ regex_t *regex; /**< compiled expression */ - bool not : 1; /**< do not match */ + bool not; /**< do not match */ }; /** @@ -85,10 +86,21 @@ struct ReplaceList 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 */ diff --git a/muttlib.c b/muttlib.c index 00c565ef8..44372304c 100644 --- a/muttlib.c +++ b/muttlib.c @@ -54,7 +54,6 @@ #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" @@ -116,42 +115,6 @@ void mutt_adv_mktemp(char *s, size_t l) } } -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 * @@ -785,103 +748,6 @@ void mutt_safe_path(char *s, size_t l, struct Address *a) *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 @@ -1558,159 +1424,6 @@ const char *mutt_make_version(void) 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); diff --git a/ncrypt/pgp.c b/ncrypt/pgp.c index b623f8902..967916a35 100644 --- a/ncrypt/pgp.c +++ b/ncrypt/pgp.c @@ -53,7 +53,6 @@ #include "mime.h" #include "mutt_charset.h" #include "mutt_curses.h" -#include "mutt_regex.h" #include "ncrypt.h" #include "options.h" #include "parameter.h" diff --git a/pager.c b/pager.c index 3b06494e0..58524783e 100644 --- a/pager.c +++ b/pager.c @@ -48,7 +48,6 @@ #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" diff --git a/parse.c b/parse.c index 56da6e91e..32b845c19 100644 --- a/parse.c +++ b/parse.c @@ -38,7 +38,6 @@ #include "mailbox.h" #include "mime.h" #include "mutt_charset.h" -#include "mutt_regex.h" #include "ncrypt/ncrypt.h" #include "options.h" #include "parameter.h" diff --git a/pattern.c b/pattern.c index 936257f01..9acbec4ee 100644 --- a/pattern.c +++ b/pattern.c @@ -37,6 +37,7 @@ #include #include "mutt/mutt.h" #include "mutt.h" +#include "mx.h" #include "pattern.h" #include "address.h" #include "body.h" @@ -49,8 +50,6 @@ #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" diff --git a/protos.h b/protos.h index f0877e394..e5a16bef4 100644 --- a/protos.h +++ b/protos.h @@ -43,12 +43,10 @@ struct Context; struct EnterState; struct Envelope; struct Header; +struct ListHead; struct Parameter; -struct Regex; -struct ReplaceList; struct RegexList; struct State; -struct ListHead; struct stat; struct passwd; @@ -141,8 +139,6 @@ const char *mutt_make_version(void); 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); @@ -189,7 +185,6 @@ void mutt_forward_intro(struct Context *ctx, struct Header *cur, FILE *fp); 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); @@ -231,7 +226,6 @@ void mutt_update_encoding(struct Body *a); 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); @@ -240,7 +234,6 @@ void mutt_alias_delete_reverse(struct Alias *t); 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); @@ -294,8 +287,6 @@ bool mutt_is_message_type(int type, const char *subtype); 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); diff --git a/send.c b/send.c index 749d98a31..7531445a4 100644 --- a/send.c +++ b/send.c @@ -47,7 +47,6 @@ #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"