<<< Date Index >>>     <<< Thread Index >>>

FEATURE: regexp support for lists/subscribe; alternates change



OK, I know that I risk repeating history here -- mostly because no
archives of the early days of mutt-dev appear to be online, and
because my own e-mail archives don't go back that long. So I guess
I'm not entirely sure if my statement in #1466 that the original
regexp-based list support code was modeled after alternates was
actually true.


Anyway, here's the proposal: We currently have either regular
expressions, or lists of strings which are matched against
identifiers.  I'd like to unify these, at least for some
configuration settings.  The attached patch does just that, for the
list commands, and for $alternates; ironically, this returns the
"alternates" command from the very old days.   With the attached
patfch, you can say things like this:

 subscribe ^mutt-.*@mutt.org ^[0-9]*@bugs.guug.de
 alternates roessler@does-not-exist.(org|net) roessler@xxxxxxxx

(Of course, strictly speaking, these expressions are too lose, as
the dots should each be replaced by '\\.'.)


I'd love to hear your thoughts on this; I have not come to a
decision, yet, if this is the right way to do things.

Flames, comments, and improvements welcome.
-- 
Thomas Roessler · Personal soap box at <http://log.does-not-exist.org/>.
Index: alias.c
===================================================================
RCS file: /cvs/mutt/mutt/alias.c,v
retrieving revision 3.8
diff -u -r3.8 alias.c
--- alias.c     24 Jul 2003 18:40:50 -0000      3.8
+++ alias.c     12 Jan 2004 22:11:27 -0000
@@ -545,8 +545,7 @@
   if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
     return 1;
 
-  if (Alternates.pattern &&
-      regexec (Alternates.rx, addr->mailbox, 0, NULL, 0) == 0)
+  if (mutt_match_rx_list (addr->mailbox, Alternates))
     return 1;
   
   return 0;
Index: globals.h
===================================================================
RCS file: /cvs/mutt/mutt/globals.h,v
retrieving revision 3.6
diff -u -r3.6 globals.h
--- globals.h   15 Jul 2003 11:41:32 -0000      3.6
+++ globals.h   12 Jan 2004 22:11:27 -0000
@@ -121,8 +121,10 @@
 WHERE LIST *Ignore INITVAL(0);
 WHERE LIST *MimeLookupList INITVAL(0);
 WHERE LIST *UnIgnore INITVAL(0);
-WHERE LIST *MailLists INITVAL(0);
-WHERE LIST *SubscribedLists INITVAL(0);
+
+WHERE RX_LIST *Alternates INITVAL(0);
+WHERE RX_LIST *MailLists INITVAL(0);
+WHERE RX_LIST *SubscribedLists INITVAL(0);
 
 /* bit vector for boolean variables */
 #ifdef MAIN_C
Index: hdrline.c
===================================================================
RCS file: /cvs/mutt/mutt/hdrline.c,v
retrieving revision 3.10
diff -u -r3.10 hdrline.c
--- hdrline.c   4 Jan 2004 10:51:49 -0000       3.10
+++ hdrline.c   12 Jan 2004 22:11:28 -0000
@@ -28,31 +28,14 @@
 #include <string.h>
 #include <locale.h>
 
