NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
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 "email/lib.h" // IWYU pragma: keep
44#include "core/lib.h"
45#include "lib.h"
46#include "menu/lib.h"
47#include "parse/lib.h"
48#include "mview.h"
49
50// clang-format off
51typedef uint16_t ParseDateRangeFlags;
52#define MUTT_PDR_NO_FLAGS 0
53#define MUTT_PDR_MINUS (1 << 0)
54#define MUTT_PDR_PLUS (1 << 1)
55#define MUTT_PDR_WINDOW (1 << 2)
56#define MUTT_PDR_ABSOLUTE (1 << 3)
57#define MUTT_PDR_DONE (1 << 4)
58#define MUTT_PDR_ERROR (1 << 8)
59// clang-format on
60
61#define MUTT_PDR_ERRORDONE (MUTT_PDR_ERROR | MUTT_PDR_DONE)
62
67{
71};
72
73#define KILO 1024
74#define MEGA 1048576
75
79static bool eat_regex(struct Pattern *pat, PatternCompFlags flags,
80 struct Buffer *s, struct Buffer *err)
81{
82 struct Buffer *buf = mutt_buffer_pool_get();
83 bool rc = false;
84 char *pexpr = s->dptr;
85 if ((parse_extract_token(buf, s, TOKEN_PATTERN | TOKEN_COMMENT) != 0) || !buf->data)
86 {
87 mutt_buffer_printf(err, _("Error in expression: %s"), pexpr);
88 goto out;
89 }
90 if (buf->data[0] == '\0')
91 {
92 mutt_buffer_printf(err, "%s", _("Empty expression"));
93 goto out;
94 }
95
96 if (pat->string_match)
97 {
98 pat->p.str = mutt_str_dup(buf->data);
99 pat->ign_case = mutt_mb_is_lower(buf->data);
100 }
101 else if (pat->group_match)
102 {
103 pat->p.group = mutt_pattern_group(buf->data);
104 }
105 else
106 {
107 pat->p.regex = mutt_mem_calloc(1, sizeof(regex_t));
108#ifdef USE_DEBUG_GRAPHVIZ
109 pat->raw_pattern = mutt_str_dup(buf->data);
110#endif
111 uint16_t case_flags = mutt_mb_is_lower(buf->data) ? REG_ICASE : 0;
112 int rc2 = REG_COMP(pat->p.regex, buf->data, REG_NEWLINE | REG_NOSUB | case_flags);
113 if (rc2 != 0)
114 {
115 char errmsg[256] = { 0 };
116 regerror(rc2, pat->p.regex, errmsg, sizeof(errmsg));
117 mutt_buffer_printf(err, "'%s': %s", buf->data, errmsg);
118 FREE(&pat->p.regex);
119 goto out;
120 }
121 }
122
123 rc = true;
124
125out:
127 return rc;
128}
129
134static bool add_query_msgid(char *line, int line_num, void *user_data)
135{
136 struct ListHead *msgid_list = (struct ListHead *) (user_data);
137 char *nows = mutt_str_skip_whitespace(line);
138 if (*nows == '\0')
139 return true;
141 mutt_list_insert_tail(msgid_list, mutt_str_dup(nows));
142 return true;
143}
144
154static bool eat_query(struct Pattern *pat, PatternCompFlags flags,
155 struct Buffer *s, struct Buffer *err, struct Mailbox *m)
156{
157 struct Buffer *cmd_buf = mutt_buffer_pool_get();
158 struct Buffer *tok_buf = mutt_buffer_pool_get();
159 bool rc = false;
160
161 FILE *fp = NULL;
162
163 const char *const c_external_search_command = cs_subset_string(NeoMutt->sub, "external_search_command");
164 if (!c_external_search_command)
165 {
166 mutt_buffer_printf(err, "%s", _("No search command defined"));
167 goto out;
168 }
169
170 char *pexpr = s->dptr;
171 if ((parse_extract_token(tok_buf, s, TOKEN_PATTERN | TOKEN_COMMENT) != 0) ||
172 !tok_buf->data)
173 {
174 mutt_buffer_printf(err, _("Error in expression: %s"), pexpr);
175 goto out;
176 }
177 if (*tok_buf->data == '\0')
178 {
179 mutt_buffer_printf(err, "%s", _("Empty expression"));
180 goto out;
181 }
182
183 mutt_buffer_addstr(cmd_buf, c_external_search_command);
184 mutt_buffer_addch(cmd_buf, ' ');
185
186 if (m)
187 {
188 char *escaped_folder = mutt_path_escape(mailbox_path(m));
189 mutt_debug(LL_DEBUG2, "escaped folder path: %s\n", escaped_folder);
190 mutt_buffer_addch(cmd_buf, '\'');
191 mutt_buffer_addstr(cmd_buf, escaped_folder);
192 mutt_buffer_addch(cmd_buf, '\'');
193 }
194 else
195 {
196 mutt_buffer_addch(cmd_buf, '/');
197 }
198 mutt_buffer_addch(cmd_buf, ' ');
199 mutt_buffer_addstr(cmd_buf, tok_buf->data);
200
201 mutt_message(_("Running search command: %s ..."), cmd_buf->data);
202 pat->is_multi = true;
204 pid_t pid = filter_create(cmd_buf->data, NULL, &fp, NULL);
205 if (pid < 0)
206 {
207 mutt_buffer_printf(err, "unable to fork command: %s\n", cmd_buf->data);
208 goto out;
209 }
210
212 mutt_file_fclose(&fp);
213 filter_wait(pid);
214
215 rc = true;
216
217out:
218 mutt_buffer_pool_release(&cmd_buf);
219 mutt_buffer_pool_release(&tok_buf);
220 return rc;
221}
222
235static const char *get_offset(struct tm *tm, const char *s, int sign)
236{
237 char *ps = NULL;
238 int offset = strtol(s, &ps, 0);
239 if (((sign < 0) && (offset > 0)) || ((sign > 0) && (offset < 0)))
240 offset = -offset;
241
242 switch (*ps)
243 {
244 case 'y':
245 tm->tm_year += offset;
246 break;
247 case 'm':
248 tm->tm_mon += offset;
249 break;
250 case 'w':
251 tm->tm_mday += 7 * offset;
252 break;
253 case 'd':
254 tm->tm_mday += offset;
255 break;
256 case 'H':
257 tm->tm_hour += offset;
258 break;
259 case 'M':
260 tm->tm_min += offset;
261 break;
262 case 'S':
263 tm->tm_sec += offset;
264 break;
265 default:
266 return s;
267 }
269 return ps + 1;
270}
271
289static const char *get_date(const char *s, struct tm *t, struct Buffer *err)
290{
291 char *p = NULL;
292 struct tm tm = mutt_date_localtime(mutt_date_now());
293 bool iso8601 = true;
294
295 for (int v = 0; v < 8; v++)
296 {
297 if (s[v] && (s[v] >= '0') && (s[v] <= '9'))
298 continue;
299
300 iso8601 = false;
301 break;
302 }
303
304 if (iso8601)
305 {
306 int year = 0;
307 int month = 0;
308 int mday = 0;
309 sscanf(s, "%4d%2d%2d", &year, &month, &mday);
310
311 t->tm_year = year;
312 if (t->tm_year > 1900)
313 t->tm_year -= 1900;
314 t->tm_mon = month - 1;
315 t->tm_mday = mday;
316
317 if ((t->tm_mday < 1) || (t->tm_mday > 31))
318 {
319 mutt_buffer_printf(err, _("Invalid day of month: %s"), s);
320 return NULL;
321 }
322 if ((t->tm_mon < 0) || (t->tm_mon > 11))
323 {
324 mutt_buffer_printf(err, _("Invalid month: %s"), s);
325 return NULL;
326 }
327
328 return (s + 8);
329 }
330
331 t->tm_mday = strtol(s, &p, 10);
332 if ((t->tm_mday < 1) || (t->tm_mday > 31))
333 {
334 mutt_buffer_printf(err, _("Invalid day of month: %s"), s);
335 return NULL;
336 }
337 if (*p != '/')
338 {
339 /* fill in today's month and year */
340 t->tm_mon = tm.tm_mon;
341 t->tm_year = tm.tm_year;
342 return p;
343 }
344 p++;
345 t->tm_mon = strtol(p, &p, 10) - 1;
346 if ((t->tm_mon < 0) || (t->tm_mon > 11))
347 {
348 mutt_buffer_printf(err, _("Invalid month: %s"), p);
349 return NULL;
350 }
351 if (*p != '/')
352 {
353 t->tm_year = tm.tm_year;
354 return p;
355 }
356 p++;
357 t->tm_year = strtol(p, &p, 10);
358 if (t->tm_year < 70) /* year 2000+ */
359 t->tm_year += 100;
360 else if (t->tm_year > 1900)
361 t->tm_year -= 1900;
362 return p;
363}
364
375static const char *parse_date_range(const char *pc, struct tm *min, struct tm *max,
376 bool have_min, struct tm *base_min, struct Buffer *err)
377{
379 while (*pc && ((flags & MUTT_PDR_DONE) == 0))
380 {
381 const char *pt = NULL;
382 char ch = *pc++;
383 SKIPWS(pc);
384 switch (ch)
385 {
386 case '-':
387 {
388 /* try a range of absolute date minus offset of Ndwmy */
389 pt = get_offset(min, pc, -1);
390 if (pc == pt)
391 {
392 if (flags == MUTT_PDR_NO_FLAGS)
393 { /* nothing yet and no offset parsed => absolute date? */
394 if (!get_date(pc, max, err))
395 flags |= (MUTT_PDR_ABSOLUTE | MUTT_PDR_ERRORDONE); /* done bad */
396 else
397 {
398 /* reestablish initial base minimum if not specified */
399 if (!have_min)
400 memcpy(min, base_min, sizeof(struct tm));
401 flags |= (MUTT_PDR_ABSOLUTE | MUTT_PDR_DONE); /* done good */
402 }
403 }
404 else
405 flags |= MUTT_PDR_ERRORDONE;
406 }
407 else
408 {
409 pc = pt;
410 if ((flags == MUTT_PDR_NO_FLAGS) && !have_min)
411 { /* the very first "-3d" without a previous absolute date */
412 max->tm_year = min->tm_year;
413 max->tm_mon = min->tm_mon;
414 max->tm_mday = min->tm_mday;
415 }
416 flags |= MUTT_PDR_MINUS;
417 }
418 break;
419 }
420 case '+':
421 { /* enlarge plus range */
422 pt = get_offset(max, pc, 1);
423 if (pc == pt)
424 flags |= MUTT_PDR_ERRORDONE;
425 else
426 {
427 pc = pt;
428 flags |= MUTT_PDR_PLUS;
429 }
430 break;
431 }
432 case '*':
433 { /* enlarge window in both directions */
434 pt = get_offset(min, pc, -1);
435 if (pc == pt)
436 flags |= MUTT_PDR_ERRORDONE;
437 else
438 {
439 pc = get_offset(max, pc, 1);
440 flags |= MUTT_PDR_WINDOW;
441 }
442 break;
443 }
444 default:
445 flags |= MUTT_PDR_ERRORDONE;
446 }
447 SKIPWS(pc);
448 }
449 if ((flags & MUTT_PDR_ERROR) && !(flags & MUTT_PDR_ABSOLUTE))
450 { /* get_date has its own error message, don't overwrite it here */
451 mutt_buffer_printf(err, _("Invalid relative date: %s"), pc - 1);
452 }
453 return (flags & MUTT_PDR_ERROR) ? NULL : pc;
454}
455
461static void adjust_date_range(struct tm *min, struct tm *max)
462{
463 if ((min->tm_year > max->tm_year) ||
464 ((min->tm_year == max->tm_year) && (min->tm_mon > max->tm_mon)) ||
465 ((min->tm_year == max->tm_year) && (min->tm_mon == max->tm_mon) &&
466 (min->tm_mday > max->tm_mday)))
467 {
468 int tmp;
469
470 tmp = min->tm_year;
471 min->tm_year = max->tm_year;
472 max->tm_year = tmp;
473
474 tmp = min->tm_mon;
475 min->tm_mon = max->tm_mon;
476 max->tm_mon = tmp;
477
478 tmp = min->tm_mday;
479 min->tm_mday = max->tm_mday;
480 max->tm_mday = tmp;
481
482 min->tm_hour = 0;
483 min->tm_min = 0;
484 min->tm_sec = 0;
485 max->tm_hour = 23;
486 max->tm_min = 59;
487 max->tm_sec = 59;
488 }
489}
490
499bool eval_date_minmax(struct Pattern *pat, const char *s, struct Buffer *err)
500{
501 /* the '0' time is Jan 1, 1970 UTC, so in order to prevent a negative time
502 * when doing timezone conversion, we use Jan 2, 1970 UTC as the base here */
503 struct tm min = { 0 };
504 min.tm_mday = 2;
505 min.tm_year = 70;
506
507 /* Arbitrary year in the future. Don't set this too high or
508 * mutt_date_make_time() returns something larger than will fit in a time_t
509 * on some systems */
510 struct tm max = { 0 };
511 max.tm_year = 130;
512 max.tm_mon = 11;
513 max.tm_mday = 31;
514 max.tm_hour = 23;
515 max.tm_min = 59;
516 max.tm_sec = 59;
517
518 if (strchr("<>=", s[0]))
519 {
520 /* offset from current time
521 * <3d less than three days ago
522 * >3d more than three days ago
523 * =3d exactly three days ago */
524 struct tm *tm = NULL;
525 bool exact = false;
526
527 if (s[0] == '<')
528 {
530 tm = &min;
531 }
532 else
533 {
535 tm = &max;
536
537 if (s[0] == '=')
538 exact = true;
539 }
540
541 /* Reset the HMS unless we are relative matching using one of those
542 * offsets. */
543 char *offset_type = NULL;
544 strtol(s + 1, &offset_type, 0);
545 if (!(*offset_type && strchr("HMS", *offset_type)))
546 {
547 tm->tm_hour = 23;
548 tm->tm_min = 59;
549 tm->tm_sec = 59;
550 }
551
552 /* force negative offset */
553 get_offset(tm, s + 1, -1);
554
555 if (exact)
556 {
557 /* start at the beginning of the day in question */
558 memcpy(&min, &max, sizeof(max));
559 min.tm_hour = 0;
560 min.tm_sec = 0;
561 min.tm_min = 0;
562 }
563 }
564 else
565 {
566 const char *pc = s;
567
568 bool have_min = false;
569 bool until_now = false;
570 if (isdigit((unsigned char) *pc))
571 {
572 /* minimum date specified */
573 pc = get_date(pc, &min, err);
574 if (!pc)
575 {
576 return false;
577 }
578 have_min = true;
579 SKIPWS(pc);
580 if (*pc == '-')
581 {
582 const char *pt = pc + 1;
583 SKIPWS(pt);
584 until_now = (*pt == '\0');
585 }
586 }
587
588 if (!until_now)
589 { /* max date or relative range/window */
590
591 struct tm base_min;
592
593 if (!have_min)
594 { /* save base minimum and set current date, e.g. for "-3d+1d" */
595 memcpy(&base_min, &min, sizeof(base_min));
597 min.tm_hour = 0;
598 min.tm_sec = 0;
599 min.tm_min = 0;
600 }
601
602 /* preset max date for relative offsets,
603 * if nothing follows we search for messages on a specific day */
604 max.tm_year = min.tm_year;
605 max.tm_mon = min.tm_mon;
606 max.tm_mday = min.tm_mday;
607
608 if (!parse_date_range(pc, &min, &max, have_min, &base_min, err))
609 { /* bail out on any parsing error */
610 return false;
611 }
612 }
613 }
614
615 /* Since we allow two dates to be specified we'll have to adjust that. */
616 adjust_date_range(&min, &max);
617
618 pat->min = mutt_date_make_time(&min, true);
619 pat->max = mutt_date_make_time(&max, true);
620
621 return true;
622}
623
627static bool eat_range(struct Pattern *pat, PatternCompFlags flags,
628 struct Buffer *s, struct Buffer *err)
629{
630 char *tmp = NULL;
631 bool do_exclusive = false;
632 bool skip_quote = false;
633
634 /* If simple_search is set to "~m %s", the range will have double quotes
635 * around it... */
636 if (*s->dptr == '"')
637 {
638 s->dptr++;
639 skip_quote = true;
640 }
641 if (*s->dptr == '<')
642 do_exclusive = true;
643 if ((*s->dptr != '-') && (*s->dptr != '<'))
644 {
645 /* range minimum */
646 if (*s->dptr == '>')
647 {
648 pat->max = MUTT_MAXRANGE;
649 pat->min = strtol(s->dptr + 1, &tmp, 0) + 1; /* exclusive range */
650 }
651 else
652 pat->min = strtol(s->dptr, &tmp, 0);
653 if (toupper((unsigned char) *tmp) == 'K') /* is there a prefix? */
654 {
655 pat->min *= 1024;
656 tmp++;
657 }
658 else if (toupper((unsigned char) *tmp) == 'M')
659 {
660 pat->min *= 1048576;
661 tmp++;
662 }
663 if (*s->dptr == '>')
664 {
665 s->dptr = tmp;
666 return true;
667 }
668 if (*tmp != '-')
669 {
670 /* exact value */
671 pat->max = pat->min;
672 s->dptr = tmp;
673 return true;
674 }
675 tmp++;
676 }
677 else
678 {
679 s->dptr++;
680 tmp = s->dptr;
681 }
682
683 if (isdigit((unsigned char) *tmp))
684 {
685 /* range maximum */
686 pat->max = strtol(tmp, &tmp, 0);
687 if (toupper((unsigned char) *tmp) == 'K')
688 {
689 pat->max *= 1024;
690 tmp++;
691 }
692 else if (toupper((unsigned char) *tmp) == 'M')
693 {
694 pat->max *= 1048576;
695 tmp++;
696 }
697 if (do_exclusive)
698 (pat->max)--;
699 }
700 else
701 pat->max = MUTT_MAXRANGE;
702
703 if (skip_quote && (*tmp == '"'))
704 tmp++;
705
706 SKIPWS(tmp);
707 s->dptr = tmp;
708 return true;
709}
710
718static int report_regerror(int regerr, regex_t *preg, struct Buffer *err)
719{
720 size_t ds = err->dsize;
721
722 if (regerror(regerr, preg, err->data, ds) > ds)
723 mutt_debug(LL_DEBUG2, "warning: buffer too small for regerror\n");
724 /* The return value is fixed, exists only to shorten code at callsite */
725 return RANGE_E_SYNTAX;
726}
727
738static bool is_menu_available(struct Buffer *s, regmatch_t pmatch[], int kind,
739 struct Buffer *err, const struct Menu *menu)
740{
741 const char *context_req_chars[] = {
742 [RANGE_K_REL] = ".0123456789",
743 [RANGE_K_ABS] = ".",
744 [RANGE_K_LT] = "",
745 [RANGE_K_GT] = "",
746 [RANGE_K_BARE] = ".",
747 };
748
749 /* First decide if we're going to need the menu at all.
750 * Relative patterns need it if they contain a dot or a number.
751 * Absolute patterns only need it if they contain a dot. */
752 char *context_loc = strpbrk(s->dptr + pmatch[0].rm_so, context_req_chars[kind]);
753 if (!context_loc || (context_loc >= &s->dptr[pmatch[0].rm_eo]))
754 return true;
755
756 /* We need a current message. Do we actually have one? */
757 if (menu)
758 return true;
759
760 /* Nope. */
761 mutt_buffer_strcpy(err, _("No current message"));
762 return false;
763}
764
775static int scan_range_num(struct Buffer *s, regmatch_t pmatch[], int group,
776 int kind, struct Mailbox *m, struct Menu *menu)
777{
778 int num = (int) strtol(&s->dptr[pmatch[group].rm_so], NULL, 0);
779 unsigned char c = (unsigned char) (s->dptr[pmatch[group].rm_eo - 1]);
780 if (toupper(c) == 'K')
781 num *= KILO;
782 else if (toupper(c) == 'M')
783 num *= MEGA;
784 switch (kind)
785 {
786 case RANGE_K_REL:
787 {
788 struct Email *e = mutt_get_virt_email(m, menu_get_index(menu));
789 return num + EMSG(e);
790 }
791 case RANGE_K_LT:
792 return num - 1;
793 case RANGE_K_GT:
794 return num + 1;
795 default:
796 return num;
797 }
798}
799
811static int scan_range_slot(struct Buffer *s, regmatch_t pmatch[], int grp,
812 int side, int kind, struct Mailbox *m, struct Menu *menu)
813{
814 /* This means the left or right subpattern was empty, e.g. ",." */
815 if ((pmatch[grp].rm_so == -1) || (pmatch[grp].rm_so == pmatch[grp].rm_eo))
816 {
817 if (side == RANGE_S_LEFT)
818 return 1;
819 if (side == RANGE_S_RIGHT)
820 return m->msg_count;
821 }
822 /* We have something, so determine what */
823 unsigned char c = (unsigned char) (s->dptr[pmatch[grp].rm_so]);
824 switch (c)
825 {
826 case RANGE_CIRCUM:
827 return 1;
828 case RANGE_DOLLAR:
829 return m->msg_count;
830 case RANGE_DOT:
831 {
832 struct Email *e = mutt_get_virt_email(m, menu_get_index(menu));
833 return EMSG(e);
834 }
835 case RANGE_LT:
836 case RANGE_GT:
837 return scan_range_num(s, pmatch, grp + 1, kind, m, menu);
838 default:
839 /* Only other possibility: a number */
840 return scan_range_num(s, pmatch, grp, kind, m, menu);
841 }
842}
843
848static void order_range(struct Pattern *pat)
849{
850 if (pat->min <= pat->max)
851 return;
852 long num = pat->min;
853 pat->min = pat->max;
854 pat->max = num;
855}
856
867static int eat_range_by_regex(struct Pattern *pat, struct Buffer *s, int kind,
868 struct Buffer *err, struct Mailbox *m, struct Menu *menu)
869{
870 int regerr;
871 regmatch_t pmatch[RANGE_RX_GROUPS];
872 struct RangeRegex *pspec = &RangeRegexes[kind];
873
874 /* First time through, compile the big regex */
875 if (!pspec->ready)
876 {
877 regerr = regcomp(&pspec->cooked, pspec->raw, REG_EXTENDED);
878 if (regerr != 0)
879 return report_regerror(regerr, &pspec->cooked, err);
880 pspec->ready = true;
881 }
882
883 /* Match the pattern buffer against the compiled regex.
884 * No match means syntax error. */
885 regerr = regexec(&pspec->cooked, s->dptr, RANGE_RX_GROUPS, pmatch, 0);
886 if (regerr != 0)
887 return report_regerror(regerr, &pspec->cooked, err);
888
889 if (!is_menu_available(s, pmatch, kind, err, menu))
890 return RANGE_E_MVIEW;
891
892 /* Snarf the contents of the two sides of the range. */
893 pat->min = scan_range_slot(s, pmatch, pspec->lgrp, RANGE_S_LEFT, kind, m, menu);
894 pat->max = scan_range_slot(s, pmatch, pspec->rgrp, RANGE_S_RIGHT, kind, m, menu);
895 mutt_debug(LL_DEBUG1, "pat->min=%ld pat->max=%ld\n", pat->min, pat->max);
896
897 /* Special case for a bare 0. */
898 if ((kind == RANGE_K_BARE) && (pat->min == 0) && (pat->max == 0))
899 {
900 if (!m || !menu)
901 {
902 mutt_buffer_strcpy(err, _("No current message"));
903 return RANGE_E_MVIEW;
904 }
905 struct Email *e = mutt_get_virt_email(m, menu_get_index(menu));
906 if (!e)
907 return RANGE_E_MVIEW;
908
909 pat->max = EMSG(e);
910 pat->min = pat->max;
911 }
912
913 /* Since we don't enforce order, we must swap bounds if they're backward */
914 order_range(pat);
915
916 /* Slide pointer past the entire match. */
917 s->dptr += pmatch[0].rm_eo;
918 return RANGE_E_OK;
919}
920
931static bool eat_message_range(struct Pattern *pat, PatternCompFlags flags,
932 struct Buffer *s, struct Buffer *err,
933 struct Mailbox *m, struct Menu *menu)
934{
935 if (!m || !menu)
936 {
937 // We need these for pretty much anything
938 mutt_buffer_strcpy(err, _("No Context"));
939 return false;
940 }
941
942 bool skip_quote = false;
943
944 /* If simple_search is set to "~m %s", the range will have double quotes
945 * around it... */
946 if (*s->dptr == '"')
947 {
948 s->dptr++;
949 skip_quote = true;
950 }
951
952 for (int i_kind = 0; i_kind != RANGE_K_INVALID; i_kind++)
953 {
954 switch (eat_range_by_regex(pat, s, i_kind, err, m, menu))
955 {
956 case RANGE_E_MVIEW:
957 /* This means it matched syntactically but lacked context.
958 * No point in continuing. */
959 break;
960 case RANGE_E_SYNTAX:
961 /* Try another syntax, then */
962 continue;
963 case RANGE_E_OK:
964 if (skip_quote && (*s->dptr == '"'))
965 s->dptr++;
966 SKIPWS(s->dptr);
967 return true;
968 }
969 }
970 return false;
971}
972
976static bool eat_date(struct Pattern *pat, PatternCompFlags flags,
977 struct Buffer *s, struct Buffer *err)
978{
979 struct Buffer *tmp = mutt_buffer_pool_get();
980 bool rc = false;
981
982 char *pexpr = s->dptr;
984 {
985 mutt_buffer_printf(err, _("Error in expression: %s"), pexpr);
986 goto out;
987 }
988
989 if (mutt_buffer_is_empty(tmp))
990 {
991 mutt_buffer_printf(err, "%s", _("Empty expression"));
992 goto out;
993 }
994
995 if (flags & MUTT_PC_PATTERN_DYNAMIC)
996 {
997 pat->dynamic = true;
998 pat->p.str = mutt_str_dup(tmp->data);
999 }
1000
1001 rc = eval_date_minmax(pat, tmp->data, err);
1002
1003out:
1005 return rc;
1006}
1007
1015static /* const */ char *find_matching_paren(/* const */ char *s)
1016{
1017 int level = 1;
1018
1019 for (; *s; s++)
1020 {
1021 if (*s == '(')
1022 level++;
1023 else if (*s == ')')
1024 {
1025 level--;
1026 if (level == 0)
1027 break;
1028 }
1029 }
1030 return s;
1031}
1032
1037void mutt_pattern_free(struct PatternList **pat)
1038{
1039 if (!pat || !*pat)
1040 return;
1041
1042 struct Pattern *np = SLIST_FIRST(*pat), *next = NULL;
1043
1044 while (np)
1045 {
1046 next = SLIST_NEXT(np, entries);
1047
1048 if (np->is_multi)
1050 else if (np->string_match || np->dynamic)
1051 FREE(&np->p.str);
1052 else if (np->group_match)
1053 np->p.group = NULL;
1054 else if (np->p.regex)
1055 {
1056 regfree(np->p.regex);
1057 FREE(&np->p.regex);
1058 }
1059
1060#ifdef USE_DEBUG_GRAPHVIZ
1061 FREE(&np->raw_pattern);
1062#endif
1064 FREE(&np);
1065
1066 np = next;
1067 }
1068
1069 FREE(pat);
1070}
1071
1076static struct Pattern *mutt_pattern_new(void)
1077{
1078 return mutt_mem_calloc(1, sizeof(struct Pattern));
1079}
1080
1085static struct PatternList *mutt_pattern_list_new(void)
1086{
1087 struct PatternList *h = mutt_mem_calloc(1, sizeof(struct PatternList));
1088 SLIST_INIT(h);
1089 struct Pattern *p = mutt_pattern_new();
1090 SLIST_INSERT_HEAD(h, p, entries);
1091 return h;
1092}
1093
1100static struct Pattern *attach_leaf(struct PatternList *list, struct Pattern *leaf)
1101{
1102 struct Pattern *last = NULL;
1103 SLIST_FOREACH(last, list, entries)
1104 {
1105 // TODO - or we could use a doubly-linked list
1106 if (SLIST_NEXT(last, entries) == NULL)
1107 {
1108 SLIST_NEXT(last, entries) = leaf;
1109 break;
1110 }
1111 }
1112 return leaf;
1113}
1114
1122static struct Pattern *attach_new_root(struct PatternList **curlist)
1123{
1124 struct PatternList *root = mutt_pattern_list_new();
1125 struct Pattern *leaf = SLIST_FIRST(root);
1126 leaf->child = *curlist;
1127 *curlist = root;
1128 return leaf;
1129}
1130
1138static struct Pattern *attach_new_leaf(struct PatternList **curlist)
1139{
1140 if (*curlist)
1141 {
1142 return attach_leaf(*curlist, mutt_pattern_new());
1143 }
1144 else
1145 {
1146 return attach_new_root(curlist);
1147 }
1148}
1149
1159struct PatternList *mutt_pattern_comp(struct Mailbox *m, struct Menu *menu, const char *s,
1160 PatternCompFlags flags, struct Buffer *err)
1161{
1162 /* curlist when assigned will always point to a list containing at least one node
1163 * with a Pattern value. */
1164 struct PatternList *curlist = NULL;
1165 bool pat_not = false;
1166 bool all_addr = false;
1167 bool pat_or = false;
1168 bool implicit = true; /* used to detect logical AND operator */
1169 bool is_alias = false;
1170 const struct PatternFlags *entry = NULL;
1171 char *p = NULL;
1172 char *buf = NULL;
1173 struct Buffer ps;
1174
1175 if (!s || (s[0] == '\0'))
1176 {
1177 mutt_buffer_strcpy(err, _("empty pattern"));
1178 return NULL;
1179 }
1180
1181 mutt_buffer_init(&ps);
1182 ps.dptr = (char *) s;
1183 ps.dsize = mutt_str_len(s);
1184
1185 while (*ps.dptr)
1186 {
1187 SKIPWS(ps.dptr);
1188 switch (*ps.dptr)
1189 {
1190 case '^':
1191 ps.dptr++;
1192 all_addr = !all_addr;
1193 break;
1194 case '!':
1195 ps.dptr++;
1196 pat_not = !pat_not;
1197 break;
1198 case '@':
1199 ps.dptr++;
1200 is_alias = !is_alias;
1201 break;
1202 case '|':
1203 if (!pat_or)
1204 {
1205 if (!curlist)
1206 {
1207 mutt_buffer_printf(err, _("error in pattern at: %s"), ps.dptr);
1208 return NULL;
1209 }
1210
1211 struct Pattern *pat = SLIST_FIRST(curlist);
1212 if (SLIST_NEXT(pat, entries))
1213 {
1214 /* A & B | C == (A & B) | C */
1215 struct Pattern *root = attach_new_root(&curlist);
1216 root->op = MUTT_PAT_AND;
1217 }
1218
1219 pat_or = true;
1220 }
1221 ps.dptr++;
1222 implicit = false;
1223 pat_not = false;
1224 all_addr = false;
1225 is_alias = false;
1226 break;
1227 case '%':
1228 case '=':
1229 case '~':
1230 {
1231 if (ps.dptr[1] == '\0')
1232 {
1233 mutt_buffer_printf(err, _("missing pattern: %s"), ps.dptr);
1234 goto cleanup;
1235 }
1236 short thread_op = 0;
1237 if (ps.dptr[1] == '(')
1238 thread_op = MUTT_PAT_THREAD;
1239 else if ((ps.dptr[1] == '<') && (ps.dptr[2] == '('))
1240 thread_op = MUTT_PAT_PARENT;
1241 else if ((ps.dptr[1] == '>') && (ps.dptr[2] == '('))
1242 thread_op = MUTT_PAT_CHILDREN;
1243 if (thread_op != 0)
1244 {
1245 ps.dptr++; /* skip ~ */
1246 if ((thread_op == MUTT_PAT_PARENT) || (thread_op == MUTT_PAT_CHILDREN))
1247 ps.dptr++;
1248 p = find_matching_paren(ps.dptr + 1);
1249 if (p[0] != ')')
1250 {
1251 mutt_buffer_printf(err, _("mismatched parentheses: %s"), ps.dptr);
1252 goto cleanup;
1253 }
1254 struct Pattern *leaf = attach_new_leaf(&curlist);
1255 leaf->op = thread_op;
1256 leaf->pat_not = pat_not;
1257 leaf->all_addr = all_addr;
1258 leaf->is_alias = is_alias;
1259 pat_not = false;
1260 all_addr = false;
1261 is_alias = false;
1262 /* compile the sub-expression */
1263 buf = mutt_strn_dup(ps.dptr + 1, p - (ps.dptr + 1));
1264 leaf->child = mutt_pattern_comp(m, menu, buf, flags, err);
1265 if (!leaf->child)
1266 {
1267 FREE(&buf);
1268 goto cleanup;
1269 }
1270 FREE(&buf);
1271 ps.dptr = p + 1; /* restore location */
1272 SKIPWS(ps.dptr);
1273 break;
1274 }
1275 if (implicit && pat_or)
1276 {
1277 /* A | B & C == (A | B) & C */
1278 struct Pattern *root = attach_new_root(&curlist);
1279 root->op = MUTT_PAT_OR;
1280 pat_or = false;
1281 }
1282
1283 entry = lookup_tag(ps.dptr[1]);
1284 if (!entry)
1285 {
1286 mutt_buffer_printf(err, _("%c: invalid pattern modifier"), *ps.dptr);
1287 goto cleanup;
1288 }
1289 if (entry->flags && ((flags & entry->flags) == 0))
1290 {
1291 mutt_buffer_printf(err, _("%c: not supported in this mode"), *ps.dptr);
1292 goto cleanup;
1293 }
1294
1295 struct Pattern *leaf = attach_new_leaf(&curlist);
1296 leaf->pat_not = pat_not;
1297 leaf->all_addr = all_addr;
1298 leaf->is_alias = is_alias;
1299 leaf->string_match = (ps.dptr[0] == '=');
1300 leaf->group_match = (ps.dptr[0] == '%');
1301 leaf->sendmode = (flags & MUTT_PC_SEND_MODE_SEARCH);
1302 leaf->op = entry->op;
1303 pat_not = false;
1304 all_addr = false;
1305 is_alias = false;
1306
1307 ps.dptr++; /* move past the ~ */
1308 ps.dptr++; /* eat the operator and any optional whitespace */
1309 SKIPWS(ps.dptr);
1310 if (entry->eat_arg)
1311 {
1312 if (ps.dptr[0] == '\0')
1313 {
1314 mutt_buffer_printf(err, "%s", _("missing parameter"));
1315 goto cleanup;
1316 }
1317 switch (entry->eat_arg)
1318 {
1319 case EAT_REGEX:
1320 if (!eat_regex(leaf, flags, &ps, err))
1321 goto cleanup;
1322 break;
1323 case EAT_DATE:
1324 if (!eat_date(leaf, flags, &ps, err))
1325 goto cleanup;
1326 break;
1327 case EAT_RANGE:
1328 if (!eat_range(leaf, flags, &ps, err))
1329 goto cleanup;
1330 break;
1331 case EAT_MESSAGE_RANGE:
1332 if (!eat_message_range(leaf, flags, &ps, err, m, menu))
1333 goto cleanup;
1334 break;
1335 case EAT_QUERY:
1336 if (!eat_query(leaf, flags, &ps, err, m))
1337 goto cleanup;
1338 break;
1339 default:
1340 break;
1341 }
1342 }
1343 implicit = true;
1344 break;
1345 }
1346
1347 case '(':
1348 {
1349 p = find_matching_paren(ps.dptr + 1);
1350 if (p[0] != ')')
1351 {
1352 mutt_buffer_printf(err, _("mismatched parentheses: %s"), ps.dptr);
1353 goto cleanup;
1354 }
1355 /* compile the sub-expression */
1356 buf = mutt_strn_dup(ps.dptr + 1, p - (ps.dptr + 1));
1357 struct PatternList *sub = mutt_pattern_comp(m, menu, buf, flags, err);
1358 FREE(&buf);
1359 if (!sub)
1360 goto cleanup;
1361 struct Pattern *leaf = SLIST_FIRST(sub);
1362 if (curlist)
1363 {
1364 attach_leaf(curlist, leaf);
1365 FREE(&sub);
1366 }
1367 else
1368 {
1369 curlist = sub;
1370 }
1371 leaf->pat_not ^= pat_not;
1372 leaf->all_addr |= all_addr;
1373 leaf->is_alias |= is_alias;
1374 pat_not = false;
1375 all_addr = false;
1376 is_alias = false;
1377 ps.dptr = p + 1; /* restore location */
1378 SKIPWS(ps.dptr);
1379 break;
1380 }
1381
1382 default:
1383 mutt_buffer_printf(err, _("error in pattern at: %s"), ps.dptr);
1384 goto cleanup;
1385 }
1386 }
1387
1388 if (!curlist)
1389 {
1390 mutt_buffer_strcpy(err, _("empty pattern"));
1391 return NULL;
1392 }
1393
1394 if (SLIST_NEXT(SLIST_FIRST(curlist), entries))
1395 {
1396 struct Pattern *root = attach_new_root(&curlist);
1397 root->op = pat_or ? MUTT_PAT_OR : MUTT_PAT_AND;
1398 }
1399
1400 return curlist;
1401
1402cleanup:
1403 mutt_pattern_free(&curlist);
1404 return NULL;
1405}
Email Address Handling.
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:365
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
#define MUTT_PDR_PLUS
Extend the range using '+'.
Definition: compile.c:54
#define MUTT_PDR_NO_FLAGS
No flags are set.
Definition: compile.c:52
bool eval_date_minmax(struct Pattern *pat, const char *s, struct Buffer *err)
Evaluate a date-range pattern against 'now'.
Definition: compile.c:499
static struct Pattern * attach_new_root(struct PatternList **curlist)
Create a new Pattern as a parent for a List.
Definition: compile.c:1122
static int scan_range_slot(struct Buffer *s, regmatch_t pmatch[], int grp, int side, int kind, struct Mailbox *m, struct Menu *menu)
Parse a range of message numbers.
Definition: compile.c:811
uint16_t ParseDateRangeFlags
Flags for parse_date_range(), e.g. MUTT_PDR_MINUS.
Definition: compile.c:51
#define MUTT_PDR_ERROR
Invalid pattern.
Definition: compile.c:58
static int scan_range_num(struct Buffer *s, regmatch_t pmatch[], int group, int kind, struct Mailbox *m, struct Menu *menu)
Parse a number range.
Definition: compile.c:775
#define MUTT_PDR_ABSOLUTE
Absolute pattern range.
Definition: compile.c:56
static int eat_range_by_regex(struct Pattern *pat, struct Buffer *s, int kind, struct Buffer *err, struct Mailbox *m, struct Menu *menu)
Parse a range given as a regex.
Definition: compile.c:867
static struct Pattern * attach_new_leaf(struct PatternList **curlist)
Attach a new Pattern to a List.
Definition: compile.c:1138
static struct Pattern * attach_leaf(struct PatternList *list, struct Pattern *leaf)
Attach a Pattern to a Pattern List.
Definition: compile.c:1100
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:375
static char * find_matching_paren(char *s)
Find the matching parenthesis.
Definition: compile.c:1015
static void adjust_date_range(struct tm *min, struct tm *max)
Put a date range in the correct order.
Definition: compile.c:461
static struct Pattern * mutt_pattern_new(void)
Create a new Pattern.
Definition: compile.c:1076
#define MEGA
Definition: compile.c:74
EatRangeError
Error codes for eat_range_by_regex()
Definition: compile.c:67
@ RANGE_E_MVIEW
Range requires MailboxView, but none available.
Definition: compile.c:70
@ RANGE_E_OK
Range is valid.
Definition: compile.c:68
@ RANGE_E_SYNTAX
Range contains syntax error.
Definition: compile.c:69
static const char * get_offset(struct tm *tm, const char *s, int sign)
Calculate a symbolic offset.
Definition: compile.c:235
#define MUTT_PDR_DONE
Pattern parse successfully.
Definition: compile.c:57
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:289
struct PatternList * mutt_pattern_comp(struct Mailbox *m, struct Menu *menu, const char *s, PatternCompFlags flags, struct Buffer *err)
Create a Pattern.
Definition: compile.c:1159
#define KILO
Definition: compile.c:73
static struct PatternList * mutt_pattern_list_new(void)
Create a new list containing a Pattern.
Definition: compile.c:1085
static void order_range(struct Pattern *pat)
Put a range in order.
Definition: compile.c:848
#define MUTT_PDR_ERRORDONE
Definition: compile.c:61
void mutt_pattern_free(struct PatternList **pat)
Free a Pattern.
Definition: compile.c:1037
#define MUTT_PDR_MINUS
Pattern contains a range.
Definition: compile.c:53
static int report_regerror(int regerr, regex_t *preg, struct Buffer *err)
Create a regex error message.
Definition: compile.c:718
static bool is_menu_available(struct Buffer *s, regmatch_t pmatch[], int kind, struct Buffer *err, const struct Menu *menu)
Do we need a MailboxView for this Pattern?
Definition: compile.c:738
#define MUTT_PDR_WINDOW
Extend the range in both directions using '*'.
Definition: compile.c:55
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition: date.c:653
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition: date.c:229
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:428
void mutt_date_normalize_time(struct tm *tm)
Fix the contents of a struct tm.
Definition: date.c:297
Structs that make up an email.
int parse_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: extract.c:48
#define TOKEN_COMMENT
Don't reap comments.
Definition: extract.h:39
#define TOKEN_PATTERN
~%=!| are terms (for patterns)
Definition: extract.h:38
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:151
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:838
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:39
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
struct Group * mutt_pattern_group(const char *pat)
Match a pattern to a Group.
Definition: group.c:112
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:154
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:627
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:976
static bool eat_message_range(struct Pattern *pat, PatternCompFlags flags, struct Buffer *s, struct Buffer *err, struct Mailbox *m, struct Menu *menu)
Parse a range of message numbers - Implements eat_arg_t -.
Definition: compile.c:931
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:79
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
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:134
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: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:355
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:43
GUI present the user with a selectable list.
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition: menu.c:154
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:520
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:451
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:636
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:622
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: mview.c:414
The "currently-open" mailbox.
Text parsing functions.
const struct PatternFlags * lookup_tag(char tag)
Lookup a pattern modifier.
Definition: flags.c:200
#define MUTT_PC_SEND_MODE_SEARCH
Allow send-mode body searching.
Definition: lib.h:64
uint8_t PatternCompFlags
Flags for mutt_pattern_comp(), e.g. MUTT_PC_FULL_MSG.
Definition: lib.h:60
@ MUTT_PAT_OR
Either pattern can match.
Definition: lib.h:131
@ MUTT_PAT_CHILDREN
Pattern matches a child email.
Definition: lib.h:134
@ MUTT_PAT_PARENT
Pattern matches parent.
Definition: lib.h:133
@ MUTT_PAT_AND
Both patterns must match.
Definition: lib.h:130
@ MUTT_PAT_THREAD
Pattern matches email thread.
Definition: lib.h:132
#define MUTT_PC_PATTERN_DYNAMIC
Enable runtime date range evaluation.
Definition: lib.h:63
@ RANGE_S_LEFT
Left side of range.
Definition: private.h:123
@ RANGE_S_RIGHT
Right side of range.
Definition: private.h:124
#define RANGE_DOLLAR
Definition: private.h:114
#define RANGE_GT
Definition: private.h:116
@ EAT_RANGE
Process a number (range)
Definition: private.h:51
@ EAT_MESSAGE_RANGE
Process a message number (range)
Definition: private.h:52
@ EAT_DATE
Process a date (range)
Definition: private.h:50
@ EAT_QUERY
Process a query string.
Definition: private.h:53
@ EAT_REGEX
Process a regex.
Definition: private.h:49
#define MUTT_MAXRANGE
Definition: private.h:129
#define RANGE_RX_GROUPS
Definition: private.h:110
#define RANGE_CIRCUM
Definition: private.h:113
#define EMSG(e)
Definition: private.h:127
#define RANGE_DOT
Definition: private.h:112
#define RANGE_LT
Definition: private.h:115
@ RANGE_K_REL
Relative range.
Definition: private.h:86
@ RANGE_K_ABS
Absolute range.
Definition: private.h:87
@ RANGE_K_LT
Less-than range.
Definition: private.h:88
@ RANGE_K_INVALID
Range is invalid.
Definition: private.h:92
@ RANGE_K_BARE
Single symbol.
Definition: private.h:90
@ RANGE_K_GT
Greater-than range.
Definition: private.h:89
struct RangeRegex RangeRegexes[]
Set of Regexes for various range types.
Definition: pattern.c:64
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#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:53
GUI display the mailboxes in a side panel.
Key value store.
#define SKIPWS(ch)
Definition: string2.h:46
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
The envelope/body of an email.
Definition: email.h:37
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
Definition: lib.h:70
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Mapping between user character and internal constant.
Definition: private.h:60
enum PatternEat eat_arg
Type of function needed to parse flag, e.g. EAT_DATE.
Definition: private.h:65
PatternCompFlags flags
Pattern flags, e.g. MUTT_PC_FULL_MSG.
Definition: private.h:63
int op
Operation to perform, e.g. MUTT_PAT_SCORE.
Definition: private.h:62
A simple (non-regex) pattern.
Definition: lib.h:70
bool group_match
Check a group of Addresses.
Definition: lib.h:75
union Pattern::@1 p
bool all_addr
All Addresses in the list must match.
Definition: lib.h:73
struct Group * group
Address group if group_match is set.
Definition: lib.h:86
struct PatternList * child
Arguments to logical operation.
Definition: lib.h:83
long min
Minimum for range checks.
Definition: lib.h:81
bool string_match
Check a string for a match.
Definition: lib.h:74
regex_t * regex
Compiled regex, for non-pattern matching.
Definition: lib.h:85
struct ListHead multi_cases
Multiple strings for ~I pattern.
Definition: lib.h:88
char * str
String, if string_match is set.
Definition: lib.h:87
bool is_alias
Is there an alias for this Address?
Definition: lib.h:77
bool ign_case
Ignore case for local string_match searches.
Definition: lib.h:76
long max
Maximum for range checks.
Definition: lib.h:82
bool dynamic
Evaluate date ranges at run time.
Definition: lib.h:78
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: lib.h:71
bool sendmode
Evaluate searches in send-mode.
Definition: lib.h:79
bool is_multi
Multiple case (only for ~I pattern now)
Definition: lib.h:80
bool pat_not
Pattern should be inverted (not)
Definition: lib.h:72
Regular expression representing a range.
Definition: private.h:73
int lgrp
Paren group matching the left side.
Definition: private.h:75
int rgrp
Paren group matching the right side.
Definition: private.h:76
regex_t cooked
Compiled form.
Definition: private.h:78
bool ready
Compiled yet?
Definition: private.h:77
const char * raw
Regex as string.
Definition: private.h:74