On 2020-06-15 19:37:21 -0700, Kevin J. McCarthy wrote:
> On Mon, Jun 15, 2020 at 12:34:50PM -0500, Corey Minyard wrote:
> > I have a bookmark set to http://www.mutt.org/doc/manual/#patterns
> > because, well, I can't remember all those patterns.  I was wondering if
> > a command could be added so help text could be displayed wherever a
> > pattern modifiers is prompted for.  It would make my life a little
> > easier.
> 
> Thanks for the suggestion.  I'll try to think of what might be feasible.

There's Tamo's patch that does that for ~[Tab] to get help on the
patterns.

FYI, I'm using this patch merged together with the addition of
~a and %a pattern modifiers that do:

<row><entry>~a <emphasis>EXPR</emphasis></entry><entry>messages which contain 
<emphasis>EXPR</emphasis> in some envelope address field</entry></row>
<row><entry>%a <emphasis>GROUP</emphasis></entry><entry>messages which contain 
a member of <emphasis>GROUP</emphasis> in some envelope address 
field</entry></row>

I've maintained this patch since 2008 and modified it when need be.
I'm attaching it.

-- 
Vincent Lefèvre <vinc...@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)
diff --git a/PATCHES b/PATCHES
index e69de29b..a3e1715f 100644
--- a/PATCHES
+++ b/PATCHES
@@ -0,0 +1 @@
+patch-20200424.tamovl.patterns.1
diff --git a/curs_lib.c b/curs_lib.c
index 92eda9b1..18b169f3 100644
--- a/curs_lib.c
+++ b/curs_lib.c
@@ -248,6 +248,7 @@ int _mutt_buffer_get_field (const char *field, BUFFER 
*buffer, int complete, int
     mutt_refresh ();
     mutt_window_getyx (MuttMessageWindow, NULL, &x);
     ret = _mutt_enter_string (buffer->data, buffer->dsize, x, complete, 
multiple, files, numfiles, es);
+    complete &= ~(MUTT_CLEAR);
   }
   while (ret == 1);
 
diff --git a/curs_main.c b/curs_main.c
index b1891354..7686754a 100644
--- a/curs_main.c
+++ b/curs_main.c
@@ -973,7 +973,7 @@ int mutt_index_menu (void)
 
        CHECK_ATTACH;
        mutt_pattern_func (MUTT_DELETE, _("Delete messages matching: "));
-       menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
+       menu->redraw |= REDRAW_FULL;
        break;
 
 #ifdef USE_POP
@@ -1033,10 +1033,8 @@ int mutt_index_menu (void)
            menu->current = 0;
          if (Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS)
            mutt_draw_tree (Context);
-         menu->redraw = REDRAW_FULL;
        }
-        if (Context->pattern)
-         mutt_message _("To view all messages, limit to \"all\".");
+       menu->redraw = REDRAW_FULL;
        break;
 
       case OP_QUIT:
@@ -1096,6 +1094,8 @@ int mutt_index_menu (void)
          menu->current = menu->oldcurrent;
        else
          menu->redraw = REDRAW_MOTION;
+       if (op == OP_SEARCH || op == OP_SEARCH_REVERSE)
+         menu->redraw = REDRAW_FULL;
        break;
 
       case OP_SORT:
@@ -1151,7 +1151,7 @@ int mutt_index_menu (void)
        CHECK_MSGCOUNT;
         CHECK_VISIBLE;
        mutt_pattern_func (MUTT_TAG, _("Tag messages matching: "));
-       menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
+       menu->redraw |= REDRAW_FULL;
        break;
 
       case OP_MAIN_UNDELETE_PATTERN:
@@ -1162,16 +1162,16 @@ int mutt_index_menu (void)
         /* L10N: CHECK_ACL */
        CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message(s)"));
 