-static int _mutt_is_mail_list (ADDRESS *addr, LIST *p)
-{
-  char *s;
-  if (addr->mailbox)
-  {
-    for (;p; p = p->next)
-    {
-      if (mutt_strncasecmp (addr->mailbox, p->data, mutt_strlen (p->data)) == 
0)
-       return 1;
-      else if (*p->data == '@' && (s = strchr (addr->mailbox, '@')))
-       if (mutt_strncasecmp (s, p->data, mutt_strlen (p->data)) == 0)
-         return 1;
-    }
-  }
-  return 0;
-}
-
 int mutt_is_mail_list (ADDRESS *addr)
 {
-  return _mutt_is_mail_list (addr, MailLists);
+  return mutt_match_rx_list (addr->mailbox, MailLists);
 }
 
 int mutt_is_subscribed_list (ADDRESS *addr)
 {
-  return _mutt_is_mail_list (addr, SubscribedLists);
+  return mutt_match_rx_list (addr->mailbox, SubscribedLists);
 }
 
 /* Search for a mailing list in the list of addresses pointed to by adr.
Index: init.c
===================================================================
RCS file: /cvs/mutt/mutt/init.c,v
retrieving revision 3.17
diff -u -r3.17 init.c
--- init.c      4 Jan 2004 11:10:21 -0000       3.17
+++ init.c      12 Jan 2004 22:11:32 -0000
@@ -320,6 +320,52 @@
   }
 }
 
+static int add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER 
*err)
+{
+  RX_LIST *t, *last = NULL;
+  REGEXP *rx;
+
+  if (!s || !*s)
+    return 0;
+
+  if (!(rx = mutt_compile_regexp (s, flags)))
+  {
+    snprintf (err->data, err->dsize, "Bad regexp: %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 (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
+    {
+      /* already on the list, so just ignore it */
+      last = NULL;
+      break;
+    }
+    if (!last->next)
+      break;
+  }
+
+  if (!*list || last)
+  {
+    t = mutt_new_rx_list();
+    t->rx = rx;
+    if (last)
+    {
+      last->next = t;
+      last = last->next;
+    }
+    else
+      *list = last = t;
+  }
+  else /* duplicate */
+    mutt_free_regexp (&rx);
+
+  return 0;
+}
+
+
 static void remove_from_list (LIST **l, const char *str)
 {
   LIST *p, *last = NULL;
@@ -350,6 +396,36 @@
   }
 }
 
+static void remove_from_rx_list (RX_LIST **l, const char *str)
+{
+  RX_LIST *p, *last = NULL;
+
+  if (mutt_strcmp ("*", str) == 0)
+    mutt_free_rx_list (l);    /* ``unCMD *'' means delete all current entries 
*/
+  else
+  {
+    p = *l;
+    last = NULL;
+    while (p)
+    {
+      if (ascii_strcasecmp (str, p->rx->pattern) == 0)
+      {
+       mutt_free_regexp (&p->rx);
+       if (last)
+         last->next = p->next;
+       else
+         (*l) = p->next;
+       FREE (&p);
+      }
+      else
+      {
+       last = p;
+       p = p->next;
+      }
+    }
+  }
+}
+
 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER 
*err)
 {
   do
@@ -392,6 +468,42 @@
   return 0;
 }
 
+static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER 
*err, int flags)
+{
+  do 
+  {
+    mutt_extract_token (buf, s, 0);
+    if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
+      return -1;
+       
+  }
+  while (MoreArgs (s));
+  
+  return 0;
+}
+
+static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER 
*err)
+{
+  return _parse_rx_list (buf, s, data, err, REG_ICASE);
+}
+
+static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER 
*err)
+{
+  do
+  {
+    mutt_extract_token (buf, s, 0);
+    if (mutt_strcmp (buf->data, "*") == 0)
+    {
+      mutt_free_rx_list ((RX_LIST **) data);
+      break;
+    }
+    remove_from_rx_list ((RX_LIST **) data, buf->data);
+  }
+  while (MoreArgs (s));
+  
+  return 0;
+}
+
 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER 
