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