-       if (mutt_pattern_func (MUTT_UNDELETE, _("Undelete messages matching: 
")) == 0)
-         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
+       mutt_pattern_func (MUTT_UNDELETE, _("Undelete messages matching: "));
+       menu->redraw |= REDRAW_FULL;
        break;
 
       case OP_MAIN_UNTAG_PATTERN:
 
        CHECK_MSGCOUNT;
         CHECK_VISIBLE;
-       if (mutt_pattern_func (MUTT_UNTAG, _("Untag messages matching: ")) == 0)
-         menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
+       mutt_pattern_func (MUTT_UNTAG, _("Untag messages matching: "));
+       menu->redraw |= REDRAW_FULL;
        break;
 
        /* --------------------------------------------------------------------
diff --git a/doc/manual.xml.head b/doc/manual.xml.head
index 631692aa..dc5c62af 100644
--- a/doc/manual.xml.head
+++ b/doc/manual.xml.head
@@ -5690,6 +5690,8 @@ shows several ways to select messages.
 <row><entry>Pattern modifier</entry><entry>Description</entry></row>
 </thead>
 <tbody>
+<row><entry>~a <emphasis>EXPR</emphasis></entry><entry>messages which contain 
<emphasis>EXPR</emphasis> in some envelope address field</entry></row>
+<row><entry>%a <emphasis>GROUP</emphasis></entry><entry>messages which contain 
a member of <emphasis>GROUP</emphasis> in some envelope address 
field</entry></row>
 <row><entry>~A</entry><entry>all messages</entry></row>
 <row><entry>~b <emphasis>EXPR</emphasis></entry><entry>messages which contain 
<emphasis>EXPR</emphasis> in the message body ***)</entry></row>
 <row><entry>=b <emphasis>STRING</emphasis></entry><entry>If IMAP is enabled, 
like ~b but searches for <emphasis>STRING</emphasis> on the server, rather than 
downloading each message and searching it locally.</entry></row>
diff --git a/enter.c b/enter.c
index 546754ac..060a9cee 100644
--- a/enter.c
+++ b/enter.c
@@ -619,7 +619,15 @@ int _mutt_enter_string (char *buf, size_t buflen, int col,
          }
           else if (flags & MUTT_PATTERN && ch == OP_EDITOR_COMPLETE)
           {
-            for (i = state->curpos; i && state->wbuf[i-1] != '~'; i--)
+            i = state->curpos;
+            if (i && state->wbuf[i - 1] == '~')
+            {
+              if (mutt_ask_pattern (buf, buflen))
+                replace_part (state, i, buf);
+              rv = 1;
+              goto bye;
+            }
+            for (; i && state->wbuf[i - 1] != '~'; i--)
               ;
             if (i && i < state->curpos && state->wbuf[i-1] == '~' && 
state->wbuf[i] == 'y')
             {
diff --git a/mutt.h b/mutt.h
index 0d58e276..a54216a9 100644
--- a/mutt.h
+++ b/mutt.h
@@ -264,6 +264,7 @@ enum
   MUTT_PERSONAL_RECIP,
   MUTT_PERSONAL_FROM,
   MUTT_ADDRESS,
+  MUTT_ADDRESS_ALL,
   MUTT_CRYPT_SIGN,
   MUTT_CRYPT_VERIFIED,
   MUTT_CRYPT_ENCRYPT,
diff --git a/pattern.c b/pattern.c
index a995934e..8077d4ec 100644
--- a/pattern.c
+++ b/pattern.c
@@ -26,6 +26,8 @@
 #include "mailbox.h"
 #include "copy.h"
 #include "mime.h"
+#include "mutt_menu.h"
+#include "mutt_curses.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -48,59 +50,109 @@ static int eat_date (pattern_t *pat, int, BUFFER *, BUFFER 
*);
 static int eat_range (pattern_t *pat, int, BUFFER *, BUFFER *);
 static int patmatch (const pattern_t *pat, const char *buf);
 
+#define EAT_REGEXP     1
+#define EAT_DATE       2
+#define EAT_RANGE      3
 static const struct pattern_flags
 {
   int tag;     /* character used to represent this op */
   int op;      /* operation to perform */
   int class;
-  int (*eat_arg) (pattern_t *, int, BUFFER *, BUFFER *);
+  int eat_arg;
+  char *desc;
 }
 Flags[] =
 {
-  { 'A', MUTT_ALL,                     0,              NULL },
-  { 'b', MUTT_BODY,  MUTT_FULL_MSG|MUTT_SEND_MODE_SEARCH, eat_regexp },
-  { 'B', MUTT_WHOLE_MSG,  MUTT_FULL_MSG|MUTT_SEND_MODE_SEARCH, eat_regexp },
-  { 'c', MUTT_CC,                      0,              eat_regexp },
-  { 'C', MUTT_RECIPIENT,               0,              eat_regexp },
-  { 'd', MUTT_DATE,            0,              eat_date },
-  { 'D', MUTT_DELETED,         0,              NULL },
-  { 'e', MUTT_SENDER,          0,              eat_regexp },
-  { 'E', MUTT_EXPIRED,         0,              NULL },
-  { 'f', MUTT_FROM,            0,              eat_regexp },
-  { 'F', MUTT_FLAG,            0,              NULL },
-  { 'g', MUTT_CRYPT_SIGN,              0,              NULL },
-  { 'G', MUTT_CRYPT_ENCRYPT,   0,              NULL },
-  { 'h', MUTT_HEADER,  MUTT_FULL_MSG|MUTT_SEND_MODE_SEARCH, eat_regexp },
-  { 'H', MUTT_HORMEL,          0,              eat_regexp },
-  { 'i', MUTT_ID,                      0,              eat_regexp },
-  { 'k', MUTT_PGP_KEY,         0,              NULL },
-  { 'l', MUTT_LIST,            0,              NULL },
-  { 'L', MUTT_ADDRESS,         0,              eat_regexp },
-  { 'm', MUTT_MESSAGE,         0,              eat_range },
-  { 'M', MUTT_MIMETYPE,                MUTT_FULL_MSG,  eat_regexp },
-  { 'n', MUTT_SCORE,           0,              eat_range },
-  { 'N', MUTT_NEW,                     0,              NULL },
-  { 'O', MUTT_OLD,                     0,              NULL },
-  { 'p', MUTT_PERSONAL_RECIP,  0,              NULL },
-  { 'P', MUTT_PERSONAL_FROM,   0,              NULL },
-  { 'Q', MUTT_REPLIED,         0,              NULL },
-  { 'r', MUTT_DATE_RECEIVED,   0,              eat_date },
-  { 'R', MUTT_READ,            0,              NULL },
-  { 's', MUTT_SUBJECT,         0,              eat_regexp },
-  { 'S', MUTT_SUPERSEDED,              0,              NULL },
-  { 't', MUTT_TO,                      0,              eat_regexp },
-  { 'T', MUTT_TAG,                     0,              NULL },
-  { 'u', MUTT_SUBSCRIBED_LIST, 0,              NULL },
-  { 'U', MUTT_UNREAD,          0,              NULL },
-  { 'v', MUTT_COLLAPSED,               0,              NULL },
-  { 'V', MUTT_CRYPT_VERIFIED,  0,              NULL },
-  { 'x', MUTT_REFERENCE,               0,              eat_regexp },
-  { 'X', MUTT_MIMEATTACH,              0,              eat_range },
-  { 'y', MUTT_XLABEL,          0,              eat_regexp },
-  { 'z', MUTT_SIZE,            0,              eat_range },
-  { '=', MUTT_DUPLICATED,              0,              NULL },
-  { '$', MUTT_UNREFERENCED,    0,              NULL },
-  { 0,   0,                    0,              NULL }
+  { 'a', MUTT_ADDRESS_ALL,             0,              EAT_REGEXP,
+       N_("messages whose some address from the envelope matches EXPR") },
+  { 'A', MUTT_ALL,                     0,              0,
+       N_("all messages") },
+  { 'b', MUTT_BODY,  MUTT_FULL_MSG|MUTT_SEND_MODE_SEARCH, EAT_REGEXP,
+       N_("messages whose body matches EXPR") },
+  { 'B', MUTT_WHOLE_MSG,  MUTT_FULL_MSG|MUTT_SEND_MODE_SEARCH, EAT_REGEXP,
+       N_("messages whose body or headers match EXPR") },
+  { 'c', MUTT_CC,                      0,              EAT_REGEXP,
+       N_("messages whose CC header matches EXPR") },
+  { 'C', MUTT_RECIPIENT,               0,              EAT_REGEXP,
+       N_("messages whose recipient matches EXPR") },
+  { 'd', MUTT_DATE,            0,              EAT_DATE,
+       N_("messages sent in DATERANGE") },
+  { 'D', MUTT_DELETED,         0,              0,
+       N_("deleted messages") },
+  { 'e', MUTT_SENDER,          0,              EAT_REGEXP,
+       N_("messages whose Sender header matches EXPR") },
+  { 'E', MUTT_EXPIRED,         0,              0,
+       N_("expired messages") },
+  { 'f', MUTT_FROM,            0,              EAT_REGEXP,
+       N_("messages whose From header matches EXPR") },
+  { 'F', MUTT_FLAG,            0,              0,
+       N_("flagged messages") },
+  { 'g', MUTT_CRYPT_SIGN,              0,              0,
+       N_("cryptographically signed messages") },
+  { 'G', MUTT_CRYPT_ENCRYPT,   0,              0,
+       N_("cryptographically encrypted messages") },
+  { 'h', MUTT_HEADER,  MUTT_FULL_MSG|MUTT_SEND_MODE_SEARCH, EAT_REGEXP,
+       N_("messages whose header matches EXPR") },
+  { 'H', MUTT_HORMEL,          0,              EAT_REGEXP,
+       N_("messages whose spam tag matches EXPR") },
+  { 'i', MUTT_ID,                      0,              EAT_REGEXP,
+       N_("messages whose Message-ID matches EXPR") },
+  { 'k', MUTT_PGP_KEY,                 0,              0,
+       N_("messages which contain PGP key") },
+  { 'l', MUTT_LIST,            0,              0,
+       N_("messages addressed to known mailing lists") },
+  { 'L', MUTT_ADDRESS,         0,              EAT_REGEXP,
+       N_("messages whose From/Sender/To/CC matches EXPR") },
+  { 'm', MUTT_MESSAGE,         0,              EAT_RANGE,
+       N_("messages whose number is in RANGE") },
+  { 'M', MUTT_MIMETYPE,                MUTT_FULL_MSG,  EAT_REGEXP,
+       N_("messages with a Content-Type matching EXPR") },
+  { 'n', MUTT_SCORE,           0,              EAT_RANGE,
+       N_("messages whose score is in RANGE") },
+  { 'N', MUTT_NEW,                     0,              0,
+       N_("new messages") },
+  { 'O', MUTT_OLD,                     0,              0,
+       N_("old messages") },
+  { 'p', MUTT_PERSONAL_RECIP,  0,              0,
+       N_("messages addressed to you") },
+  { 'P', MUTT_PERSONAL_FROM,   0,              0,
+       N_("messages from you") },
+  { 'Q', MUTT_REPLIED,         0,              0,
+       N_("messages which have been replied to") },
+  { 'r', MUTT_DATE_RECEIVED,   0,              EAT_DATE,
+       N_("messages received in DATERANGE") },
+  { 'R', MUTT_READ,            0,              0,
+       N_("already read messages") },
+  { 's', MUTT_SUBJECT,         0,              EAT_REGEXP,
+       N_("messages whose Subject header matches EXPR") },
+  { 'S', MUTT_SUPERSEDED,              0,              0,
+       N_("superseded messages") },
+  { 't', MUTT_TO,                      0,              EAT_REGEXP,
+       N_("messages whose To header matches EXPR") },
+  { 'T', MUTT_TAG,                     0,              0,
+       N_("tagged messages") },
+  { 'u', MUTT_SUBSCRIBED_LIST, 0,              0,
+       N_("messages addressed to subscribed mailing lists") },
+  { 'U', MUTT_UNREAD,          0,              0,
+       N_("unread messages") },
+  { 'v', MUTT_COLLAPSED,               0,              0,
+       N_("messages in collapsed threads") },
+  { 'V', MUTT_CRYPT_VERIFIED,  0,              0,
+       N_("cryptographically verified messages") },
+  { 'x', MUTT_REFERENCE,               0,              EAT_REGEXP,
+       N_("messages whose References header matches EXPR") },
+  { 'X', MUTT_MIMEATTACH,              0,              EAT_RANGE,
+       N_("messages with RANGE attachments") },
+  { 'y', MUTT_XLABEL,          0,              EAT_REGEXP,
+       N_("messages whose X-Label header matches EXPR") },
+  { 'z', MUTT_SIZE,            0,              EAT_RANGE,
+       N_("messages whose size is in RANGE") },
+  { '=', MUTT_DUPLICATED,              0,              0,
+       N_("duplicated messages") },
+  { '$', MUTT_UNREFERENCED,    0,              0,
+       N_("unreferenced messages") },
+  { 0,   0,                    0,              0,
+       NULL }
 };
 
 static pattern_t *SearchPattern = NULL; /* current search pattern */
@@ -1118,13 +1170,20 @@ pattern_t *mutt_pattern_comp (/* const */ char *s, int 
flags, BUFFER *err)
 
        if (entry->eat_arg)
        {
+         int eatrv = 0;
          if (!*ps.dptr)
          {
            snprintf (err->data, err->dsize, "%s", _("missing parameter"));
            mutt_pattern_free (&curlist);
            return NULL;
          }
-         if (entry->eat_arg (tmp, flags, &ps, err) == -1)
+         switch (entry->eat_arg)
+         {
+           case EAT_REGEXP: eatrv = eat_regexp (tmp, flags, &ps, err); break;
+           case EAT_DATE: eatrv = eat_date (tmp, flags, &ps, err); break;
+           case EAT_RANGE: eatrv = eat_range (tmp, flags, &ps, err); break;
+         }
+         if (eatrv == -1)
          {
            mutt_pattern_free (&curlist);
            return NULL;
@@ -1478,6 +1537,12 @@ mutt_pattern_exec (struct pattern_t *pat, 
pattern_exec_flag flags, CONTEXT *ctx,
       return (pat->not ^ match_adrlist (pat, flags & MUTT_MATCH_FULL_ADDRESS, 
4,
                                         h->env->from, h->env->sender,
                                         h->env->to, h->env->cc));
+    case MUTT_ADDRESS_ALL:
+      return (pat->not ^ match_adrlist (pat, flags & MUTT_MATCH_FULL_ADDRESS, 
8,
+                                        h->env->from, h->env->sender,
+                                        h->env->to, h->env->cc, h->env->bcc,
+                                        h->env->return_path, h->env->reply_to,
+                                        h->env->mail_followup_to));
     case MUTT_RECIPIENT:
       return (pat->not ^ match_adrlist (pat, flags & MUTT_MATCH_FULL_ADDRESS,
                                         2, h->env->to, h->env->cc));
@@ -1668,6 +1733,8 @@ int mutt_pattern_func (int op, char *prompt)
       !mutt_buffer_len (buf))
   {
     mutt_buffer_pool_release (&buf);
+    if (op == MUTT_LIMIT && Context->pattern)
+      mutt_message _("To view all messages, limit to \"all\".");
     return (-1);
   }
 
@@ -1926,3 +1993,130 @@ int mutt_search_command (int cur, int op)
   mutt_error _("Not found.");
   return (-1);
 }
+
+static void pattern_entry (char *s, size_t l, MUTTMENU * menu, int num)
+{
+  LIST **PatTable = (LIST **) menu->data;
+
+  mutt_format_string (s, l, 0, COLS, 0, ' ', PatTable[num]->data,
+                    mutt_strlen (PatTable[num]->data), 0);
+}
+
+static char pattern_menu (LIST *pats)
+{
+  int patmax = 0;
+  LIST **PatTable = NULL;
+  MUTTMENU *menu;
+  int i, done = 0;
+  char helpstr[SHORT_STRING], buf[LONG_STRING];
+  LIST *pat;
+  char rv = 0;
+
+  for (i = 0, pat = pats; pat; pat = pat->next)
+  {
+      if (i == patmax)
+      {
+       patmax += 5;
+       safe_realloc (&PatTable, sizeof (LIST *) * patmax);
+      }
+      PatTable[i++] = pat;
+  }
+
+  helpstr[0] = 0;
+  mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
+  safe_strcat (helpstr, sizeof (helpstr), buf);
+  mutt_make_help (buf, sizeof (buf), _("Select  "), MENU_GENERIC,
+                 OP_GENERIC_SELECT_ENTRY);
+  safe_strcat (helpstr, sizeof (helpstr), buf);
+  mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
+  safe_strcat (helpstr, sizeof (helpstr), buf);
+
+  menu = mutt_new_menu (MENU_GENERIC);
+  menu->max = i;
+  menu->make_entry = pattern_entry;
+  menu->help = helpstr;
+  menu->data = PatTable;
+  menu->title = _("Patterns");
+
+  mutt_clear_error ();
+
+  while (!done)
+  {
+    switch (mutt_menuLoop (menu))
+    {
+    case OP_GENERIC_SELECT_ENTRY:
+      rv = PatTable[menu->current]->data[1];
+      done = 1;
+      break;
+
+    case OP_EXIT:
+      rv = 0;
+      done = 1;
+      break;
+    }
+  }
+
+  mutt_menuDestroy (&menu);
+  FREE (&PatTable);
+
+  return (rv);
+}
+
+static LIST *list_patterns ()
+{
+  LIST *first = NULL, *last = NULL, *cur = NULL;
+  int i;
+
+  for (i = 0; Flags[i].tag; i++)
+  {
+    char buf[LONG_STRING];
+    switch (Flags[i].eat_arg)
+    {
+    case EAT_REGEXP:
+      snprintf (buf, sizeof (buf), _("~%c EXPR       %s"),
+                (char) Flags[i].tag, _(Flags[i].desc));
+      break;
+    case EAT_RANGE:
+      snprintf (buf, sizeof (buf), _("~%c RANGE      %s"),
+                (char) Flags[i].tag, _(Flags[i].desc));
+      break;
+    case EAT_DATE:
+      snprintf (buf, sizeof (buf), _("~%c DATERANGE  %s"),
+                (char) Flags[i].tag, _(Flags[i].desc));
+      break;
+    default:
+      snprintf (buf, sizeof (buf), _("~%c            %s"),
+                (char) Flags[i].tag, _(Flags[i].desc));
+    }
+    cur = (LIST *) safe_calloc (1, sizeof (LIST));
+    cur->data = safe_strdup (buf);
+    if (!first)
+      first = cur;
+    if (last)
+      last->next = cur;
+    last = cur;
+  }
+  if (cur)
+    cur->next = NULL;
+  return (first);
+}
+
+int mutt_ask_pattern (char *buf, size_t buflen)
+{
+  char c;
+  LIST *l;
+  int rv = 0;
+
+  if (!buf || buflen < 3)
+    return 0;
+  if ((l = list_patterns()))
+  {
+    if ((c = pattern_menu (l)))
+    {
+      sprintf (buf, "%c ", c); /* __SPRINTF_CHECKED__ */
+      rv = 1;
+    }
+    mutt_free_list (&l);
+  }
+  return rv;
+}
diff --git a/protos.h b/protos.h
index e1bfe746..72c9ad57 100644
--- a/protos.h
+++ b/protos.h
@@ -294,6 +294,7 @@ int mutt_alias_complete (char *, size_t);
 void mutt_alias_add_reverse (ALIAS *t);
 void mutt_alias_delete_reverse (ALIAS *t);
 int mutt_alloc_color (int fg, int bg);
+int mutt_ask_pattern (char *, size_t);
 int mutt_any_key_to_continue (const char *);
 char *mutt_apply_replace (char *, size_t, char *, REPLACE_LIST *);
 int mutt_builtin_editor (SEND_CONTEXT *);

Reply via email to