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