*err)
 {
   do
@@ -418,8 +530,8 @@
   do
   {
     mutt_extract_token (buf, s, 0);
-    remove_from_list (&MailLists, buf->data);
-    remove_from_list (&SubscribedLists, buf->data);
+    remove_from_rx_list (&MailLists, buf->data);
+    remove_from_rx_list (&SubscribedLists, buf->data);
   }
   while (MoreArgs (s));
 
@@ -431,8 +543,10 @@
   do
   {
     mutt_extract_token (buf, s, 0);
-    add_to_list (&MailLists, buf->data);
-    add_to_list (&SubscribedLists, buf->data);
+    if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
+      return -1;
+    if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
+      return -1;
   }
   while (MoreArgs (s));
 
Index: init.h
===================================================================
RCS file: /cvs/mutt/mutt/init.h,v
retrieving revision 3.42
diff -u -r3.42 init.h
--- init.h      4 Jan 2004 10:55:20 -0000       3.42
+++ init.h      12 Jan 2004 22:11:45 -0000
@@ -157,13 +157,6 @@
   ** message could include a line like "[-- PGP output follows ..." and
   ** give it the same color as your attachment color.
   */
-  { "alternates",      DT_RX,   R_BOTH, UL &Alternates, 0 },
-  /*
-  ** .pp
-  ** A regexp that allows you to specify \fIalternate\fP addresses where
-  ** you receive mail.  This affects Mutt's idea about messages from you
-  ** and addressed to you.
-  */
   { "arrow_cursor",    DT_BOOL, R_BOTH, OPTARROWCURSOR, 0 },
   /*
   ** .pp
@@ -402,7 +395,7 @@
   ** variable at the time the hook is declared.  The default value matches
   ** if the message is either from a user matching the regular expression
   ** given, or if it is from you (if the from address matches
-  ** ``$$alternates'') and is to or cc'ed to a user matching the given
+  ** ``alternates'') and is to or cc'ed to a user matching the given
   ** regular expression.
   */
   { "delete",          DT_QUAD, R_NONE, OPT_DELETE, M_ASKYES },
@@ -1034,8 +1027,8 @@
   { "metoo",           DT_BOOL, R_NONE, OPTMETOO, 0 },
   /*
   ** .pp
-  ** If unset, Mutt will remove your address (see the ``$$alternates''
-  ** variable) from the list of recipients when replying to a message.
+  ** If unset, Mutt will remove your address (see the ``alternates''
+  ** command) from the list of recipients when replying to a message.
   */
   { "menu_scroll",     DT_BOOL, R_NONE, OPTMENUSCROLL, 0 },
   /*
@@ -2734,7 +2727,10 @@
 /* functions used to parse commands in a rc file */
 
 static int parse_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_rx_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
 static int parse_unlist (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_rx_unlist (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+
 static int parse_unlists (BUFFER *, BUFFER *, unsigned long, BUFFER *);
 static int parse_alias (BUFFER *, BUFFER *, unsigned long, BUFFER *);
 static int parse_unalias (BUFFER *, BUFFER *, unsigned long, BUFFER *);
@@ -2754,6 +2750,8 @@
 };
 
 struct command_t Commands[] = {
+  { "alternates",      parse_rx_list,          UL &Alternates },
+  { "unalternates",    parse_rx_unlist,        UL &Alternates },
 #ifdef USE_SOCKET
   { "account-hook",     mutt_parse_hook,        M_ACCOUNTHOOK },
 #endif
@@ -2775,7 +2773,7 @@
   { "iconv-hook",      mutt_parse_hook,        M_ICONVHOOK }, 
 #endif
   { "ignore",          parse_ignore,           0 },
-  { "lists",           parse_list,             UL &MailLists },
+  { "lists",           parse_rx_list,          UL &MailLists },
   { "macro",           mutt_parse_macro,       0 },
   { "mailboxes",       mutt_parse_mailboxes,   M_MAILBOXES },
   { "unmailboxes",     mutt_parse_mailboxes,   M_UNMAILBOXES },
@@ -2808,6 +2806,6 @@
   { "unmy_hdr",                parse_unmy_hdr,         0 },
   { "unscore",         mutt_parse_unscore,     0 },
   { "unset",           parse_set,              M_SET_UNSET },
-  { "unsubscribe",     parse_unlist,           UL &SubscribedLists },
+  { "unsubscribe",     parse_rx_unlist,        UL &SubscribedLists },
   { NULL }
 };
Index: mutt.h
===================================================================
RCS file: /cvs/mutt/mutt/mutt.h,v
retrieving revision 3.22
diff -u -r3.22 mutt.h
--- mutt.h      30 Dec 2003 13:04:20 -0000      3.22
+++ mutt.h      12 Jan 2004 22:11:52 -0000
@@ -75,6 +75,8 @@
 #define INITVAL(x) 
 #endif
 
+#include "mutt_regex.h"
+
 /* flags for mutt_copy_header() */
 #define CH_UPDATE      1      /* update the status and x-status fields? */
 #define CH_WEED                (1<<1) /* weed the headers? */
@@ -504,8 +506,16 @@
   struct list_t *next;
 } LIST;
 
+typedef struct rx_list_t
+{
+  REGEXP *rx;
+  struct rx_list_t *next;
+} RX_LIST;
+
 #define mutt_new_list() safe_calloc (1, sizeof (LIST))
+#define mutt_new_rx_list() safe_calloc (1, sizeof (RX_LIST))
 void mutt_free_list (LIST **);
+void mutt_free_rx_list (RX_LIST **);
 int mutt_matches_ignore (const char *, LIST *);
 
 /* add an element to a list */
@@ -720,7 +730,6 @@
   HEADER *sort_key;
 } THREAD;
 
