NeoMutt  2024-04-25-76-g20fe7b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
compile.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <ctype.h>
33#include <stdbool.h>
34#include <stdint.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <sys/types.h>
39#include <time.h>
40#include "private.h"
41#include "mutt/lib.h"
42#include "address/lib.h"
43#include "config/lib.h"
44#include "core/lib.h"
45#include "lib.h"
46#include "parse/lib.h"
47#include "globals.h"
48#include "mview.h"
49
50struct Menu;
51
52// clang-format off
53typedef uint16_t ParseDateRangeFlags;
54#define MUTT_PDR_NO_FLAGS 0
55#define MUTT_PDR_MINUS (1 << 0)
56#define MUTT_PDR_PLUS (1 << 1)
57#define MUTT_PDR_WINDOW (1 << 2)
58#define MUTT_PDR_ABSOLUTE (1 << 3)
59#define MUTT_PDR_DONE (1 << 4)
60#define MUTT_PDR_ERROR (1 << 8)
61// clang-format on
62
63#define MUTT_PDR_ERRORDONE (MUTT_PDR_ERROR | MUTT_PDR_DONE)
64
68static bool eat_regex(struct Pattern *pat, PatternCompFlags flags,
69 struct Buffer *s, struct Buffer *err)
70{
71 struct Buffer *buf = buf_pool_get();
72 bool rc = false;
73 char *pexpr = s->dptr;
74 if ((parse_extract_token(buf, s, TOKEN_PATTERN | TOKEN_COMMENT) != 0) || !buf->data)
75 {
76 buf_printf(err, _("Error in expression: %s"), pexpr);
77 goto out;
78 }
79 if (buf_is_empty(buf))
80 {
81 buf_addstr(err, _("Empty expression"));
82 goto out;
83 }
84
85 if (pat->string_match)
86 {
87 pat->p.str = mutt_str_dup(buf->data);
88 pat->ign_case = mutt_mb_is_lower(buf->data);
89 }
90 else if (pat->group_match)
91 {
92 pat->p.group = mutt_pattern_group(buf->data);
93 }
94 else
95 {
96 pat->p.regex = mutt_mem_calloc(1, sizeof(regex_t));
97#ifdef USE_DEBUG_GRAPHVIZ
98 pat->raw_pattern = mutt_str_dup(buf->data);
99#endif
100 uint16_t case_flags = mutt_mb_is_lower(buf->data) ? REG_ICASE : 0;
101 int rc2 = REG_COMP(pat->p.regex, buf->data, REG_NEWLINE | REG_NOSUB | case_flags);
102 if (rc2 != 0)
103 {
104 char errmsg[256] = { 0 };
105 regerror(rc2, pat->p.regex, errmsg, sizeof(errmsg));
106 buf_printf(err, "'%s': %s", buf->data, errmsg);
107 FREE(&pat->p.regex);
108 goto out;
109 }
110 }
111
112 rc = true;
113
114out:
115 buf_pool_release(&buf);
116 return rc;
117}
118
123static bool add_query_msgid(char *line, int line_num, void *user_data)
124{
125 struct ListHead *msgid_list = (struct ListHead *) (user_data);
126 char *nows = mutt_str_skip_whitespace(line);
127 if (*nows == '\0')
128 return true;
130 mutt_list_insert_tail(msgid_list, mutt_str_dup(nows));
131 return true;
132}
133
143static bool eat_query(struct Pattern *pat, PatternCompFlags flags,
144 struct Buffer *s, struct Buffer *err, struct Mailbox *m)
145{
146 struct Buffer *cmd_buf = buf_pool_get();
147 struct Buffer *tok_buf = buf_pool_get();
148 bool rc = false;
149
150 FILE *fp = NULL;
151
152 const char *const c_external_search_command = cs_subset_string(NeoMutt->sub, "external_search_command");
153 if (!c_external_search_command)
154 {
155 buf_addstr(err, _("No search command defined"));
156 goto out;
157 }
158
159 char *pexpr = s->dptr;
160 if ((parse_extract_token(tok_buf, s, TOKEN_PATTERN | TOKEN_COMMENT) != 0) ||
161 !tok_buf->data)
162 {
163 buf_printf(err, _("Error in expression: %s"), pexpr);
164 goto out;
165 }
166 if (*tok_buf->data == '\0')
167 {
168 buf_addstr(err, _("Empty expression"));
169 goto out;
170 }
171
172 buf_addstr(cmd_buf, c_external_search_command);
173 buf_addch(cmd_buf, ' ');
174
175 if (m)
176 {
177 char *escaped_folder = mutt_path_escape(mailbox_path(m));
178 mutt_debug(LL_DEBUG2, "escaped folder path: %s\n", escaped_folder);
179 buf_addch(cmd_buf, '\'');
180 buf_addstr(cmd_buf, escaped_folder);
181 buf_addch(cmd_buf, '\'');
182 }
183 else
184 {
185 buf_addch(cmd_buf, '/');
186 }
187 buf_addch(cmd_buf, ' ');
188 buf_addstr(cmd_buf, tok_buf->data);
189
190 mutt_message(_("Running search command: %s ..."), cmd_buf->data);
191 pat->is_multi = true;
193 pid_t pid = filter_create(cmd_buf->data, NULL, &fp, NULL, EnvList);
194 if (pid < 0)
195 {
196 buf_printf(err, "unable to fork command: %s\n", cmd_buf->data);
197 goto out;
198 }
199
201 mutt_file_fclose(&fp);
202 filter_wait(pid);
203
204 rc = true;
205
206out:
207 buf_pool_release(&cmd_buf);
208 buf_pool_release(&tok_buf);
209 return rc;
210}
211
224static const char *get_offset(struct tm *tm, const char *s, int sign)
225{
226 char *ps = NULL;
227 int offset = strtol(s, &ps, 0);
228 if (((sign < 0) && (offset > 0)) || ((sign > 0) && (offset < 0)))
229 offset = -offset;
230
231 switch (*ps)
232 {
233 case 'y':
234 tm->tm_year += offset;
235 break;
236 case 'm':
237 tm->tm_mon += offset;
238 break;
239 case 'w':
240 tm->tm_mday += 7 * offset;
241 break;
242 case 'd':
243 tm->tm_mday += offset;
244 break;
245 case 'H':
246 tm->tm_hour += offset;
247 break;
248 case 'M':
249 tm->tm_min += offset;
250 break;
251 case 'S':
252 tm->tm_sec += offset;
253 break;
254 default:
255 return s;
256 }
258 return ps + 1;
259}
260
278static const char *get_date(const char *s, struct tm *t, struct Buffer *err)
279{
280 char *p = NULL;
281 struct tm tm = mutt_date_localtime(mutt_date_now());
282 bool iso8601 = true;
283
284 for (int v = 0; v < 8; v++)
285 {
286 if (s[v] && (s[v] >= '0') && (s[v] <= '9'))
287 continue;
288
289 iso8601 = false;
290 break;
291 }
292
293 if (iso8601)
294 {
295 int year = 0;
296 int month = 0;
297 int mday = 0;
298 sscanf(s, "%4d%2d%2d", &year, &month, &mday);
299
300 t->tm_year = year;
301 if (t->tm_year > 1900)
302 t->tm_year -= 1900;
303 t->tm_mon = month - 1;
304 t->tm_mday = mday;
305
306 if ((t->tm_mday < 1) || (t->tm_mday > 31))
307 {
308 buf_printf(err, _("Invalid day of month: %s"), s);
309 return NULL;
310 }
311 if ((t->tm_mon < 0) || (t->tm_mon > 11))
312 {
313 buf_printf(err, _("Invalid month: %s"), s);
314 return NULL;
315 }
316
317 return (s + 8);
318 }
319
320 t->tm_mday = strtol(s, &p, 10);
321 if ((t->tm_mday < 1) || (t->tm_mday > 31))
322 {
323 buf_printf(err, _("Invalid day of month: %s"), s);
324 return NULL;
325 }
326 if (*p != '/')
327 {
328 /* fill in today's month and year */
329 t->tm_mon = tm.tm_mon;
330 t->tm_year = tm.tm_year;
331 return p;
332 }
333 p++;
334 t->tm_mon = strtol(p, &p, 10) - 1;
335 if ((t->tm_mon < 0) || (t->tm_mon > 11))
336 {
337 buf_printf(err, _("Invalid month: %s"), p);
338 return NULL;
339 }
340 if (*p != '/')
341 {
342 t->tm_year = tm.tm_year;
343 return p;
344 }
345 p++;
346 t->tm_year = strtol(p, &p, 10);
347 if (t->tm_year < 70) /* year 2000+ */
348 t->tm_year += 100;
349 else if (t->tm_year > 1900)
350 t->tm_year -= 1900;
351 return p;
352}
353
364static const char *parse_date_range(const char *pc, struct tm *min, struct tm *max,
365 bool have_min, struct tm *base_min, struct Buffer *err)
366{
368 while (*pc && ((flags & MUTT_PDR_DONE) == 0))
369 {
370 const char *pt = NULL;
371 char ch = *pc++;
372 SKIPWS(pc);
373 switch (ch)
374 {
375 case '-':
376 {
377 /* try a range of absolute date minus offset of Ndwmy */
378 pt = get_offset(min, pc, -1);
379 if (pc == pt)
380 {
381 if (flags == MUTT_PDR_NO_FLAGS)
382 { /* nothing yet and no offset parsed => absolute date? */
383 if (!get_date(pc, max, err))
384 {
385 flags |= (MUTT_PDR_ABSOLUTE | MUTT_PDR_ERRORDONE); /* done bad */
386 }
387 else
388 {
389 /* reestablish initial base minimum if not specified */
390 if (!have_min)
391 memcpy(min, base_min, sizeof(struct tm));
392 flags |= (MUTT_PDR_ABSOLUTE | MUTT_PDR_DONE); /* done good */
393 }
394 }
395 else
396 {
397 flags |= MUTT_PDR_ERRORDONE;
398 }
399 }
400 else
401 {
402 pc = pt;
403 if ((flags == MUTT_PDR_NO_FLAGS) && !have_min)
404 { /* the very first "-3d" without a previous absolute date */
405 max->tm_year = min->tm_year;
406 max->tm_mon = min->tm_mon;
407 max->tm_mday = min->tm_mday;
408 }
409 flags |= MUTT_PDR_MINUS;
410 }
411 break;
412 }
413 case '+':
414 { /* enlarge plus range */
415 pt = get_offset(max, pc, 1);
416 if (pc == pt)
417 {
418 flags |= MUTT_PDR_ERRORDONE;
419 }
420 else
421 {
422 pc = pt;
423 flags |= MUTT_PDR_PLUS;
424 }
425 break;
426 }
427 case '*':
428 { /* enlarge window in both directions */
429 pt = get_offset(min, pc, -1);
430 if (pc == pt)
431 {
432 flags |= MUTT_PDR_ERRORDONE;
433 }
434 else
435 {
436 pc = get_offset(max, pc, 1);
437 flags |= MUTT_PDR_WINDOW;
438 }
439 break;
440 }
441 default:
442 flags |= MUTT_PDR_ERRORDONE;
443 }
444 SKIPWS(pc);
445 }
446 if ((flags & MUTT_PDR_ERROR) && !(flags & MUTT_PDR_ABSOLUTE))
447 { /* get_date has its own error message, don't overwrite it here */
448 buf_printf(err, _("Invalid relative date: %s"), pc - 1);
449 }
450 return (flags & MUTT_PDR_ERROR) ? NULL : pc;
451}
452
458static void adjust_date_range(struct tm *min, struct tm *max)
459{
460 if ((min->tm_year > max->tm_year) ||
461 ((min->tm_year == max->tm_year) && (min->tm_mon > max->tm_mon)) ||
462 ((min->tm_year == max->tm_year) && (min->tm_mon == max->tm_mon) &&
463 (min->tm_mday > max->tm_mday)))
464 {
465 int tmp;
466
467 tmp = min->tm_year;
468 min->tm_year = max->tm_year;
469 max->tm_year = tmp;
470
471 tmp = min->tm_mon;
472 min->tm_mon = max->tm_mon;
473 max->tm_mon = tmp;
474
475 tmp = min->tm_mday;
476 min->tm_mday = max->tm_mday;
477 max->tm_mday = tmp;
478
479 min->tm_hour = 0;
480 min->tm_min = 0;
481 min->tm_sec = 0;
482 max->tm_hour = 23;
483 max->tm_min = 59;
484 max->tm_sec = 59;
485 }
486}
487
496bool eval_date_minmax(struct Pattern *pat, const char *s, struct Buffer *err)
497{
498 /* the '0' time is Jan 1, 1970 UTC, so in order to prevent a negative time
499 * when doing timezone conversion, we use Jan 2, 1970 UTC as the base here */
500 struct tm min = { 0 };
501 min.tm_mday = 2;
502 min.tm_year = 70;
503
504 /* Arbitrary year in the future. Don't set this too high or
505 * mutt_date_make_time() returns something larger than will fit in a time_t
506 * on some systems */
507 struct tm max = { 0 };
508 max.tm_year = 130;
509 max.tm_mon = 11;
510 max.tm_mday = 31;
511 max.tm_hour = 23;
512 max.tm_min = 59;
513 max.tm_sec = 59;
514
515 if (strchr("<>=", s[0]))
516 {
517 /* offset from current time
518 * <3d less than three days ago
519 * >3d more than three days ago
520 * =3d exactly three days ago */
521 struct tm *tm = NULL;
522 bool exact = false;
523
524 if (s[0] == '<')
525 {
527 tm = &min;
528 }
529 else
530 {
532 tm = &max;
533
534 if (s[0] == '=')
535 exact = true;
536 }
537
538 /* Reset the HMS unless we are relative matching using one of those
539 * offsets. */
540 char *offset_type = NULL;
541 strtol(s + 1, &offset_type, 0);
542 if (!(*offset_type && strchr("HMS", *offset_type)))
543 {
544 tm->tm_hour = 23;
545 tm->tm_min = 59;
546 tm->tm_sec = 59;
547 }
548
549 /* force negative offset */
550 get_offset(tm, s + 1, -1);
551
552 if (exact)
553 {
554 /* start at the beginning of the day in question */
555 memcpy(&min, &max, sizeof(max));
556 min.tm_hour = 0;
557 min.tm_sec = 0;
558 min.tm_min = 0;
559 }
560 }
561 else
562 {
563 const char *pc = s;
564
565 bool have_min = false;
566 bool until_now = false;
567 if (isdigit((unsigned char) *pc))
568 {
569 /* minimum date specified */
570 pc = get_date(pc, &min, err);
571 if (!pc)
572 {
573 return false;
574 }
575 have_min = true;
576 SKIPWS(pc);
577 if (*pc == '-')
578 {
579 const char *pt = pc + 1;
580 SKIPWS(pt);
581 until_now = (*pt == '\0');
582 }
583 }
584
585 if (!until_now)
586 { /* max date or relative range/window */
587
588 struct tm base_min = { 0 };
589
590 if (!have_min)
591 { /* save base minimum and set current date, e.g. for "-3d+1d" */
592 memcpy(&base_min, &min, sizeof(base_min));
594 min.tm_hour = 0;
595 min.tm_sec = 0;
596 min.tm_min = 0;
597 }
598
599 /* preset max date for relative offsets,
600 * if nothing follows we search for messages on a specific day */
601 max.tm_year = min.tm_year;
602 max.tm_mon = min.tm_mon;
603 max.tm_mday = min.tm_mday;
604
605 if (!parse_date_range(pc, &min, &max, have_min, &base_min, err))
606 { /* bail out on any parsing error */
607 return false;
608 }
609 }
610 }
611
612 /* Since we allow two dates to be specified we'll have to adjust that. */
613 adjust_date_range(&min, &max);
614
615 pat->min = mutt_date_make_time(&min, true);
616 pat->max = mutt_date_make_time(&max, true);
617
618 return true;
619}
620
624static bool eat_range(struct Pattern *pat, PatternCompFlags flags,
625 struct Buffer *s, struct Buffer *err)
626{
627 char *tmp = NULL;
628 bool do_exclusive = false;
629 bool skip_quote = false;
630
631 /* If simple_search is set to "~m %s", the range will have double quotes
632 * around it... */
633 if (*s->dptr == '"')
634 {
635 s->dptr++;
636 skip_quote = true;
637 }
638 if (*s->dptr == '<')
639 do_exclusive = true;
640 if ((*s->dptr != '-') && (*s->dptr != '<'))
641 {
642 /* range minimum */
643 if (*s->dptr == '>')
644 {
645 pat->max = MUTT_MAXRANGE;
646 pat->min = strtol(s->dptr + 1, &tmp, 0) + 1; /* exclusive range */
647 }
648 else
649 {
650 pat->min = strtol(s->dptr, &tmp, 0);
651 }
652 if (toupper((unsigned char) *tmp) == 'K') /* is there a prefix? */
653 {
654 pat->min *= 1024;
655 tmp++;
656 }
657 else if (toupper((unsigned char) *tmp) == 'M')
658 {
659 pat->min *= 1048576;
660 tmp++;
661 }
662 if (*s->dptr == '>')
663 {
664 s->dptr = tmp;
665 return true;
666 }
667 if (*tmp != '-')
668 {
669 /* exact value */
670 pat->max = pat->min;
671 s->dptr = tmp;
672 return true;
673 }
674 tmp++;
675 }
676 else
677 {
678 s->dptr++;
679 tmp = s->dptr;
680 }
681
682 if (isdigit((unsigned char) *tmp))
683 {
684 /* range maximum */
685 pat->max = strtol(tmp, &tmp, 0);
686 if (toupper((unsigned char) *tmp) == 'K')
687 {
688 pat->max *= 1024;
689 tmp++;
690 }
691 else if (toupper((unsigned char) *tmp) == 'M')
692 {
693 pat->max *= 1048576;
694 tmp++;
695 }
696 if (do_exclusive)
697 (pat->max)--;
698 }
699 else
700 {
701 pat->max = MUTT_MAXRANGE;
702 }
703
704 if (skip_quote && (*tmp == '"'))
705 tmp++;
706
707 SKIPWS(tmp);
708 s->dptr = tmp;
709 return true;
710}
711
715static bool eat_date(struct Pattern *pat, PatternCompFlags flags,
716 struct Buffer *s, struct Buffer *err)
717{
718 struct Buffer *tmp = buf_pool_get();
719 bool rc = false;
720
721 char *pexpr = s->dptr;
723 {
724 buf_printf(err, _("Error in expression: %s"), pexpr);
725 goto out;
726 }
727
728 if (buf_is_empty(tmp))
729 {
730 buf_addstr(err, _("Empty expression"));
731 goto out;
732 }
733
734 if (flags & MUTT_PC_PATTERN_DYNAMIC)
735 {
736 pat->dynamic = true;
737 pat->p.str = mutt_str_dup(tmp->data);
738 }
739
740 rc = eval_date_minmax(pat, tmp->data, err);
741
742out:
743 buf_pool_release(&tmp);
744 return rc;
745}
746
754static /* const */ char *find_matching_paren(/* const */ char *s)
755{
756 int level = 1;
757
758 for (; *s; s++)
759 {
760 if (*s == '(')
761 {
762 level++;
763 }
764 else if (*s == ')')
765 {
766 level--;
767 if (level == 0)
768 break;
769 }
770 }
771 return s;
772}
773
778void mutt_pattern_free(struct PatternList **pat)
779{
780 if (!pat || !*pat)
781 return;
782
783 struct Pattern *np = SLIST_FIRST(*pat), *next = NULL;
784
785 while (np)
786 {
787 next = SLIST_NEXT(np, entries);
788
789 if (np->is_multi)
790 {
792 }
793 else if (np->string_match || np->dynamic)
794 {
795 FREE(&np->p.str);
796 }
797 else if (np->group_match)
798 {
799 np->p.group = NULL;
800 }
801 else if (np->p.regex)
802 {
803 regfree(np->p.regex);
804 FREE(&np->p.regex);
805 }
806
807#ifdef USE_DEBUG_GRAPHVIZ
808 FREE(&np->raw_pattern);
809#endif
811 FREE(&np);
812
813 np = next;
814 }
815
816 FREE(pat);
817}
818
823static struct Pattern *mutt_pattern_new(void)
824{
825 return mutt_mem_calloc(1, sizeof(struct Pattern));
826}
827
832static struct PatternList *mutt_pattern_list_new(void)
833{
834 struct PatternList *h = mutt_mem_calloc(1, sizeof(struct PatternList));
835 SLIST_INIT(h);
836 struct Pattern *p = mutt_pattern_new();
837 SLIST_INSERT_HEAD(h, p, entries);
838 return h;
839}
840
847static struct Pattern *attach_leaf(struct PatternList *list, struct Pattern *leaf)
848{
849 struct Pattern *last = NULL;
850 SLIST_FOREACH(last, list, entries)
851 {
852 // TODO - or we could use a doubly-linked list
853 if (!SLIST_NEXT(last, entries))
854 {
855 SLIST_NEXT(last, entries) = leaf;
856 break;
857 }
858 }
859 return leaf;
860}
861
869static struct Pattern *attach_new_root(struct PatternList **curlist)
870{
871 struct PatternList *root = mutt_pattern_list_new();
872 struct Pattern *leaf = SLIST_FIRST(root);
873 leaf->child = *curlist;
874 *curlist = root;
875 return leaf;
876}
877
885static struct Pattern *attach_new_leaf(struct PatternList **curlist)
886{
887 if (*curlist)
888 {
889 return attach_leaf(*curlist, mutt_pattern_new());
890 }
891 else
892 {
893 return attach_new_root(curlist);
894 }
895}
896
906struct PatternList *mutt_pattern_comp(struct MailboxView *mv, struct Menu *menu,
907 const char *s, PatternCompFlags flags,
908 struct Buffer *err)
909{
910 /* curlist when assigned will always point to a list containing at least one node
911 * with a Pattern value. */
912 struct PatternList *curlist = NULL;
913 bool pat_not = false;
914 bool all_addr = false;
915 bool pat_or = false;
916 bool implicit = true; /* used to detect logical AND operator */
917 bool is_alias = false;
918 const struct PatternFlags *entry = NULL;
919 char *p = NULL;
920 char *buf = NULL;
921 struct Mailbox *m = mv ? mv->mailbox : NULL;
922
923 if (!s || (s[0] == '\0'))
924 {
925 buf_strcpy(err, _("empty pattern"));
926 return NULL;
927 }
928
929 struct Buffer *ps = buf_pool_get();
930 buf_strcpy(ps, s);
931 buf_seek(ps, 0);
932
933 SKIPWS(ps->dptr);
934 while (*ps->dptr)
935 {
936 switch (*ps->dptr)
937 {
938 case '^':
939 ps->dptr++;
940 all_addr = !all_addr;
941 break;
942 case '!':
943 ps->dptr++;
944 pat_not = !pat_not;
945 break;
946 case '@':
947 ps->dptr++;
948 is_alias = !is_alias;
949 break;
950 case '|':
951 if (!pat_or)
952 {
953 if (!curlist)
954 {
955 buf_printf(err, _("error in pattern at: %s"), ps->dptr);
956 buf_pool_release(&ps);
957 return NULL;
958 }
959
960 struct Pattern *pat = SLIST_FIRST(curlist);
961 if (SLIST_NEXT(pat, entries))
962 {
963 /* A & B | C == (A & B) | C */
964 struct Pattern *root = attach_new_root(&curlist);
965 root->op = MUTT_PAT_AND;
966 }
967
968 pat_or = true;
969 }
970 ps->dptr++;
971 implicit = false;
972 pat_not = false;
973 all_addr = false;
974 is_alias = false;
975 break;
976 case '%':
977 case '=':
978 case '~':
979 {
980 if (ps->dptr[1] == '\0')
981 {
982 buf_printf(err, _("missing pattern: %s"), ps->dptr);
983 goto cleanup;
984 }
985 short thread_op = 0;
986 if (ps->dptr[1] == '(')
987 thread_op = MUTT_PAT_THREAD;
988 else if ((ps->dptr[1] == '<') && (ps->dptr[2] == '('))
989 thread_op = MUTT_PAT_PARENT;
990 else if ((ps->dptr[1] == '>') && (ps->dptr[2] == '('))
991 thread_op = MUTT_PAT_CHILDREN;
992 if (thread_op != 0)
993 {
994 ps->dptr++; /* skip ~ */
995 if ((thread_op == MUTT_PAT_PARENT) || (thread_op == MUTT_PAT_CHILDREN))
996 ps->dptr++;
997 p = find_matching_paren(ps->dptr + 1);
998 if (p[0] != ')')
999 {
1000 buf_printf(err, _("mismatched parentheses: %s"), ps->dptr);
1001 goto cleanup;
1002 }
1003 struct Pattern *leaf = attach_new_leaf(&curlist);
1004 leaf->op = thread_op;
1005 leaf->pat_not = pat_not;
1006 leaf->all_addr = all_addr;
1007 leaf->is_alias = is_alias;
1008 pat_not = false;
1009 all_addr = false;
1010 is_alias = false;
1011 /* compile the sub-expression */
1012 buf = mutt_strn_dup(ps->dptr + 1, p - (ps->dptr + 1));
1013 leaf->child = mutt_pattern_comp(mv, menu, buf, flags, err);
1014 if (!leaf->child)
1015 {
1016 FREE(&buf);
1017 goto cleanup;
1018 }
1019 FREE(&buf);
1020 ps->dptr = p + 1; /* restore location */
1021 break;
1022 }
1023 if (implicit && pat_or)
1024 {
1025 /* A | B & C == (A | B) & C */
1026 struct Pattern *root = attach_new_root(&curlist);
1027 root->op = MUTT_PAT_OR;
1028 pat_or = false;
1029 }
1030
1031 entry = lookup_tag(ps->dptr[1]);
1032 if (!entry)
1033 {
1034 buf_printf(err, _("%c: invalid pattern modifier"), *ps->dptr);
1035 goto cleanup;
1036 }
1037 if (entry->flags && ((flags & entry->flags) == 0))
1038 {
1039 buf_printf(err, _("%c: not supported in this mode"), *ps->dptr);
1040 goto cleanup;
1041 }
1042
1043 struct Pattern *leaf = attach_new_leaf(&curlist);
1044 leaf->pat_not = pat_not;
1045 leaf->all_addr = all_addr;
1046 leaf->is_alias = is_alias;
1047 leaf->string_match = (ps->dptr[0] == '=');
1048 leaf->group_match = (ps->dptr[0] == '%');
1049 leaf->sendmode = (flags & MUTT_PC_SEND_MODE_SEARCH);
1050 leaf->op = entry->op;
1051 pat_not = false;
1052 all_addr = false;
1053 is_alias = false;
1054
1055 ps->dptr++; /* move past the ~ */
1056 ps->dptr++; /* eat the operator and any optional whitespace */
1057 SKIPWS(ps->dptr);
1058 if (entry->eat_arg)
1059 {
1060 if (ps->dptr[0] == '\0')
1061 {
1062 buf_addstr(err, _("missing parameter"));
1063 goto cleanup;
1064 }
1065 switch (entry->eat_arg)
1066 {
1067 case EAT_REGEX:
1068 if (!eat_regex(leaf, flags, ps, err))
1069 goto cleanup;
1070 break;
1071 case EAT_DATE:
1072 if (!eat_date(leaf, flags, ps, err))
1073 goto cleanup;
1074 break;
1075 case EAT_RANGE:
1076 if (!eat_range(leaf, flags, ps, err))
1077 goto cleanup;
1078 break;
1079 case EAT_MESSAGE_RANGE:
1080 if (!eat_message_range(leaf, flags, ps, err, mv))
1081 goto cleanup;
1082 break;
1083 case EAT_QUERY:
1084 if (!eat_query(leaf, flags, ps, err, m))
1085 goto cleanup;
1086 break;
1087 default:
1088 break;
1089 }
1090 }
1091 implicit = true;
1092 break;
1093 }
1094
1095 case '(':
1096 {
1097 p = find_matching_paren(ps->dptr + 1);
1098 if (p[0] != ')')
1099 {
1100 buf_printf(err, _("mismatched parentheses: %s"), ps->dptr);
1101 goto cleanup;
1102 }
1103 /* compile the sub-expression */
1104 buf = mutt_strn_dup(ps->dptr + 1, p - (ps->dptr + 1));
1105 struct PatternList *sub = mutt_pattern_comp(mv, menu, buf, flags, err);
1106 FREE(&buf);
1107 if (!sub)
1108 goto cleanup;
1109 struct Pattern *leaf = SLIST_FIRST(sub);
1110 if (curlist)
1111 {
1112 attach_leaf(curlist, leaf);
1113 FREE(&sub);
1114 }
1115 else
1116 {
1117 curlist = sub;
1118 }
1119 leaf->pat_not ^= pat_not;
1120 leaf->all_addr |= all_addr;
1121 leaf->is_alias |= is_alias;
1122 pat_not = false;
1123 all_addr = false;
1124 is_alias = false;
1125 ps->dptr = p + 1; /* restore location */
1126 break;
1127 }
1128
1129 default:
1130 buf_printf(err, _("error in pattern at: %s"), ps->dptr);
1131 goto cleanup;
1132 }
1133 SKIPWS(ps->dptr);
1134 }
1135 buf_pool_release(&ps);
1136
1137 if (!curlist)
1138 {
1139 buf_strcpy(err, _("empty pattern"));
1140 return NULL;
1141 }
1142
1143 if (SLIST_NEXT(SLIST_FIRST(curlist), entries))
1144 {
1145 struct Pattern *root = attach_new_root(&curlist);
1146 root->op = pat_or ? MUTT_PAT_OR : MUTT_PAT_AND;
1147 }
1148
1149 return curlist;
1150
1151cleanup:
1152 mutt_pattern_free(&curlist);
1153 buf_pool_release(&ps);
1154 return NULL;
1155}
Email Address Handling.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
void buf_seek(struct Buffer *buf, size_t offset)
Set current read/write position to offset from beginning.
Definition: buffer.c:622
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:291
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
#define MUTT_PDR_PLUS
Extend the range using '+'.
Definition: compile.c:56
#define MUTT_PDR_NO_FLAGS
No flags are set.
Definition: compile.c:54
bool eval_date_minmax(struct Pattern *pat, const char *s, struct Buffer *err)
Evaluate a date-range pattern against 'now'.
Definition: compile.c:496
static struct Pattern * attach_new_root(struct PatternList **curlist)
Create a new Pattern as a parent for a List.
Definition: compile.c:869
uint16_t ParseDateRangeFlags
Flags for parse_date_range(), e.g. MUTT_PDR_MINUS.
Definition: compile.c:53
#define MUTT_PDR_ERROR
Invalid pattern.
Definition: compile.c:60
#define MUTT_PDR_ABSOLUTE
Absolute pattern range.
Definition: compile.c:58
static struct Pattern * attach_new_leaf(struct PatternList **curlist)
Attach a new Pattern to a List.
Definition: compile.c:885
static struct Pattern * attach_leaf(struct PatternList *list, struct Pattern *leaf)
Attach a Pattern to a Pattern List.
Definition: compile.c:847
static const char * parse_date_range(const char *pc, struct tm *min, struct tm *max, bool have_min, struct tm *base_min, struct Buffer *err)
Parse a date range.
Definition: compile.c:364
static char * find_matching_paren(char *s)
Find the matching parenthesis.
Definition: compile.c:754
static void adjust_date_range(struct tm *min, struct tm *max)
Put a date range in the correct order.
Definition: compile.c:458
static struct Pattern * mutt_pattern_new(void)
Create a new Pattern.
Definition: compile.c:823
static const char * get_offset(struct tm *tm, const char *s, int sign)
Calculate a symbolic offset.
Definition: compile.c:224
#define MUTT_PDR_DONE
Pattern parse successfully.
Definition: compile.c:59
static const char * get_date(const char *s, struct tm *t, struct Buffer *err)
Parse a (partial) date in dd/mm/yyyy format.
Definition: compile.c:278
static struct PatternList * mutt_pattern_list_new(void)
Create a new list containing a Pattern.
Definition: compile.c:832
struct PatternList * mutt_pattern_comp(struct MailboxView *mv, struct Menu *menu, const char *s, PatternCompFlags flags, struct Buffer *err)
Create a Pattern.
Definition: compile.c:906
#define MUTT_PDR_ERRORDONE
Definition: compile.c:63
void mutt_pattern_free(struct PatternList **pat)
Free a Pattern.
Definition: compile.c:778
#define MUTT_PDR_MINUS
Pattern contains a range.
Definition: compile.c:55
#define MUTT_PDR_WINDOW
Extend the range in both directions using '*'.
Definition: compile.c:57
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:223
int parse_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: extract.c:50
#define TOKEN_COMMENT
Don't reap comments.
Definition: extract.h:52
#define TOKEN_PATTERN
~%=!| are terms (for patterns)
Definition: extract.h:51
bool mutt_file_map_lines(mutt_file_map_t func, void *user_data, FILE *fp, ReadLineFlags flags)
Process lines of text read from a file pointer.
Definition: file.c:910
#define mutt_file_fclose(FP)
Definition: file.h:149
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:40
char ** EnvList
Private copy of the environment variables.
Definition: globals.c:78
struct Group * mutt_pattern_group(const char *pat)
Match a pattern to a Group.
Definition: group.c:117
static bool eat_query(struct Pattern *pat, PatternCompFlags flags, struct Buffer *s, struct Buffer *err, struct Mailbox *m)
Parse a query for an external search program - Implements eat_arg_t -.
Definition: compile.c:143
bool eat_message_range(struct Pattern *pat, PatternCompFlags flags, struct Buffer *s, struct Buffer *err, struct MailboxView *mv)
Parse a range of message numbers - Implements eat_arg_t -.
Definition: message.c:282
static bool eat_range(struct Pattern *pat, PatternCompFlags flags, struct Buffer *s, struct Buffer *err)
Parse a number range - Implements eat_arg_t -.
Definition: compile.c:624
static bool eat_date(struct Pattern *pat, PatternCompFlags flags, struct Buffer *s, struct Buffer *err)
Parse a date pattern - Implements eat_arg_t -.
Definition: compile.c:715
static bool eat_regex(struct Pattern *pat, PatternCompFlags flags, struct Buffer *s, struct Buffer *err)
Parse a regex - Implements eat_arg_t -.
Definition: compile.c:68
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
static bool add_query_msgid(char *line, int line_num, void *user_data)
Parse a Message-Id and add it to a list - Implements mutt_file_map_t -.
Definition: compile.c:123
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:65
void mutt_list_clear(struct ListHead *h)
Free a list, but NOT its strings.
Definition: list.c:166
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:354
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
#define FREE(x)
Definition: memory.h:45
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition: date.c:906
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition: date.c:242
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:456
void mutt_date_normalize_time(struct tm *tm)
Fix the contents of a struct tm.
Definition: date.c:310
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:220
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, char **envlist)
Set up filter program.
Definition: filter.c:209
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
char * mutt_path_escape(const char *src)
Escapes single quotes in a path for a command string.
Definition: path.c:426
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:380
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:565
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:551
View of a Mailbox.
Text parsing functions.
const struct PatternFlags * lookup_tag(char tag)
Lookup a pattern modifier.
Definition: flags.c:198
#define MUTT_PC_SEND_MODE_SEARCH
Allow send-mode body searching.
Definition: lib.h:71
uint8_t PatternCompFlags
Flags for mutt_pattern_comp(), e.g. MUTT_PC_FULL_MSG.
Definition: lib.h:67
@ MUTT_PAT_OR
Either pattern can match.
Definition: lib.h:138
@ MUTT_PAT_CHILDREN
Pattern matches a child email.
Definition: lib.h:141
@ MUTT_PAT_PARENT
Pattern matches parent.
Definition: lib.h:140
@ MUTT_PAT_AND
Both patterns must match.
Definition: lib.h:137
@ MUTT_PAT_THREAD
Pattern matches email thread.
Definition: lib.h:139
#define MUTT_PC_PATTERN_DYNAMIC
Enable runtime date range evaluation.
Definition: lib.h:70
@ EAT_RANGE
Process a number (range)
Definition: private.h:66
@ EAT_MESSAGE_RANGE
Process a message number (range)
Definition: private.h:67
@ EAT_DATE
Process a date (range)
Definition: private.h:65
@ EAT_QUERY
Process a query string.
Definition: private.h:68
@ EAT_REGEX
Process a regex.
Definition: private.h:64
#define MUTT_MAXRANGE
Definition: private.h:152
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
#define SLIST_FOREACH(var, head, field)
Definition: queue.h:231
#define SLIST_INIT(head)
Definition: queue.h:256
#define SLIST_INSERT_HEAD(head, elm, field)
Definition: queue.h:265
#define SLIST_NEXT(elm, field)
Definition: queue.h:270
#define SLIST_FIRST(head)
Definition: queue.h:229
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:49
GUI display the mailboxes in a side panel.
Key value store.
#define SKIPWS(ch)
Definition: string2.h:45
String manipulation buffer.
Definition: buffer.h:36
char * dptr
Current read/write position.
Definition: buffer.h:38
char * data
Pointer to data.
Definition: buffer.h:37
View of a Mailbox.
Definition: mview.h:40
struct Mailbox * mailbox
Current Mailbox.
Definition: mview.h:51
A mailbox.
Definition: mailbox.h:79
Definition: lib.h:79
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
Mapping between user character and internal constant.
Definition: private.h:75
enum PatternEat eat_arg
Type of function needed to parse flag, e.g. EAT_DATE.
Definition: private.h:80
PatternCompFlags flags
Pattern flags, e.g. MUTT_PC_FULL_MSG.
Definition: private.h:78
int op
Operation to perform, e.g. MUTT_PAT_SCORE.
Definition: private.h:77
A simple (non-regex) pattern.
Definition: lib.h:77
bool group_match
Check a group of Addresses.
Definition: lib.h:82
union Pattern::@1 p
bool all_addr
All Addresses in the list must match.
Definition: lib.h:80
struct Group * group
Address group if group_match is set.
Definition: lib.h:93
struct PatternList * child
Arguments to logical operation.
Definition: lib.h:90
long min
Minimum for range checks.
Definition: lib.h:88
bool string_match
Check a string for a match.
Definition: lib.h:81
regex_t * regex
Compiled regex, for non-pattern matching.
Definition: lib.h:92
struct ListHead multi_cases
Multiple strings for ~I pattern.
Definition: lib.h:95
char * str
String, if string_match is set.
Definition: lib.h:94
bool is_alias
Is there an alias for this Address?
Definition: lib.h:84
bool ign_case
Ignore case for local string_match searches.
Definition: lib.h:83
long max
Maximum for range checks.
Definition: lib.h:89
bool dynamic
Evaluate date ranges at run time.
Definition: lib.h:85
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: lib.h:78
bool sendmode
Evaluate searches in send-mode.
Definition: lib.h:86
bool is_multi
Multiple case (only for ~I pattern now)
Definition: lib.h:87
bool pat_not
Pattern should be inverted (not)
Definition: lib.h:79