|
Link Here
|
| 1 |
--- doc/manual.xml.head.orig 2017-05-30 19:27:52 UTC |
|
|
| 2 |
+++ doc/manual.xml.head |
| 3 |
@@ -4265,6 +4265,22 @@ variable <quote>all</quote>, which allow |
| 4 |
their system defaults. |
| 5 |
</para> |
| 6 |
|
| 7 |
+<para> |
| 8 |
+<emphasis role="bold">Parent and child match</emphasis>. |
| 9 |
+You can tell mutt that the following pattern has to be matched against |
| 10 |
+the parent message with < or one of its childs with >. |
| 11 |
+This example matches all mails which have at least an unread duplicate |
| 12 |
+message: |
| 13 |
+</para> |
| 14 |
+ |
| 15 |
+<para> |
| 16 |
+ |
| 17 |
+<screen> |
| 18 |
+>(~= ~N) |
| 19 |
+</screen> |
| 20 |
+ |
| 21 |
+</para> |
| 22 |
+ |
| 23 |
</sect2> |
| 24 |
|
| 25 |
<sect2 id="set-myvar"> |
| 26 |
--- mutt.h.orig 2017-06-03 23:16:03 UTC |
| 27 |
+++ mutt.h |
| 28 |
@@ -865,6 +865,8 @@ typedef struct pattern_t |
| 29 |
unsigned int alladdr : 1; |
| 30 |
unsigned int stringmatch : 1; |
| 31 |
unsigned int groupmatch : 1; |
| 32 |
+ unsigned int parentmatch : 1; |
| 33 |
+ unsigned int childsmatch : 1; |
| 34 |
unsigned int ign_case : 1; /* ignore case for local stringmatch searches */ |
| 35 |
unsigned int isalias : 1; |
| 36 |
int min; |
| 37 |
--- pattern.c.orig 2017-05-30 19:26:40 UTC |
| 38 |
+++ pattern.c |
| 39 |
@@ -46,6 +46,7 @@ static int eat_regexp (pattern_t *pat, B |
| 40 |
static int eat_date (pattern_t *pat, BUFFER *, BUFFER *); |
| 41 |
static int eat_range (pattern_t *pat, BUFFER *, BUFFER *); |
| 42 |
static int patmatch (const pattern_t *pat, const char *buf); |
| 43 |
+static int pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h, pattern_cache_t *cache); |
| 44 |
|
| 45 |
static const struct pattern_flags |
| 46 |
{ |
| 47 |
@@ -781,6 +782,8 @@ pattern_t *mutt_pattern_comp (/* const * |
| 48 |
pattern_t *last = NULL; |
| 49 |
int not = 0; |
| 50 |
int alladdr = 0; |
| 51 |
+ int parentmatch = 0; |
| 52 |
+ int childsmatch = 0; |
| 53 |
int or = 0; |
| 54 |
int implicit = 1; /* used to detect logical AND operator */ |
| 55 |
int isalias = 0; |
| 56 |
@@ -810,6 +813,24 @@ pattern_t *mutt_pattern_comp (/* const * |
| 57 |
ps.dptr++; |
| 58 |
isalias = !isalias; |
| 59 |
break; |
| 60 |
+ case '<': |
| 61 |
+ ps.dptr++; |
| 62 |
+ if (childsmatch) { |
| 63 |
+ snprintf (err->data, err->dsize, _("cannot use both < and > as a pattern modifier")); |
| 64 |
+ mutt_pattern_free (&curlist); |
| 65 |
+ return NULL; |
| 66 |
+ } |
| 67 |
+ parentmatch = 1; |
| 68 |
+ break; |
| 69 |
+ case '>': |
| 70 |
+ ps.dptr++; |
| 71 |
+ if (parentmatch) { |
| 72 |
+ snprintf (err->data, err->dsize, _("cannot use both < and > as a pattern modifier")); |
| 73 |
+ mutt_pattern_free (&curlist); |
| 74 |
+ return NULL; |
| 75 |
+ } |
| 76 |
+ childsmatch = 1; |
| 77 |
+ break; |
| 78 |
case '|': |
| 79 |
if (!or) |
| 80 |
{ |
| 81 |
@@ -835,6 +856,8 @@ pattern_t *mutt_pattern_comp (/* const * |
| 82 |
implicit = 0; |
| 83 |
not = 0; |
| 84 |
alladdr = 0; |
| 85 |
+ parentmatch = 0; |
| 86 |
+ childsmatch = 0; |
| 87 |
isalias = 0; |
| 88 |
break; |
| 89 |
case '%': |
| 90 |
@@ -865,9 +888,13 @@ pattern_t *mutt_pattern_comp (/* const * |
| 91 |
last = tmp; |
| 92 |
tmp->not ^= not; |
| 93 |
tmp->alladdr |= alladdr; |
| 94 |
+ tmp->parentmatch |= parentmatch; |
| 95 |
+ tmp->childsmatch |= childsmatch; |
| 96 |
tmp->isalias |= isalias; |
| 97 |
not = 0; |
| 98 |
alladdr = 0; |
| 99 |
+ parentmatch = 0; |
| 100 |
+ childsmatch = 0; |
| 101 |
isalias = 0; |
| 102 |
/* compile the sub-expression */ |
| 103 |
buf = mutt_substrdup (ps.dptr + 1, p); |
| 104 |
@@ -896,11 +923,15 @@ pattern_t *mutt_pattern_comp (/* const * |
| 105 |
tmp = new_pattern (); |
| 106 |
tmp->not = not; |
| 107 |
tmp->alladdr = alladdr; |
| 108 |
+ tmp->parentmatch = parentmatch; |
| 109 |
+ tmp->childsmatch = childsmatch; |
| 110 |
tmp->isalias = isalias; |
| 111 |
tmp->stringmatch = (*ps.dptr == '=') ? 1 : 0; |
| 112 |
tmp->groupmatch = (*ps.dptr == '%') ? 1 : 0; |
| 113 |
not = 0; |
| 114 |
alladdr = 0; |
| 115 |
+ parentmatch = 0; |
| 116 |
+ childsmatch = 0; |
| 117 |
isalias = 0; |
| 118 |
|
| 119 |
if (last) |
| 120 |
@@ -967,9 +998,13 @@ pattern_t *mutt_pattern_comp (/* const * |
| 121 |
last = tmp; |
| 122 |
tmp->not ^= not; |
| 123 |
tmp->alladdr |= alladdr; |
| 124 |
+ tmp->parentmatch |= parentmatch; |
| 125 |
+ tmp->childsmatch |= childsmatch; |
| 126 |
tmp->isalias |= isalias; |
| 127 |
not = 0; |
| 128 |
alladdr = 0; |
| 129 |
+ parentmatch = 0; |
| 130 |
+ childsmatch = 0; |
| 131 |
isalias = 0; |
| 132 |
ps.dptr = p + 1; /* restore location */ |
| 133 |
break; |
| 134 |
@@ -1137,6 +1172,37 @@ int |
| 135 |
mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h, |
| 136 |
pattern_cache_t *cache) |
| 137 |
{ |
| 138 |
+ THREAD *t; |
| 139 |
+ |
| 140 |
+ if (pat->parentmatch) { |
| 141 |
+ if (h->thread && h->thread->parent && h->thread->parent->message) |
| 142 |
+ return pattern_exec (pat, flags, ctx, h->thread->parent->message, cache); |
| 143 |
+ else |
| 144 |
+ return pat->not; |
| 145 |
+ } |
| 146 |
+ if (pat->childsmatch) { |
| 147 |
+ if (!h->thread) |
| 148 |
+ return pat->not; |
| 149 |
+ if (!h->thread->child) |
| 150 |
+ return pat->not; |
| 151 |
+ t = h->thread->child; |
| 152 |
+ while (t->prev) |
| 153 |
+ t = t->prev; |
| 154 |
+ for (; t; t = t->next) { |
| 155 |
+ if (!t->message) |
| 156 |
+ continue; |
| 157 |
+ if (pattern_exec (pat, flags, ctx, t->message, cache)) |
| 158 |
+ return !pat->not; |
| 159 |
+ } |
| 160 |
+ return pat->not; |
| 161 |
+ } |
| 162 |
+ return pattern_exec (pat, flags, ctx, h, cache); |
| 163 |
+} |
| 164 |
+ |
| 165 |
+static int |
| 166 |
+pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h, |
| 167 |
+ pattern_cache_t *cache) |
| 168 |
+{ |
| 169 |
int result; |
| 170 |
int *cache_entry; |
| 171 |
|