A happy new year. This patch (against current CVS) adds the concept of "pattern groups" to mutt. A pattern group consists of a list of e-mail addresses and a list of regular expressions. If any of the addresses or any of the regular expressions match, the pattern group matches. To match against a pattern group, use '$' instead of '~' (better character suggestions would be much appreciated.) To populate a pattern group, add -group <groupname> immediately after the alias, subscribe, lists, and alternates commands: alias -group mutt mutt-dev@xxxxxxxx alias -group mutt mutt-users@xxxxxxxx alternates -group mutt roessler@xxxxxxxx lists -group mutt .*@mutt.org A hook can then talk about these groups like this: send-hook '$C mutt | $f mutt' ... Missing features include support for multiple groups in a single alias / alternates / lists, and some group maintenance commands (such as "add / remove this regexp / address to this group"). Documentation is also missing. Some particularly interesting perspective that one could do with this would be to refactor the existing alternates / lists / subscribe code on top of this. One could then do things like: alias -group mutt -group alternates rmutt roessler@xxxxxxxx I haven't added this to CVS, yet, and would welcome any comments. I intend to commit it, though, in particular since I'm using it to organize my own mailing habits. Regards, -- Thomas Roessler · Personal soap box at <http://log.does-not-exist.org/>.
Index: Makefile.am =================================================================== RCS file: /cvs/mutt/mutt/Makefile.am,v retrieving revision 3.39 diff -u -r3.39 Makefile.am --- Makefile.am 20 Dec 2005 17:50:46 -0000 3.39 +++ Makefile.am 9 Jan 2006 13:24:27 -0000 @@ -20,7 +20,8 @@ addrbook.c alias.c attach.c base64.c browser.c buffy.c color.c \ crypt.c cryptglue.c \ commands.c complete.c compose.c copy.c curs_lib.c curs_main.c date.c \ - edit.c enter.c flags.c init.c filter.c from.c getdomain.c \ + edit.c enter.c flags.c init.c filter.c from.c \ + getdomain.c group.c \ handler.c hash.c hdrline.c headers.c help.c hook.c keymap.c \ main.c mbox.c menu.c mh.c mx.c pager.c parse.c pattern.c \ postpone.c query.c recvattach.c recvcmd.c \ Index: globals.h =================================================================== RCS file: /cvs/mutt/mutt/globals.h,v retrieving revision 3.27 diff -u -r3.27 globals.h --- globals.h 22 Dec 2005 17:46:40 -0000 3.27 +++ globals.h 9 Jan 2006 13:24:27 -0000 @@ -139,6 +139,8 @@ WHERE char *CurrentFolder; WHERE char *LastFolder; +WHERE HASH *Groups; + WHERE LIST *AutoViewList INITVAL(0); WHERE LIST *AlternativeOrderList INITVAL(0); WHERE LIST *AttachAllow INITVAL(0); Index: group.c =================================================================== RCS file: group.c diff -N group.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ group.c 9 Jan 2006 13:24:27 -0000 @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2006 Thomas Roessler <roessler@xxxxxxxxxxxxxxxxxx> + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "mapping.h" +#include "mutt_curses.h" +#include "mutt_regex.h" +#include "history.h" +#include "keymap.h" +#include "mbyte.h" +#include "charset.h" +#include "mutt_crypt.h" +#include "mutt_idna.h" + +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/utsname.h> +#include <errno.h> +#include <sys/wait.h> + +group_t *mutt_pattern_group (const char *k) +{ + group_t *p; + + if (!k) + return 0; + + if (!(p = hash_find (Groups, k))) + { + dprint (2, (debugfile, "mutt_pattern_group: Creating group %s.\n", k)); + p = safe_calloc (1, sizeof (group_t)); + p->name = safe_strdup (k); + hash_insert (Groups, p->name, p, 0); + } + + return p; +} + +void mutt_group_add_adrlist (group_t *g, ADDRESS *a) +{ + ADDRESS **p; + + if (!g) + return; + if (!a) + return; + + for (p = &g->as; *p; p = &((*p)->next)) + ; + + *p = rfc822_cpy_adr (a); +} + +int mutt_group_match (group_t *g, const char *s) +{ + ADDRESS *ap; + + + if (s && g) + { + if (mutt_match_rx_list (s, g->rs)) + return 1; + for (ap = g->as; ap; ap = ap->next) + if (ap->mailbox && !mutt_strcasecmp (s, ap->mailbox)) + return 1; + } + return 0; +} + Index: init.c =================================================================== RCS file: /cvs/mutt/mutt/init.c,v retrieving revision 3.47 diff -u -r3.47 init.c --- init.c 16 Dec 2005 18:49:40 -0000 3.47 +++ init.c 9 Jan 2006 13:24:28 -0000 @@ -134,7 +134,7 @@ (ch == '#' && !(flags & M_TOKEN_COMMENT)) || (ch == '=' && (flags & M_TOKEN_EQUAL)) || (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) || - ((flags & M_TOKEN_PATTERN) && strchr ("~=!|", ch))) + ((flags & M_TOKEN_PATTERN) && strchr ("~$=!|", ch))) break; } @@ -652,14 +652,40 @@ static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) { + short first = 1; + group_t *group = NULL; + _alternates_clean(); do { mutt_extract_token (buf, s, 0); + + if (first && !mutt_strcasecmp (buf->data, "-group")) + { + if (!MoreArgs (s)) + { + strfcpy (err->data, _("-group: no group name"), err->dsize); + return -1; + } + + mutt_extract_token (buf, s, 0); + group = mutt_pattern_group (buf->data); + + if (!MoreArgs (s)) + break; + + mutt_extract_token (buf, s, 0); + } + + first = 0; + remove_from_rx_list (&UnAlternates, buf->data); if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0) return -1; + /* XXX -- unclean encapsulation */ + if (group && add_to_rx_list (&group->rs, buf->data, REG_ICASE, err) != 0) + return -1; } while (MoreArgs (s)); @@ -780,13 +806,40 @@ static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) { + short first = 1; + group_t *group = NULL; + do { mutt_extract_token (buf, s, 0); + + if (first && !mutt_strcasecmp (buf->data, "-group")) + { + if (!MoreArgs (s)) + { + strfcpy (err->data, _("-group: no group name"), err->dsize); + return -1; + } + + mutt_extract_token (buf, s, 0); + group = mutt_pattern_group (buf->data); + + if (!MoreArgs (s)) + break; + + mutt_extract_token (buf, s, 0); + } + + first = 0; + remove_from_rx_list (&UnMailLists, buf->data); if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0) return -1; + + /* XXX -- unclean encapsulation */ + if (group && add_to_rx_list (&group->rs, buf->data, REG_ICASE, err) != 0) + return -1; } while (MoreArgs (s)); @@ -1077,9 +1130,32 @@ static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) { + short first = 1; + group_t *group = NULL; + do { mutt_extract_token (buf, s, 0); + + if (first && !mutt_strcasecmp (buf->data, "-group")) + { + if (!MoreArgs (s)) + { + strfcpy (err->data, _("-group: no group name"), err->dsize); + return -1; + } + + mutt_extract_token (buf, s, 0); + group = mutt_pattern_group (buf->data); + + if (!MoreArgs (s)) + break; + + mutt_extract_token (buf, s, 0); + } + + first = 0; + remove_from_rx_list (&UnMailLists, buf->data); remove_from_rx_list (&UnSubscribedLists, buf->data); @@ -1087,6 +1163,9 @@ return -1; if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0) return -1; + /* XXX -- unclean encapsulation */ + if (group && add_to_rx_list (&group->rs, buf->data, REG_ICASE, err) != 0) + return -1; } while (MoreArgs (s)); @@ -1161,6 +1240,7 @@ ALIAS *tmp = Aliases; ALIAS *last = NULL; char *estr = NULL; + group_t *group = NULL; if (!MoreArgs (s)) { @@ -1173,6 +1253,27 @@ dprint (2, (debugfile, "parse_alias: First token is '%s'.\n", buf->data)); + if (!mutt_strcasecmp (buf->data, "-group")) + { + if (!MoreArgs (s)) + { + strfcpy (err->data, _("-group: no group name"), err->dsize); + return (-1); + } + + mutt_extract_token (buf, s, 0); + + group = mutt_pattern_group (buf->data); + + if (!MoreArgs (s)) + { + strfcpy (err->data, _("alias: no address"), err->dsize); + return (-1); + } + + mutt_extract_token (buf, s, 0); + } + /* check to see if an alias with this name already exists */ for (; tmp; tmp = tmp->next) { @@ -1202,7 +1303,9 @@ mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON); dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n", buf->data)); + tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data); + if (last) last->next = tmp; else @@ -1213,6 +1316,11 @@ estr, tmp->name); return -1; } + + if (group) + mutt_group_add_adrlist (group, tmp->addr); + + #ifdef DEBUG if (debuglevel >= 2) { @@ -2541,6 +2649,8 @@ err.data = error; err.dsize = sizeof (error); + Groups = hash_create (1031); + /* * XXX - use something even more difficult to predict? */ Index: mutt.h =================================================================== RCS file: /cvs/mutt/mutt/mutt.h,v retrieving revision 3.62 diff -u -r3.62 mutt.h --- mutt.h 12 Dec 2005 17:24:11 -0000 3.62 +++ mutt.h 9 Jan 2006 13:24:28 -0000 @@ -798,18 +798,30 @@ M_MATCH_FULL_ADDRESS = 1 } pattern_exec_flag; +typedef struct group_t +{ + ADDRESS *as; + RX_LIST *rs; + char *name; +} group_t; + typedef struct pattern_t { short op; unsigned int not : 1; unsigned int alladdr : 1; unsigned int stringmatch : 1; + unsigned int groupmatch : 1; int min; int max; struct pattern_t *next; struct pattern_t *child; /* arguments to logical op */ - char *str; - regex_t *rx; + union + { + regex_t *rx; + group_t *g; + char *str; + } p; } pattern_t; typedef struct Index: pattern.c =================================================================== RCS file: /cvs/mutt/mutt/pattern.c,v retrieving revision 3.27 diff -u -r3.27 pattern.c --- pattern.c 24 Nov 2005 12:50:27 -0000 3.27 +++ pattern.c 9 Jan 2006 13:24:29 -0000 @@ -278,19 +278,24 @@ if (pat->stringmatch) { - pat->str = safe_strdup (buf.data); + pat->p.str = safe_strdup (buf.data); + FREE (&buf.data); + } + else if (pat->groupmatch) + { + pat->p.g = mutt_pattern_group (buf.data); FREE (&buf.data); } else { - pat->rx = safe_malloc (sizeof (regex_t)); - r = REGCOMP (pat->rx, buf.data, REG_NEWLINE | REG_NOSUB | mutt_which_case (buf.data)); + pat->p.rx = safe_malloc (sizeof (regex_t)); + r = REGCOMP (pat->p.rx, buf.data, REG_NEWLINE | REG_NOSUB | mutt_which_case (buf.data)); FREE (&buf.data); if (r) { - regerror (r, pat->rx, err->data, err->dsize); - regfree (pat->rx); - FREE (&pat->rx); + regerror (r, pat->p.rx, err->data, err->dsize); + regfree (pat->p.rx); + FREE (&pat->p.rx); return (-1); } } @@ -697,9 +702,11 @@ static int patmatch (const pattern_t* pat, const char* buf) { if (pat->stringmatch) - return !strstr (buf, pat->str); + return !strstr (buf, pat->p.str); + else if (pat->groupmatch) + return !mutt_group_match (pat->p.g, buf); else - return regexec (pat->rx, buf, 0, NULL, 0); + return regexec (pat->p.rx, buf, 0, NULL, 0); } static struct pattern_flags *lookup_tag (char tag) @@ -739,12 +746,16 @@ tmp = *pat; *pat = (*pat)->next; - if (tmp->rx) + if (tmp->stringmatch) + FREE (&tmp->p.str); + else if (tmp->groupmatch) + tmp->p.g = NULL; + else if (tmp->p.rx) { - regfree (tmp->rx); - FREE (&tmp->rx); + regfree (tmp->p.rx); + FREE (&tmp->p.rx); } - FREE (&tmp->str); + if (tmp->child) mutt_pattern_free (&tmp->child); FREE (&tmp); @@ -808,6 +819,7 @@ not = 0; alladdr = 0; break; + case '$': case '=': case '~': if (implicit && or) @@ -825,6 +837,7 @@ tmp->not = not; tmp->alladdr = alladdr; tmp->stringmatch = (*ps.dptr == '=') ? 1 : 0; + tmp->groupmatch = (*ps.dptr == '$') ? 1 : 0; not = 0; alladdr = 0; @@ -1164,7 +1177,7 @@ * equivalences? */ - if (!strchr (s, '~') && !strchr (s, '=')) /* yup, so spoof a real request */ + if (!strchr (s, '~') && !strchr (s, '=') && !strchr (s, '$')) /* yup, so spoof a real request */ { /* convert old tokens into the new format */ if (ascii_strcasecmp ("all", s) == 0 || Index: protos.h =================================================================== RCS file: /cvs/mutt/mutt/protos.h,v retrieving revision 3.42 diff -u -r3.42 protos.h --- protos.h 20 Dec 2005 17:50:47 -0000 3.42 +++ protos.h 9 Jan 2006 13:24:29 -0000 @@ -72,6 +72,7 @@ void mutt_FormatString (char *, size_t, const char *, format_t *, unsigned long, format_flag); void mutt_parse_content_type (char *, BODY *); void mutt_generate_boundary (PARAMETER **); +void mutt_group_add_adrlist (group_t *, ADDRESS *); void mutt_delete_parameter (const char *attribute, PARAMETER **p); void mutt_set_parameter (const char *, const char *, PARAMETER **); @@ -144,6 +145,8 @@ const char *mutt_fqdn(short); +group_t *mutt_pattern_group (const char *); + REGEXP *mutt_compile_regexp (const char *, int); void mutt_account_hook (const char* url); @@ -291,6 +294,7 @@ #define mutt_get_password(A,B,C) mutt_get_field_unbuffered(A,B,C,M_PASS) int mutt_get_postponed (CONTEXT *, HEADER *, HEADER **, char *, size_t); int mutt_get_tmp_attachment (BODY *); +int mutt_group_match (group_t *g, const char *s); int mutt_index_menu (void); int mutt_invoke_sendmail (ADDRESS *, ADDRESS *, ADDRESS *, ADDRESS *, const char *, int); int mutt_is_autoview (BODY *, const char *); Index: imap/imap.c =================================================================== RCS file: /cvs/mutt/mutt/imap/imap.c,v retrieving revision 3.77 diff -u -r3.77 imap.c --- imap/imap.c 3 Jan 2006 17:35:18 -0000 3.77 +++ imap/imap.c 9 Jan 2006 13:24:29 -0000 @@ -1657,13 +1657,13 @@ mutt_buffer_addstr (buf, "HEADER "); /* extract header name */ - if (! (delim = strchr (pat->str, ':'))) + if (! (delim = strchr (pat->p.str, ':'))) { - mutt_error (_("Header search without header name: %s"), pat->str); + mutt_error (_("Header search without header name: %s"), pat->p.str); return -1; } *delim = '\0'; - imap_quote_string (term, sizeof (term), pat->str); + imap_quote_string (term, sizeof (term), pat->p.str); mutt_buffer_addstr (buf, term); mutt_buffer_addch (buf, ' '); @@ -1676,12 +1676,12 @@ break; case M_BODY: mutt_buffer_addstr (buf, "BODY "); - imap_quote_string (term, sizeof (term), pat->str); + imap_quote_string (term, sizeof (term), pat->p.str); mutt_buffer_addstr (buf, term); break; case M_WHOLE_MSG: mutt_buffer_addstr (buf, "TEXT "); - imap_quote_string (term, sizeof (term), pat->str); + imap_quote_string (term, sizeof (term), pat->p.str); mutt_buffer_addstr (buf, term); break; }
Attachment:
pgpUxcoA7kxIO.pgp
Description: PGP signature