NeoMutt  2022-04-29-70-g0c028c
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
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 = 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];
117  regerror(rc2, pat->p.regex, errmsg, sizeof(errmsg));
118  mutt_buffer_add_printf(err, "'%s': %s", buf->data, errmsg);
119  FREE(&pat->p.regex);
120  goto out;
121  }
122  }
123 
124  rc = true;
125 
126 out:
128  return rc;
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 = 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 
218 out:
219  mutt_buffer_pool_release(&cmd_buf);
220  mutt_buffer_pool_release(&tok_buf);
221  return rc;
222 }
223 
236 static 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 
290 static 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  snprintf(err->data, err->dsize, _("Invalid day of month: %s"), s);
321  return NULL;
322  }
323  if ((t->tm_mon < 0) || (t->tm_mon > 11))
324  {
325  snprintf(err->data, err->dsize, _("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 
376 static 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 
462 static 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 
500 bool 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 
628 static 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 
719 static 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 
739 static bool is_menu_available(struct Buffer *s, regmatch_t pmatch[], int kind,
740  struct Buffer *err, 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 
776 static 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 
812 static 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 
849 static void order_range(struct Pattern *pat)
850 {
851  if (pat->min <= pat->max)
852  return;
853  int num = pat->min;
854  pat->min = pat->max;
855  pat->max = num;
856 }
857 
868 static 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=%d pat->max=%d\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 
932 static 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 
977 static 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  snprintf(err->data, err->dsize, _("Error in expression: %s"), pexpr);
987  goto out;
988  }
989 
990  if (mutt_buffer_is_empty(tmp))
991  {
992  snprintf(err->data, err->dsize, "%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 
1004 out:
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 #ifdef USE_DEBUG_GRAPHVIZ
1062  FREE(&np->raw_pattern);
1063 #endif
1064  mutt_pattern_free(&np->child);
1065  FREE(&np);
1066 
1067  np = next;
1068  }
1069 
1070  FREE(pat);
1071 }
1072 
1077 static struct Pattern *mutt_pattern_new(void)
1078 {
1079  return mutt_mem_calloc(1, sizeof(struct Pattern));
1080 }
1081 
1086 static 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 
1101 static 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 
1123 static 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 
1139 static 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 
1160 struct 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 
1403 cleanup:
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: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
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 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 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:776
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 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_leaf(struct PatternList *list, struct Pattern *leaf)
Attach a Pattern to a Pattern List.
Definition: compile.c:1101
static void adjust_date_range(struct tm *min, struct tm *max)
Put a date range in the correct order.
Definition: compile.c:462
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
#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
#define MUTT_PDR_DONE
Pattern parse successfully.
Definition: compile.c:58
static struct Pattern * attach_new_leaf(struct PatternList **curlist)
Attach a new Pattern to a List.
Definition: compile.c:1139
static struct PatternList * mutt_pattern_list_new(void)
Create a new list containing a Pattern.
Definition: compile.c:1086
#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:376
static const char * get_offset(struct tm *tm, const char *s, int sign)
Calculate a symbolic offset.
Definition: compile.c:236
static struct Pattern * attach_new_root(struct PatternList **curlist)
Create a new Pattern as a parent for a List.
Definition: compile.c:1123
static void order_range(struct Pattern *pat)
Put a range in order.
Definition: compile.c:849
static struct Pattern * mutt_pattern_new(void)
Create a new Pattern.
Definition: compile.c:1077
#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 MailboxView for this Pattern?
Definition: compile.c:739
#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
#define MUTT_PDR_WINDOW
Extend the range in both directions using '*'.
Definition: compile.c:56
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: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: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
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:211
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: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: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
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:613
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:428
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:599
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
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
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: mview.c:412
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
int max
Maximum for range checks.
Definition: lib.h:82
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
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
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
int min
Minimum for range checks.
Definition: lib.h:81
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