-#include "mutt_regex.h"
 
 /* flag to mutt_pattern_comp() */
 #define M_FULL_MSG     1       /* enable body and header matching */
Index: mutt_regex.h
===================================================================
RCS file: /cvs/mutt/mutt/mutt_regex.h,v
retrieving revision 3.1
diff -u -r3.1 mutt_regex.h
--- mutt_regex.h        11 Dec 2002 11:19:40 -0000      3.1
+++ mutt_regex.h        12 Jan 2004 22:11:52 -0000
@@ -46,7 +46,6 @@
   int not;             /* do not match */
 } REGEXP;
 
-WHERE REGEXP Alternates;
 WHERE REGEXP Mask;
 WHERE REGEXP QuoteRegexp;
 WHERE REGEXP ReplyRegexp;
Index: muttlib.c
===================================================================
RCS file: /cvs/mutt/mutt/muttlib.c,v
retrieving revision 3.17
diff -u -r3.17 muttlib.c
--- muttlib.c   4 Oct 2003 20:34:59 -0000       3.17
+++ muttlib.c   12 Jan 2004 22:11:52 -0000
@@ -1346,3 +1346,51 @@
   return vstring;
 }
 
+REGEXP *mutt_compile_regexp (const char *s, int flags)
+{
+  REGEXP *pp = safe_calloc (sizeof (REGEXP), 1);
+  pp->pattern = safe_strdup (s);
+  pp->rx = safe_calloc (sizeof (regex_t), 1);
+  if (REGCOMP (pp->rx, NONULL(s), flags) != 0)
+    mutt_free_regexp (&pp);
+
+  return pp;
+}
+
+void mutt_free_regexp (REGEXP **pp)
+{
+  FREE (&(*pp)->pattern);
+  regfree ((*pp)->rx);
+  FREE (&(*pp)->rx);
+  FREE (pp);
+}
+
+void mutt_free_rx_list (RX_LIST **list)
+{
+  RX_LIST *p;
+  
+  if (!list) return;
+  while (*list)
+  {
+    p = *list;
+    *list = (*list)->next;
+    mutt_free_regexp (&p->rx);
+    FREE (&p);
+  }
+}
+
+int mutt_match_rx_list (const char *s, RX_LIST *l)
+{
+  if (!s)  return 0;
+  
+  for (; l; l = l->next)
+  {
+    if (regexec (l->rx->rx, s, (size_t) 0, (regmatch_t *) 0, (int) 0) == 0)
+    {
+      dprint (5, (debugfile, "mutt_match_rx_list: %s matches %s\n", s, 
l->rx->pattern));
+      return 1;
+    }
+  }
+
+  return 0;
+}
Index: protos.h
===================================================================
RCS file: /cvs/mutt/mutt/protos.h,v
retrieving revision 3.18
diff -u -r3.18 protos.h
--- protos.h    4 Oct 2003 20:34:59 -0000       3.18
+++ protos.h    12 Jan 2004 22:11:52 -0000
@@ -136,6 +136,8 @@
 
 const char *mutt_fqdn(short);
 
