NeoMutt  2021-10-29-33-g41675a
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 "context.h"
49 #include "init.h"
50 
51 // clang-format off
52 typedef 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 
80 static bool eat_regex(struct Pattern *pat, PatternCompFlags flags,
81  struct Buffer *s, struct Buffer *err)
82 {
83  struct Buffer buf;
84 
85  mutt_buffer_init(&buf);
86  char *pexpr = s->dptr;
87  if ((mutt_extract_token(&buf, s, MUTT_TOKEN_PATTERN | MUTT_TOKEN_COMMENT) != 0) || !buf.data)
88  {
89  mutt_buffer_printf(err, _("Error in expression: %s"), pexpr);
90  FREE(&buf.data);
91  return false;
92  }
93  if (buf.data[0] == '\0')
94  {
95  mutt_buffer_printf(err, "%s", _("Empty expression"));
96  FREE(&buf.data);
97  return false;
98  }
99 
100  if (pat->string_match)
101  {
102  pat->p.str = mutt_str_dup(buf.data);
103  pat->ign_case = mutt_mb_is_lower(buf.data);
104  FREE(&buf.data);
105  }
106  else if (pat->group_match)
107  {
108  pat->p.group = mutt_pattern_group(buf.data);
109  FREE(&buf.data);
110  }
111  else
112  {
113  pat->p.regex = mutt_mem_malloc(sizeof(regex_t));
114  uint16_t case_flags = mutt_mb_is_lower(buf.data) ? REG_ICASE : 0;
115  int rc = REG_COMP(pat->p.regex, buf.data, REG_NEWLINE | REG_NOSUB | case_flags);
116  if (rc != 0)
117  {
118  char errmsg[256];
119  regerror(rc, pat->p.regex, errmsg, sizeof(errmsg));
120  mutt_buffer_add_printf(err, "'%s': %s", buf.data, errmsg);
121  FREE(&buf.data);
122  FREE(&pat->p.regex);
123  return false;
124  }
125  FREE(&buf.data);
126  }
127 
128  return true;
129 }
130 
135 static 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 
155 static bool eat_query(struct Pattern *pat, PatternCompFlags flags,
156  struct Buffer *s, struct Buffer *err, struct Mailbox *m)
157 {
158  struct Buffer cmd_buf;
159  struct Buffer tok_buf;
160  FILE *fp = NULL;
161 
162  const char *const c_external_search_command =
163  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  return false;
168  }
169 
170  mutt_buffer_init(&tok_buf);
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  return false;
177  }
178  if (*tok_buf.data == '\0')
179  {
180  mutt_buffer_printf(err, "%s", _("Empty expression"));
181  FREE(&tok_buf.data);
182  return false;
183  }
184 
185  mutt_buffer_init(&cmd_buf);
186  mutt_buffer_addstr(&cmd_buf, c_external_search_command);
187  mutt_buffer_addch(&cmd_buf, ' ');
188 
189  if (m)
190  {
191  char *escaped_folder = mutt_path_escape(mailbox_path(m));
192  mutt_debug(LL_DEBUG2, "escaped folder path: %s\n", escaped_folder);
193  mutt_buffer_addch(&cmd_buf, '\'');
194  mutt_buffer_addstr(&cmd_buf, escaped_folder);
195  mutt_buffer_addch(&cmd_buf, '\'');
196  }
197  else
198  {
199  mutt_buffer_addch(&cmd_buf, '/');
200  }
201  mutt_buffer_addch(&cmd_buf, ' ');
202  mutt_buffer_addstr(&cmd_buf, tok_buf.data);
203  FREE(&tok_buf.data);
204 
205  mutt_message(_("Running search command: %s ..."), cmd_buf.data);
206  pat->is_multi = true;
208  pid_t pid = filter_create(cmd_buf.data, NULL, &fp, NULL);
209  if (pid < 0)
210  {
211  mutt_buffer_printf(err, "unable to fork command: %s\n", cmd_buf.data);
212  FREE(&cmd_buf.data);
213  return false;
214  }
215 
217  mutt_file_fclose(&fp);
218  filter_wait(pid);
219  FREE(&cmd_buf.data);
220  return true;
221 }
222 
235 static 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 
289 static 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  snprintf(err->data, err->dsize, _("Invalid day of month: %s"), s);
320  return NULL;
321  }
322  if ((t->tm_mon < 0) || (t->tm_mon > 11))
323  {
324  snprintf(err->data, err->dsize, _("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 
375 static 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 
461 static 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 
499 bool 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 
627 static 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 
718 static 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 
738 static bool is_menu_available(struct Buffer *s, regmatch_t pmatch[], int kind,
739  struct Buffer *err, 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 
775 static 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 
811 static 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 
848 static void order_range(struct Pattern *pat)
849 {
850  if (pat->min <= pat->max)
851  return;
852  int num = pat->min;
853  pat->min = pat->max;
854  pat->max = num;
855 }
856 
867 static 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_CTX;
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=%d pat->max=%d\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_CTX;
904  }
905  struct Email *e = mutt_get_virt_email(m, menu_get_index(menu));
906  if (!e)
907  return RANGE_E_CTX;
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 
931 static 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_CTX:
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 
976 static 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  snprintf(err->data, err->dsize, _("Error in expression: %s"), pexpr);
986  goto out;
987  }
988 
989  if (mutt_buffer_is_empty(tmp))
990  {
991  snprintf(err->data, err->dsize, "%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 
1003 out:
1005 
1006  return rc;
1007 }
1008 
1016 static /* 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 
1038 void 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  mutt_pattern_free(&np->child);
1062  FREE(&np);
1063 
1064  np = next;
1065  }
1066 
1067  FREE(pat);
1068 }
1069 
1074 static struct PatternList *mutt_pattern_node_new(void)
1075 {
1076  struct PatternList *h = mutt_mem_calloc(1, sizeof(struct PatternList));
1077  SLIST_INIT(h);
1078  struct Pattern *p = mutt_mem_calloc(1, sizeof(struct Pattern));
1079  SLIST_INSERT_HEAD(h, p, entries);
1080  return h;
1081 }
1082 
1092 struct PatternList *mutt_pattern_comp(struct Mailbox *m, struct Menu *menu, const char *s,
1093  PatternCompFlags flags, struct Buffer *err)
1094 {
1095  /* curlist when assigned will always point to a list containing at least one node
1096  * with a Pattern value. */
1097  struct PatternList *curlist = NULL;
1098  struct PatternList *tmp = NULL, *tmp2 = NULL;
1099  struct PatternList *last = NULL;
1100  bool pat_not = false;
1101  bool all_addr = false;
1102  bool pat_or = false;
1103  bool implicit = true; /* used to detect logical AND operator */
1104  bool is_alias = false;
1105  short thread_op;
1106  const struct PatternFlags *entry = NULL;
1107  char *p = NULL;
1108  char *buf = NULL;
1109  struct Buffer ps;
1110 
1111  if (!s || (s[0] == '\0'))
1112  {
1113  mutt_buffer_strcpy(err, _("empty pattern"));
1114  return NULL;
1115  }
1116 
1117  mutt_buffer_init(&ps);
1118  ps.dptr = (char *) s;
1119  ps.dsize = mutt_str_len(s);
1120 
1121  while (*ps.dptr)
1122  {
1123  SKIPWS(ps.dptr);
1124  switch (*ps.dptr)
1125  {
1126  case '^':
1127  ps.dptr++;
1128  all_addr = !all_addr;
1129  break;
1130  case '!':
1131  ps.dptr++;
1132  pat_not = !pat_not;
1133  break;
1134  case '@':
1135  ps.dptr++;
1136  is_alias = !is_alias;
1137  break;
1138  case '|':
1139  if (!pat_or)
1140  {
1141  if (!curlist)
1142  {
1143  mutt_buffer_printf(err, _("error in pattern at: %s"), ps.dptr);
1144  return NULL;
1145  }
1146 
1147  struct Pattern *pat = SLIST_FIRST(curlist);
1148 
1149  if (SLIST_NEXT(pat, entries))
1150  {
1151  /* A & B | C == (A & B) | C */
1152  tmp = mutt_pattern_node_new();
1153  pat = SLIST_FIRST(tmp);
1154  pat->op = MUTT_PAT_AND;
1155  pat->child = curlist;
1156 
1157  curlist = tmp;
1158  last = curlist;
1159  }
1160 
1161  pat_or = true;
1162  }
1163  ps.dptr++;
1164  implicit = false;
1165  pat_not = false;
1166  all_addr = false;
1167  is_alias = false;
1168  break;
1169  case '%':
1170  case '=':
1171  case '~':
1172  {
1173  struct Pattern *pat = NULL;
1174  if (ps.dptr[1] == '\0')
1175  {
1176  mutt_buffer_printf(err, _("missing pattern: %s"), ps.dptr);
1177  goto cleanup;
1178  }
1179  thread_op = 0;
1180  if (ps.dptr[1] == '(')
1181  thread_op = MUTT_PAT_THREAD;
1182  else if ((ps.dptr[1] == '<') && (ps.dptr[2] == '('))
1183  thread_op = MUTT_PAT_PARENT;
1184  else if ((ps.dptr[1] == '>') && (ps.dptr[2] == '('))
1185  thread_op = MUTT_PAT_CHILDREN;
1186  if (thread_op)
1187  {
1188  ps.dptr++; /* skip ~ */
1189  if ((thread_op == MUTT_PAT_PARENT) || (thread_op == MUTT_PAT_CHILDREN))
1190  ps.dptr++;
1191  p = find_matching_paren(ps.dptr + 1);
1192  if (p[0] != ')')
1193  {
1194  mutt_buffer_printf(err, _("mismatched parentheses: %s"), ps.dptr);
1195  goto cleanup;
1196  }
1197  tmp = mutt_pattern_node_new();
1198  pat = SLIST_FIRST(tmp);
1199  pat->op = thread_op;
1200  if (last)
1201  SLIST_NEXT(SLIST_FIRST(last), entries) = pat;
1202  else
1203  curlist = tmp;
1204  last = tmp;
1205  pat->pat_not ^= pat_not;
1206  pat->all_addr |= all_addr;
1207  pat->is_alias |= is_alias;
1208  pat_not = false;
1209  all_addr = false;
1210  is_alias = false;
1211  /* compile the sub-expression */
1212  buf = mutt_strn_dup(ps.dptr + 1, p - (ps.dptr + 1));
1213  tmp2 = mutt_pattern_comp(m, menu, buf, flags, err);
1214  if (!tmp2)
1215  {
1216  FREE(&buf);
1217  goto cleanup;
1218  }
1219  FREE(&buf);
1220  pat->child = tmp2;
1221  ps.dptr = p + 1; /* restore location */
1222  SKIPWS(ps.dptr);
1223  break;
1224  }
1225  if (implicit && pat_or)
1226  {
1227  /* A | B & C == (A | B) & C */
1228  tmp = mutt_pattern_node_new();
1229  pat = SLIST_FIRST(tmp);
1230  pat->op = MUTT_PAT_OR;
1231  pat->child = curlist;
1232  curlist = tmp;
1233  last = tmp;
1234  pat_or = false;
1235  }
1236 
1237  tmp = mutt_pattern_node_new();
1238  pat = SLIST_FIRST(tmp);
1239  pat->pat_not = pat_not;
1240  pat->all_addr = all_addr;
1241  pat->is_alias = is_alias;
1242  pat->string_match = (ps.dptr[0] == '=');
1243  pat->group_match = (ps.dptr[0] == '%');
1244  pat_not = false;
1245  all_addr = false;
1246  is_alias = false;
1247 
1248  if (last)
1249  SLIST_NEXT(SLIST_FIRST(last), entries) = pat;
1250  else
1251  curlist = tmp;
1252  if (curlist != last)
1253  FREE(&last);
1254  last = tmp;
1255 
1256  ps.dptr++; /* move past the ~ */
1257  entry = lookup_tag(*ps.dptr);
1258  if (!entry)
1259  {
1260  mutt_buffer_printf(err, _("%c: invalid pattern modifier"), *ps.dptr);
1261  goto cleanup;
1262  }
1263  if (entry->flags && ((flags & entry->flags) == 0))
1264  {
1265  mutt_buffer_printf(err, _("%c: not supported in this mode"), *ps.dptr);
1266  goto cleanup;
1267  }
1268  if (flags & MUTT_PC_SEND_MODE_SEARCH)
1269  pat->sendmode = true;
1270 
1271  pat->op = entry->op;
1272 
1273  ps.dptr++; /* eat the operator and any optional whitespace */
1274  SKIPWS(ps.dptr);
1275 
1276  if (entry->eat_arg)
1277  {
1278  if (ps.dptr[0] == '\0')
1279  {
1280  mutt_buffer_printf(err, "%s", _("missing parameter"));
1281  goto cleanup;
1282  }
1283  switch (entry->eat_arg)
1284  {
1285  case EAT_REGEX:
1286  if (!eat_regex(pat, flags, &ps, err))
1287  goto cleanup;
1288  break;
1289  case EAT_DATE:
1290  if (!eat_date(pat, flags, &ps, err))
1291  goto cleanup;
1292  break;
1293  case EAT_RANGE:
1294  if (!eat_range(pat, flags, &ps, err))
1295  goto cleanup;
1296  break;
1297  case EAT_MESSAGE_RANGE:
1298  if (!eat_message_range(pat, flags, &ps, err, m, menu))
1299  goto cleanup;
1300  break;
1301  case EAT_QUERY:
1302  if (!eat_query(pat, flags, &ps, err, m))
1303  goto cleanup;
1304  break;
1305  default:
1306  break;
1307  }
1308  }
1309  implicit = true;
1310  break;
1311  }
1312 
1313  case '(':
1314  {
1315  p = find_matching_paren(ps.dptr + 1);
1316  if (p[0] != ')')
1317  {
1318  mutt_buffer_printf(err, _("mismatched parentheses: %s"), ps.dptr);
1319  goto cleanup;
1320  }
1321  /* compile the sub-expression */
1322  buf = mutt_strn_dup(ps.dptr + 1, p - (ps.dptr + 1));
1323  tmp = mutt_pattern_comp(m, menu, buf, flags, err);
1324  FREE(&buf);
1325  if (!tmp)
1326  goto cleanup;
1327  struct Pattern *pat = SLIST_FIRST(tmp);
1328  if (last)
1329  SLIST_NEXT(SLIST_FIRST(last), entries) = pat;
1330  else
1331  curlist = tmp;
1332  last = tmp;
1333  pat = SLIST_FIRST(tmp);
1334  pat->pat_not ^= pat_not;
1335  pat->all_addr |= all_addr;
1336  pat->is_alias |= is_alias;
1337  pat_not = false;
1338  all_addr = false;
1339  is_alias = false;
1340  ps.dptr = p + 1; /* restore location */
1341  SKIPWS(ps.dptr);
1342  break;
1343  }
1344 
1345  default:
1346  mutt_buffer_printf(err, _("error in pattern at: %s"), ps.dptr);
1347  goto cleanup;
1348  }
1349  }
1350  if (!curlist)
1351  {
1352  mutt_buffer_strcpy(err, _("empty pattern"));
1353  return NULL;
1354  }
1355  if (curlist != tmp)
1356  FREE(&tmp);
1357  if (SLIST_NEXT(SLIST_FIRST(curlist), entries))
1358  {
1359  tmp = mutt_pattern_node_new();
1360  struct Pattern *pat = SLIST_FIRST(tmp);
1361  pat->op = pat_or ? MUTT_PAT_OR : MUTT_PAT_AND;
1362  pat->child = curlist;
1363  curlist = tmp;
1364  }
1365 
1366  return curlist;
1367 
1368 cleanup:
1369  mutt_pattern_free(&curlist);
1370  return NULL;
1371 }
Email Address Handling.
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
#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
static struct PatternList * mutt_pattern_node_new(void)
Create a new list containing a Pattern.
Definition: compile.c:1074
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 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:52
#define MUTT_PDR_ERROR
Invalid pattern.
Definition: compile.c:59
static char * find_matching_paren(char *s)
Find the matching parenthesis.
Definition: compile.c:1016
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
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:1092
#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:867
static void adjust_date_range(struct tm *min, struct tm *max)
Put a date range in the correct order.
Definition: compile.c:461
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
#define MEGA
Definition: compile.c:75
EatRangeError
Error codes for eat_range_by_regex()
Definition: compile.c:68
@ RANGE_E_OK
Range is valid.
Definition: compile.c:69
@ RANGE_E_SYNTAX
Range contains syntax error.
Definition: compile.c:70
@ RANGE_E_CTX
Range requires Context, but none available.
Definition: compile.c:71
#define MUTT_PDR_DONE
Pattern parse successfully.
Definition: compile.c:58
#define KILO
Definition: compile.c:74
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 const char * get_offset(struct tm *tm, const char *s, int sign)
Calculate a symbolic offset.
Definition: compile.c:235
static void order_range(struct Pattern *pat)
Put a range in order.
Definition: compile.c:848
#define MUTT_PDR_ERRORDONE
Definition: compile.c:62
void mutt_pattern_free(struct PatternList **pat)
Free a Pattern.
Definition: compile.c:1038
static bool is_menu_available(struct Buffer *s, regmatch_t pmatch[], int kind, struct Buffer *err, struct Menu *menu)
Do we need a Context for this Pattern?
Definition: compile.c:738
#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:718
#define MUTT_PDR_WINDOW
Extend the range in both directions using '*'.
Definition: compile.c:56
Convenience wrapper for the config headers.
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:412
The "currently-open" mailbox.
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:654
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:295
#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:153
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:797
#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: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: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
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:398
Config/command parsing.
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
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
@ 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:215
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:357
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
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:40
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:608
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
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:544
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:359
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:181
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:530
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:475
Many unsorted constants and some structs.
#define MUTT_TOKEN_COMMENT
Don't reap comments.
Definition: mutt.h:72
#define MUTT_TOKEN_PATTERN
~%=!| are terms (for patterns)
Definition: mutt.h:71
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:63
uint8_t PatternCompFlags
Flags for mutt_pattern_comp(), e.g. MUTT_PC_FULL_MSG.
Definition: lib.h:59
@ MUTT_PAT_OR
Either pattern can match.
Definition: lib.h:127
@ MUTT_PAT_CHILDREN
Pattern matches a child email.
Definition: lib.h:130
@ MUTT_PAT_PARENT
Pattern matches parent.
Definition: lib.h:129
@ MUTT_PAT_AND
Both patterns must match.
Definition: lib.h:126
@ MUTT_PAT_THREAD
Pattern matches email thread.
Definition: lib.h:128
#define MUTT_PC_PATTERN_DYNAMIC
Enable runtime date range evaluation.
Definition: lib.h:62
@ RANGE_S_LEFT
Left side of range.
Definition: private.h:113
@ RANGE_S_RIGHT
Right side of range.
Definition: private.h:114
#define RANGE_DOLLAR
Definition: private.h:104
#define RANGE_GT
Definition: private.h:106
@ EAT_RANGE
Process a number (range)
Definition: private.h:41
@ EAT_MESSAGE_RANGE
Process a message number (range)
Definition: private.h:42
@ EAT_DATE
Process a date (range)
Definition: private.h:40
@ EAT_QUERY
Process a query string.
Definition: private.h:43
@ EAT_REGEX
Process a regex.
Definition: private.h:39
#define MUTT_MAXRANGE
Definition: private.h:119
#define RANGE_RX_GROUPS
Definition: private.h:100
#define RANGE_CIRCUM
Definition: private.h:103
#define EMSG(e)
Definition: private.h:117
#define RANGE_DOT
Definition: private.h:102
#define RANGE_LT
Definition: private.h:105
@ RANGE_K_REL
Relative range.
Definition: private.h:76
@ RANGE_K_ABS
Absolute range.
Definition: private.h:77
@ RANGE_K_LT
Less-than range.
Definition: private.h:78
@ RANGE_K_INVALID
Range is invalid.
Definition: private.h:82
@ RANGE_K_BARE
Single symbol.
Definition: private.h:80
@ RANGE_K_GT
Greater-than range.
Definition: private.h:79
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_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:54
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:82
int msg_count
Total number of messages.
Definition: mailbox.h:91
Definition: lib.h:67
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:50
enum PatternEat eat_arg
Type of function needed to parse flag, e.g. EAT_DATE.
Definition: private.h:55
PatternCompFlags flags
Pattern flags, e.g. MUTT_PC_FULL_MSG.
Definition: private.h:53
int op
Operation to perform, e.g. MUTT_PAT_SCORE.
Definition: private.h:52
A simple (non-regex) pattern.
Definition: lib.h:69
bool group_match
Check a group of Addresses.
Definition: lib.h:74
union Pattern::@1 p
int max
Maximum for range checks.
Definition: lib.h:81
bool all_addr
All Addresses in the list must match.
Definition: lib.h:72
struct Group * group
Address group if group_match is set.
Definition: lib.h:85
struct PatternList * child
Arguments to logical operation.
Definition: lib.h:82
bool string_match
Check a string for a match.
Definition: lib.h:73
regex_t * regex
Compiled regex, for non-pattern matching.
Definition: lib.h:84
struct ListHead multi_cases
Multiple strings for ~I pattern.
Definition: lib.h:87
char * str
String, if string_match is set.
Definition: lib.h:86
bool is_alias
Is there an alias for this Address?
Definition: lib.h:76
bool ign_case
Ignore case for local string_match searches.
Definition: lib.h:75
bool dynamic
Evaluate date ranges at run time.
Definition: lib.h:77
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: lib.h:70
bool sendmode
Evaluate searches in send-mode.
Definition: lib.h:78
bool is_multi
Multiple case (only for ~I pattern now)
Definition: lib.h:79
int min
Minimum for range checks.
Definition: lib.h:80
bool pat_not
Pattern should be inverted (not)
Definition: lib.h:71
Regular expression representing a range.
Definition: private.h:63
int lgrp
Paren group matching the left side.
Definition: private.h:65
int rgrp
Paren group matching the right side.
Definition: private.h:66
regex_t cooked
Compiled form.
Definition: private.h:68
bool ready
Compiled yet?
Definition: private.h:67
const char * raw
Regex as string.
Definition: private.h:64