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