+REGEXP *mutt_compile_regexp (const char *, int);
+
 void mutt_account_hook (const char* url);
 void mutt_add_to_reference_headers (ENVELOPE *env, ENVELOPE *curenv, LIST 
***pp, LIST ***qq);
 void mutt_adv_mktemp (char *, size_t);
@@ -181,6 +183,7 @@
 void mutt_free_envelope (ENVELOPE **);
 void mutt_free_header (HEADER **);
 void mutt_free_parameter (PARAMETER **);
+void mutt_free_regexp (REGEXP **);
 void mutt_generate_header (char *, size_t, HEADER *, int);
 void mutt_help (int);
 void mutt_draw_tree (CONTEXT *);
@@ -287,6 +290,7 @@
 int mutt_is_text_part (BODY *);
 int mutt_is_valid_mailbox (const char *);
 int mutt_lookup_mime_type (BODY *, const char *);
+int mutt_match_rx_list (const char *, RX_LIST *);
 int mutt_messages_in_thread (CONTEXT *, HEADER *, int);
 int mutt_multi_choice (char *prompt, char *letters);
 int mutt_needs_mailcap (BODY *);
Index: doc/muttrc.man.head
===================================================================
RCS file: /cvs/mutt/mutt/doc/muttrc.man.head,v
retrieving revision 3.9
diff -u -r3.9 muttrc.man.head
--- doc/muttrc.man.head 29 Sep 2003 16:34:32 -0000      3.9
+++ doc/muttrc.man.head 12 Jan 2004 22:11:52 -0000
@@ -76,6 +76,17 @@
 all aliases when \(lq\fB*\fP\(rq is used as an argument.
 .PP
 .nf
+\fBalternates\fP \fIregexp\fP [ \fB,\fP \fIregexp\fP [ ... ]]
+\fBunalternates\fP [\fB * \fP | \fIregexp\fP [ \fB,\fP \fIregexp\fP [ ... ]] ]
+.fi
+.IP
+\fBalternates\fP is used to inform mutt about alternate addresses
+where you receive mail; you can use regular expressions to specify
+alternate addresses.  This affects mutt's idea about messages
+from you, and messages addressed to you.  \fBunalternates\fP removes
+a regular expression from the list of known alternates.
+.PP
+.nf
 \fBalternative_order\fP \fItype\fP[\fB/\fP\fIsubtype\fP] [ ... ]
 \fBunalternative_order\fP [\fB * \fP | \fItype\fP/\fIsubtype\fP] [...]
 .fi
@@ -213,18 +224,15 @@
 the above mentioned list of ignored headers.
 .PP
 .nf
-\fBlists\fP \fIaddress\fP [ \fIaddress\fP ... ]
-\fBunlists\fP \fIaddress\fP [ \fIaddress\fP ... ]
-\fBsubscribe\fP \fIaddress\fP [ \fIaddress\fP ... ]
-\fBunsubscribe\fP \fIaddress\fP [ \fIaddress\fP ... ]
+\fBlists\fP \fIregexp\fP [ \fIregexp\fP ... ]
+\fBunlists\fP \fIregexp\fP [ \fIregexp\fP ... ]
+\fBsubscribe\fP \fIregexp\fP [ \fIregexp\fP ... ]
+\fBunsubscribe\fP \fIregexp\fP [ \fIregexp\fP ... ]
 .fi
 .IP
-Mutt maintains two lists of mailing list addresses, a list of
+Mutt maintains two lists of mailing list address patterns, a list of
 subscribed mailing lists, and a list of known mailing lists.  All
-subscribed mailing lists are known.  A mail address matches a
-mailing list if it begins with the given address.  For example, the
-lists pattern \(lqmutt-\(rq will match mutt-dev@xxxxxxxx and
-mutt-users@xxxxxxxxx
+subscribed mailing lists are known.  Patterns use regular expressions.
 .IP
 The \fBlists\fP command adds a mailing list address to the list of
 known mailing lists.  The \fBunlists\fP command removes a mailing

Attachment: pgp9aw0H6Ug3p.pgp
Description: PGP signature