NeoMutt  2019-12-07
Teaching an old dog new tricks
DOXYGEN
init.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <ctype.h>
32 #include <errno.h>
33 #include <inttypes.h>
34 #include <limits.h>
35 #include <pwd.h>
36 #include <regex.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <sys/utsname.h>
43 #include <unistd.h>
44 #include "mutt/mutt.h"
45 #include "address/lib.h"
46 #include "email/lib.h"
47 #include "core/lib.h"
48 #include "mutt.h"
49 #include "init.h"
50 #include "alias.h"
51 #include "context.h"
52 #include "filter.h"
53 #include "keymap.h"
54 #include "monitor.h"
55 #include "mutt_menu.h"
56 #include "mutt_parse.h"
57 #include "mutt_window.h"
58 #include "mx.h"
59 #include "myvar.h"
60 #include "options.h"
61 #include "protos.h"
62 #include "sidebar.h"
63 #include "version.h"
64 #ifdef USE_HCACHE
65 #include "hcache/hcache.h"
66 #endif
67 #ifdef USE_NOTMUCH
68 #include "notmuch/mutt_notmuch.h"
69 #endif
70 #ifdef USE_IMAP
71 #include "imap/imap.h"
72 #endif
73 #ifdef ENABLE_NLS
74 #include <libintl.h>
75 #endif
76 
77 /* LIFO designed to contain the list of config files that have been sourced and
78  * avoid cyclic sourcing */
80 
81 #define MAX_ERRS 128
82 
83 #define NUM_VARS mutt_array_size(MuttVars)
84 #define NUM_COMMANDS mutt_array_size(Commands)
85 
86 /* Initial string that starts completion. No telling how much the user has
87  * typed so far. Allocate 1024 just to be sure! */
88 static char UserTyped[1024] = { 0 };
89 
90 static int NumMatched = 0; /* Number of matches for completion */
91 static char Completed[256] = { 0 }; /* completed string (command or variable) */
92 static const char **Matches;
93 /* this is a lie until mutt_init runs: */
95 
96 #ifdef USE_NOTMUCH
97 /* List of tags found in last call to mutt_nm_query_complete(). */
98 static char **nm_tags;
99 #endif
100 
105 {
109 };
110 
118 static void add_to_stailq(struct ListHead *head, const char *str)
119 {
120  /* don't add a NULL or empty string to the list */
121  if (!str || (*str == '\0'))
122  return;
123 
124  /* check to make sure the item is not already on this list */
125  struct ListNode *np = NULL;
126  STAILQ_FOREACH(np, head, entries)
127  {
128  if (mutt_str_strcasecmp(str, np->data) == 0)
129  {
130  return;
131  }
132  }
134 }
135 
139 static void alternates_clean(void)
140 {
141  if (!Context || !Context->mailbox)
142  return;
143 
144  struct Mailbox *m = Context->mailbox;
145  for (int i = 0; i < m->msg_count; i++)
146  {
147  struct Email *e = m->emails[i];
148  if (!e)
149  break;
150  e->recip_valid = false;
151  }
152 }
153 
157 static void attachments_clean(void)
158 {
159  if (!Context || !Context->mailbox)
160  return;
161 
162  struct Mailbox *m = Context->mailbox;
163  for (int i = 0; i < m->msg_count; i++)
164  {
165  struct Email *e = m->emails[i];
166  if (!e)
167  break;
168  e->attach_valid = false;
169  }
170 }
171 
176 static void matches_ensure_morespace(int current)
177 {
178  if (current <= (MatchesListsize - 2))
179  return;
180 
181  int base_space = MAX(NUM_VARS, NUM_COMMANDS) + 1;
182  int extra_space = MatchesListsize - base_space;
183  extra_space *= 2;
184  const int space = base_space + extra_space;
185  mutt_mem_realloc(&Matches, space * sizeof(char *));
186  memset(&Matches[current + 1], 0, space - current);
187  MatchesListsize = space;
188 }
189 
199 static void candidate(char *user, const char *src, char *dest, size_t dlen)
200 {
201  if (!dest || !user || !src)
202  return;
203 
204  if (strstr(src, user) != src)
205  return;
206 
208  Matches[NumMatched++] = src;
209  if (dest[0] == '\0')
210  mutt_str_strfcpy(dest, src, dlen);
211  else
212  {
213  int l;
214  for (l = 0; src[l] && src[l] == dest[l]; l++)
215  ;
216  dest[l] = '\0';
217  }
218 }
219 
223 static void clear_subject_mods(void)
224 {
225  if (!Context || !Context->mailbox)
226  return;
227 
228  struct Mailbox *m = Context->mailbox;
229  for (int i = 0; i < m->msg_count; i++)
230  {
231  struct Email *e = m->emails[i];
232  if (!e || !e->env)
233  continue;
234  FREE(&e->env->disp_subj);
235  }
236 }
237 
238 #ifdef USE_NOTMUCH
239 
245 static int complete_all_nm_tags(const char *pt)
246 {
247  int tag_count_1 = 0;
248  int tag_count_2 = 0;
249 
250  NumMatched = 0;
251  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
252  memset(Matches, 0, MatchesListsize);
253  memset(Completed, 0, sizeof(Completed));
254 
256 
257  /* Work out how many tags there are. */
258  if (nm_get_all_tags(Context->mailbox, NULL, &tag_count_1) || (tag_count_1 == 0))
259  goto done;
260 
261  /* Free the old list, if any. */
262  if (nm_tags)
263  {
264  for (int i = 0; nm_tags[i]; i++)
265  FREE(&nm_tags[i]);
266  FREE(&nm_tags);
267  }
268  /* Allocate a new list, with sentinel. */
269  nm_tags = mutt_mem_malloc((tag_count_1 + 1) * sizeof(char *));
270  nm_tags[tag_count_1] = NULL;
271 
272  /* Get all the tags. */
273  if (nm_get_all_tags(Context->mailbox, nm_tags, &tag_count_2) || (tag_count_1 != tag_count_2))
274  {
275  FREE(&nm_tags);
276  nm_tags = NULL;
278  return -1;
279  }
280 
281  /* Put them into the completion machinery. */
282  for (int num = 0; num < tag_count_1; num++)
283  {
284  candidate(UserTyped, nm_tags[num], Completed, sizeof(Completed));
285  }
286 
289 
290 done:
292  return 0;
293 }
294 #endif
295 
302 static int execute_commands(struct ListHead *p)
303 {
304  int rc = 0;
305  struct Buffer *err = mutt_buffer_pool_get();
306  struct Buffer *token = mutt_buffer_pool_get();
307 
308  struct ListNode *np = NULL;
309  STAILQ_FOREACH(np, p, entries)
310  {
311  enum CommandResult rc2 = mutt_parse_rc_line(np->data, token, err);
312  if (rc2 == MUTT_CMD_ERROR)
313  mutt_error(_("Error in command line: %s"), mutt_b2s(err));
314  else if (rc2 == MUTT_CMD_WARNING)
315  mutt_warning(_("Warning in command line: %s"), mutt_b2s(err));
316 
317  if ((rc2 == MUTT_CMD_ERROR) || (rc2 == MUTT_CMD_WARNING))
318  {
319  mutt_buffer_pool_release(&token);
321  return -1;
322  }
323  }
324  mutt_buffer_pool_release(&token);
326 
327  return rc;
328 }
329 
337 static char *find_cfg(const char *home, const char *xdg_cfg_home)
338 {
339  const char *names[] = {
340  "neomuttrc",
341  "muttrc",
342  NULL,
343  };
344 
345  const char *locations[][2] = {
346  { xdg_cfg_home, "neomutt/" },
347  { xdg_cfg_home, "mutt/" },
348  { home, ".neomutt/" },
349  { home, ".mutt/" },
350  { home, "." },
351  { NULL, NULL },
352  };
353 
354  for (int i = 0; locations[i][0] || locations[i][1]; i++)
355  {
356  if (!locations[i][0])
357  continue;
358 
359  for (int j = 0; names[j]; j++)
360  {
361  char buf[256];
362 
363  snprintf(buf, sizeof(buf), "%s/%s%s", locations[i][0], locations[i][1], names[j]);
364  if (access(buf, F_OK) == 0)
365  return mutt_str_strdup(buf);
366  }
367  }
368 
369  return NULL;
370 }
371 
372 #ifndef DOMAIN
373 
378 static char *getmailname(void)
379 {
380  char *mailname = NULL;
381  static const char *mn_files[] = { "/etc/mailname", "/etc/mail/mailname" };
382 
383  for (size_t i = 0; i < mutt_array_size(mn_files); i++)
384  {
385  FILE *fp = mutt_file_fopen(mn_files[i], "r");
386  if (!fp)
387  continue;
388 
389  size_t len = 0;
390  mailname = mutt_file_read_line(NULL, &len, fp, NULL, 0);
391  mutt_file_fclose(&fp);
392  if (mailname && *mailname)
393  break;
394 
395  FREE(&mailname);
396  }
397 
398  return mailname;
399 }
400 #endif
401 
410 static bool get_hostname(void)
411 {
412  char *str = NULL;
413  struct utsname utsname;
414 
415  if (C_Hostname)
416  {
417  str = C_Hostname;
418  }
419  else
420  {
421  /* The call to uname() shouldn't fail, but if it does, the system is horribly
422  * broken, and the system's networking configuration is in an unreliable
423  * state. We should bail. */
424  if ((uname(&utsname)) == -1)
425  {
426  mutt_perror(_("unable to determine nodename via uname()"));
427  return false; // TEST09: can't test
428  }
429 
430  str = utsname.nodename;
431  }
432 
433  /* some systems report the FQDN instead of just the hostname */
434  char *dot = strchr(str, '.');
435  if (dot)
437  else
439 
440  if (!C_Hostname)
441  {
442  /* now get FQDN. Use configured domain first, DNS next, then uname */
443 #ifdef DOMAIN
444  /* we have a compile-time domain name, use that for C_Hostname */
445  C_Hostname =
447  sprintf((char *) C_Hostname, "%s.%s", NONULL(ShortHostname), DOMAIN);
448 #else
449  C_Hostname = getmailname();
450  if (!C_Hostname)
451  {
452  char buffer[1024];
453  if (getdnsdomainname(buffer, sizeof(buffer)) == 0)
454  {
455  C_Hostname = mutt_mem_malloc(mutt_str_strlen(buffer) +
457  sprintf((char *) C_Hostname, "%s.%s", NONULL(ShortHostname), buffer);
458  }
459  else
460  {
461  /* DNS failed, use the nodename. Whether or not the nodename had a '.'
462  * in it, we can use the nodename as the FQDN. On hosts where DNS is
463  * not being used, e.g. small network that relies on hosts files, a
464  * short host name is all that is required for SMTP to work correctly.
465  * It could be wrong, but we've done the best we can, at this point the
466  * onus is on the user to provide the correct hostname if the nodename
467  * won't work in their network. */
468  C_Hostname = mutt_str_strdup(utsname.nodename);
469  }
470  }
471 #endif
472  }
473  if (C_Hostname)
474  cs_str_initial_set(Config, "hostname", C_Hostname, NULL);
475 
476  return true;
477 }
478 
483 static struct AttachMatch *mutt_attachmatch_new(void)
484 {
485  return mutt_mem_calloc(1, sizeof(struct AttachMatch));
486 }
487 
496 static enum CommandResult parse_attach_list(struct Buffer *buf, struct Buffer *s,
497  struct ListHead *head, struct Buffer *err)
498 {
499  struct AttachMatch *a = NULL;
500  char *p = NULL;
501  char *tmpminor = NULL;
502  size_t len;
503  int ret;
504 
505  do
506  {
508 
509  if (!buf->data || (*buf->data == '\0'))
510  continue;
511 
512  a = mutt_attachmatch_new();
513 
514  /* some cheap hacks that I expect to remove */
515  if (mutt_str_strcasecmp(buf->data, "any") == 0)
516  a->major = mutt_str_strdup("*/.*");
517  else if (mutt_str_strcasecmp(buf->data, "none") == 0)
518  a->major = mutt_str_strdup("cheap_hack/this_should_never_match");
519  else
520  a->major = mutt_str_strdup(buf->data);
521 
522  p = strchr(a->major, '/');
523  if (p)
524  {
525  *p = '\0';
526  p++;
527  a->minor = p;
528  }
529  else
530  {
531  a->minor = "unknown";
532  }
533 
534  len = strlen(a->minor);
535  tmpminor = mutt_mem_malloc(len + 3);
536  strcpy(&tmpminor[1], a->minor);
537  tmpminor[0] = '^';
538  tmpminor[len + 1] = '$';
539  tmpminor[len + 2] = '\0';
540 
542  ret = REG_COMP(&a->minor_regex, tmpminor, REG_ICASE);
543 
544  FREE(&tmpminor);
545 
546  if (ret != 0)
547  {
548  regerror(ret, &a->minor_regex, err->data, err->dsize);
549  FREE(&a->major);
550  FREE(&a);
551  return MUTT_CMD_ERROR;
552  }
553 
554  mutt_debug(LL_DEBUG3, "added %s/%s [%d]\n", a->major, a->minor, a->major_int);
555 
556  mutt_list_insert_tail(head, (char *) a);
557  } while (MoreArgs(s));
558 
560  return MUTT_CMD_SUCCESS;
561 }
562 
573 static int parse_grouplist(struct GroupList *gl, struct Buffer *buf,
574  struct Buffer *s, unsigned long data, struct Buffer *err)
575 {
576  while (mutt_str_strcasecmp(buf->data, "-group") == 0)
577  {
578  if (!MoreArgs(s))
579  {
580  mutt_buffer_strcpy(err, _("-group: no group name"));
581  return -1;
582  }
583 
585 
587 
588  if (!MoreArgs(s))
589  {
590  mutt_buffer_strcpy(err, _("out of arguments"));
591  return -1;
592  }
593 
595  }
596 
597  return 0;
598 }
599 
603 static enum CommandResult parse_replace_list(struct Buffer *buf, struct Buffer *s,
604  unsigned long data, struct Buffer *err)
605 {
606  struct ReplaceList *list = (struct ReplaceList *) data;
607  struct Buffer templ = mutt_buffer_make(0);
608 
609  /* First token is a regex. */
610  if (!MoreArgs(s))
611  {
612  mutt_buffer_printf(err, _("%s: too few arguments"), "subjectrx");
613  return MUTT_CMD_WARNING;
614  }
616 
617  /* Second token is a replacement template */
618  if (!MoreArgs(s))
619  {
620  mutt_buffer_printf(err, _("%s: too few arguments"), "subjectrx");
621  return MUTT_CMD_WARNING;
622  }
624 
625  if (mutt_replacelist_add(list, buf->data, templ.data, err) != 0)
626  {
627  FREE(&templ.data);
628  return MUTT_CMD_ERROR;
629  }
630  FREE(&templ.data);
631 
632  return MUTT_CMD_SUCCESS;
633 }
634 
643 static enum CommandResult parse_unattach_list(struct Buffer *buf, struct Buffer *s,
644  struct ListHead *head, struct Buffer *err)
645 {
646  struct AttachMatch *a = NULL;
647  char *tmp = NULL;
648  char *minor = NULL;
649 
650  do
651  {
653  FREE(&tmp);
654 
655  if (mutt_str_strcasecmp(buf->data, "any") == 0)
656  tmp = mutt_str_strdup("*/.*");
657  else if (mutt_str_strcasecmp(buf->data, "none") == 0)
658  tmp = mutt_str_strdup("cheap_hack/this_should_never_match");
659  else
660  tmp = mutt_str_strdup(buf->data);
661 
662  minor = strchr(tmp, '/');
663  if (minor)
664  {
665  *minor = '\0';
666  minor++;
667  }
668  else
669  {
670  minor = "unknown";
671  }
672  const enum ContentType major = mutt_check_mime_type(tmp);
673 
674  struct ListNode *np = NULL, *tmp2 = NULL;
675  STAILQ_FOREACH_SAFE(np, head, entries, tmp2)
676  {
677  a = (struct AttachMatch *) np->data;
678  mutt_debug(LL_DEBUG3, "check %s/%s [%d] : %s/%s [%d]\n", a->major,
679  a->minor, a->major_int, tmp, minor, major);
680  if ((a->major_int == major) && (mutt_str_strcasecmp(minor, a->minor) == 0))
681  {
682  mutt_debug(LL_DEBUG3, "removed %s/%s [%d]\n", a->major, a->minor, a->major_int);
683  regfree(&a->minor_regex);
684  FREE(&a->major);
685  STAILQ_REMOVE(head, np, ListNode, entries);
686  FREE(&np->data);
687  FREE(&np);
688  }
689  }
690 
691  } while (MoreArgs(s));
692 
693  FREE(&tmp);
695  return MUTT_CMD_SUCCESS;
696 }
697 
701 static enum CommandResult parse_unreplace_list(struct Buffer *buf, struct Buffer *s,
702  unsigned long data, struct Buffer *err)
703 {
704  struct ReplaceList *list = (struct ReplaceList *) data;
705 
706  /* First token is a regex. */
707  if (!MoreArgs(s))
708  {
709  mutt_buffer_printf(err, _("%s: too few arguments"), "unsubjectrx");
710  return MUTT_CMD_WARNING;
711  }
712 
714 
715  /* "*" is a special case. */
716  if (mutt_str_strcmp(buf->data, "*") == 0)
717  {
718  mutt_replacelist_free(list);
719  return MUTT_CMD_SUCCESS;
720  }
721 
722  mutt_replacelist_remove(list, buf->data);
723  return MUTT_CMD_SUCCESS;
724 }
725 
733 static int print_attach_list(struct ListHead *h, const char op, const char *name)
734 {
735  struct ListNode *np = NULL;
736  STAILQ_FOREACH(np, h, entries)
737  {
738  printf("attachments %c%s %s/%s\n", op, name,
739  ((struct AttachMatch *) np->data)->major,
740  ((struct AttachMatch *) np->data)->minor);
741  }
742 
743  return 0;
744 }
745 
753 static void remove_from_stailq(struct ListHead *head, const char *str)
754 {
755  if (mutt_str_strcmp("*", str) == 0)
756  mutt_list_free(head); /* "unCMD *" means delete all current entries */
757  else
758  {
759  struct ListNode *np = NULL, *tmp = NULL;
760  STAILQ_FOREACH_SAFE(np, head, entries, tmp)
761  {
762  if (mutt_str_strcasecmp(str, np->data) == 0)
763  {
764  STAILQ_REMOVE(head, np, ListNode, entries);
765  FREE(&np->data);
766  FREE(&np);
767  break;
768  }
769  }
770  }
771 }
772 
779 static int source_rc(const char *rcfile_path, struct Buffer *err)
780 {
781  int line = 0, rc = 0, warnings = 0;
782  enum CommandResult line_rc;
783  struct Buffer token;
784  char *linebuf = NULL;
785  char *currentline = NULL;
786  char rcfile[PATH_MAX];
787  size_t buflen;
788 
789  pid_t pid;
790 
791  mutt_str_strfcpy(rcfile, rcfile_path, sizeof(rcfile));
792 
793  size_t rcfilelen = mutt_str_strlen(rcfile);
794  if (rcfilelen == 0)
795  return -1;
796 
797  bool ispipe = rcfile[rcfilelen - 1] == '|';
798 
799  if (!ispipe)
800  {
801  struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
802  if (!mutt_path_to_absolute(rcfile, np ? NONULL(np->data) : ""))
803  {
804  mutt_error(_("Error: Can't build path of '%s'"), rcfile_path);
805  return -1;
806  }
807 
808  STAILQ_FOREACH(np, &MuttrcStack, entries)
809  {
810  if (mutt_str_strcmp(np->data, rcfile) == 0)
811  {
812  break;
813  }
814  }
815  if (np)
816  {
817  mutt_error(_("Error: Cyclic sourcing of configuration file '%s'"), rcfile);
818  return -1;
819  }
820 
822  }
823 
824  mutt_debug(LL_DEBUG2, "Reading configuration file '%s'\n", rcfile);
825 
826  FILE *fp = mutt_open_read(rcfile, &pid);
827  if (!fp)
828  {
829  mutt_buffer_printf(err, "%s: %s", rcfile, strerror(errno));
830  return -1;
831  }
832 
833  mutt_buffer_init(&token);
834  while ((linebuf = mutt_file_read_line(linebuf, &buflen, fp, &line, MUTT_CONT)))
835  {
836  const bool conv = C_ConfigCharset && C_Charset;
837  if (conv)
838  {
839  currentline = mutt_str_strdup(linebuf);
840  if (!currentline)
841  continue;
842  mutt_ch_convert_string(&currentline, C_ConfigCharset, C_Charset, 0);
843  }
844  else
845  currentline = linebuf;
846  mutt_buffer_reset(err);
847  line_rc = mutt_parse_rc_line(currentline, &token, err);
848  if (line_rc == MUTT_CMD_ERROR)
849  {
850  mutt_error(_("Error in %s, line %d: %s"), rcfile, line, err->data);
851  if (--rc < -MAX_ERRS)
852  {
853  if (conv)
854  FREE(&currentline);
855  break;
856  }
857  }
858  else if (line_rc == MUTT_CMD_WARNING)
859  {
860  /* Warning */
861  mutt_warning(_("Warning in %s, line %d: %s"), rcfile, line, err->data);
862  warnings++;
863  }
864  else if (line_rc == MUTT_CMD_FINISH)
865  {
866  break; /* Found "finish" command */
867  }
868  else
869  {
870  if (rc < 0)
871  rc = -1;
872  }
873  if (conv)
874  FREE(&currentline);
875  }
876  FREE(&token.data);
877  FREE(&linebuf);
878  mutt_file_fclose(&fp);
879  if (pid != -1)
880  mutt_wait_filter(pid);
881  if (rc)
882  {
883  /* the neomuttrc source keyword */
884  mutt_buffer_reset(err);
885  mutt_buffer_printf(err, (rc >= -MAX_ERRS) ? _("source: errors in %s") : _("source: reading aborted due to too many errors in %s"),
886  rcfile);
887  rc = -1;
888  }
889  else
890  {
891  /* Don't alias errors with warnings */
892  if (warnings > 0)
893  {
894  mutt_buffer_printf(err, ngettext("source: %d warning in %s", "source: %d warnings in %s", warnings),
895  warnings, rcfile);
896  rc = -2;
897  }
898  }
899 
900  if (!ispipe && !STAILQ_EMPTY(&MuttrcStack))
901  {
902  struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
903  STAILQ_REMOVE_HEAD(&MuttrcStack, entries);
904  FREE(&np->data);
905  FREE(&np);
906  }
907 
908  return rc;
909 }
910 
914 static enum CommandResult parse_alias(struct Buffer *buf, struct Buffer *s,
915  unsigned long data, struct Buffer *err)
916 {
917  struct Alias *tmp = NULL;
918  char *estr = NULL;
919  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
920 
921  if (!MoreArgs(s))
922  {
923  mutt_buffer_strcpy(err, _("alias: no address"));
924  return MUTT_CMD_WARNING;
925  }
926 
928 
929  if (parse_grouplist(&gl, buf, s, data, err) == -1)
930  return MUTT_CMD_ERROR;
931 
932  /* check to see if an alias with this name already exists */
933  TAILQ_FOREACH(tmp, &Aliases, entries)
934  {
935  if (mutt_str_strcasecmp(tmp->name, buf->data) == 0)
936  break;
937  }
938 
939  if (tmp)
940  {
942  /* override the previous value */
943  mutt_addrlist_clear(&tmp->addr);
944  if (CurrentMenu == MENU_ALIAS)
946  }
947  else
948  {
949  /* create a new alias */
950  tmp = mutt_alias_new();
951  tmp->name = mutt_str_strdup(buf->data);
952  TAILQ_INSERT_TAIL(&Aliases, tmp, entries);
953  /* give the main addressbook code a chance */
954  if (CurrentMenu == MENU_ALIAS)
955  OptMenuCaller = true;
956  }
957 
959  mutt_debug(LL_DEBUG5, "Second token is '%s'\n", buf->data);
960 
961  mutt_addrlist_parse2(&tmp->addr, buf->data);
962 
963  if (mutt_addrlist_to_intl(&tmp->addr, &estr))
964  {
965  mutt_buffer_printf(err, _("Warning: Bad IDN '%s' in alias '%s'"), estr, tmp->name);
966  FREE(&estr);
967  goto bail;
968  }
969 
970  mutt_grouplist_add_addrlist(&gl, &tmp->addr);
972 
973  if (C_DebugLevel > LL_DEBUG4)
974  {
975  /* A group is terminated with an empty address, so check a->mailbox */
976  struct Address *a = NULL;
977  TAILQ_FOREACH(a, &tmp->addr, entries)
978  {
979  if (!a->mailbox)
980  break;
981 
982  if (a->group)
983  mutt_debug(LL_DEBUG5, " Group %s\n", a->mailbox);
984  else
985  mutt_debug(LL_DEBUG5, " %s\n", a->mailbox);
986  }
987  }
989  return MUTT_CMD_SUCCESS;
990 
991 bail:
993  return MUTT_CMD_ERROR;
994 }
995 
999 static enum CommandResult parse_alternates(struct Buffer *buf, struct Buffer *s,
1000  unsigned long data, struct Buffer *err)
1001 {
1002  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
1003 
1004  alternates_clean();
1005 
1006  do
1007  {
1009 
1010  if (parse_grouplist(&gl, buf, s, data, err) == -1)
1011  goto bail;
1012 
1013  mutt_regexlist_remove(&UnAlternates, buf->data);
1014 
1015  if (mutt_regexlist_add(&Alternates, buf->data, REG_ICASE, err) != 0)
1016  goto bail;
1017 
1018  if (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0)
1019  goto bail;
1020  } while (MoreArgs(s));
1021 
1023  return MUTT_CMD_SUCCESS;
1024 
1025 bail:
1027  return MUTT_CMD_ERROR;
1028 }
1029 
1033 static enum CommandResult parse_attachments(struct Buffer *buf, struct Buffer *s,
1034  unsigned long data, struct Buffer *err)
1035 {
1036  char op;
1037  char *category = NULL;
1038  struct ListHead *head = NULL;
1039 
1041  if (!buf->data || (*buf->data == '\0'))
1042  {
1043  mutt_buffer_strcpy(err, _("attachments: no disposition"));
1044  return MUTT_CMD_WARNING;
1045  }
1046 
1047  category = buf->data;
1048  op = *category++;
1049 
1050  if (op == '?')
1051  {
1052  mutt_endwin();
1053  fflush(stdout);
1054  printf("\n%s\n\n", _("Current attachments settings:"));
1055  print_attach_list(&AttachAllow, '+', "A");
1056  print_attach_list(&AttachExclude, '-', "A");
1057  print_attach_list(&InlineAllow, '+', "I");
1058  print_attach_list(&InlineExclude, '-', "I");
1060  return MUTT_CMD_SUCCESS;
1061  }
1062 
1063  if ((op != '+') && (op != '-'))
1064  {
1065  op = '+';
1066  category--;
1067  }
1068  if (mutt_str_startswith("attachment", category, CASE_IGNORE))
1069  {
1070  if (op == '+')
1071  head = &AttachAllow;
1072  else
1073  head = &AttachExclude;
1074  }
1075  else if (mutt_str_startswith("inline", category, CASE_IGNORE))
1076  {
1077  if (op == '+')
1078  head = &InlineAllow;
1079  else
1080  head = &InlineExclude;
1081  }
1082  else
1083  {
1084  mutt_buffer_strcpy(err, _("attachments: invalid disposition"));
1085  return MUTT_CMD_ERROR;
1086  }
1087 
1088  return parse_attach_list(buf, s, head, err);
1089 }
1090 
1094 static enum CommandResult parse_echo(struct Buffer *buf, struct Buffer *s,
1095  unsigned long data, struct Buffer *err)
1096 {
1097  if (!MoreArgs(s))
1098  {
1099  mutt_buffer_printf(err, _("%s: too few arguments"), "echo");
1100  return MUTT_CMD_WARNING;
1101  }
1103  OptForceRefresh = true;
1104  mutt_message("%s", buf->data);
1105  OptForceRefresh = false;
1106  mutt_sleep(0);
1107 
1108  return MUTT_CMD_SUCCESS;
1109 }
1110 
1118 static enum CommandResult parse_finish(struct Buffer *buf, struct Buffer *s,
1119  unsigned long data, struct Buffer *err)
1120 {
1121  if (MoreArgs(s))
1122  {
1123  mutt_buffer_printf(err, _("%s: too many arguments"), "finish");
1124  return MUTT_CMD_WARNING;
1125  }
1126 
1127  return MUTT_CMD_FINISH;
1128 }
1129 
1133 static enum CommandResult parse_group(struct Buffer *buf, struct Buffer *s,
1134  unsigned long data, struct Buffer *err)
1135 {
1136  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
1137  enum GroupState state = GS_NONE;
1138 
1139  do
1140  {
1142  if (parse_grouplist(&gl, buf, s, data, err) == -1)
1143  goto bail;
1144 
1145  if ((data == MUTT_UNGROUP) && (mutt_str_strcasecmp(buf->data, "*") == 0))
1146  {
1147  mutt_grouplist_clear(&gl);
1148  goto out;
1149  }
1150 
1151  if (mutt_str_strcasecmp(buf->data, "-rx") == 0)
1152  state = GS_RX;
1153  else if (mutt_str_strcasecmp(buf->data, "-addr") == 0)
1154  state = GS_ADDR;
1155  else
1156  {
1157  switch (state)
1158  {
1159  case GS_NONE:
1160  mutt_buffer_printf(err, _("%sgroup: missing -rx or -addr"),
1161  (data == MUTT_UNGROUP) ? "un" : "");
1162  goto warn;
1163 
1164  case GS_RX:
1165  if ((data == MUTT_GROUP) &&
1166  (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0))
1167  {
1168  goto bail;
1169  }
1170  else if ((data == MUTT_UNGROUP) &&
1171  (mutt_grouplist_remove_regex(&gl, buf->data) < 0))
1172  {
1173  goto bail;
1174  }
1175  break;
1176 
1177  case GS_ADDR:
1178  {
1179  char *estr = NULL;
1180  struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
1181  mutt_addrlist_parse2(&al, buf->data);
1182  if (TAILQ_EMPTY(&al))
1183  goto bail;
1184  if (mutt_addrlist_to_intl(&al, &estr))
1185  {
1186  mutt_buffer_printf(err, _("%sgroup: warning: bad IDN '%s'"),
1187  (data == 1) ? "un" : "", estr);
1188  mutt_addrlist_clear(&al);
1189  FREE(&estr);
1190  goto bail;
1191  }
1192  if (data == MUTT_GROUP)
1193  mutt_grouplist_add_addrlist(&gl, &al);
1194  else if (data == MUTT_UNGROUP)
1196  mutt_addrlist_clear(&al);
1197  break;
1198  }
1199  }
1200  }
1201  } while (MoreArgs(s));
1202 
1203 out:
1205  return MUTT_CMD_SUCCESS;
1206 
1207 bail:
1209  return MUTT_CMD_ERROR;
1210 
1211 warn:
1213  return MUTT_CMD_WARNING;
1214 }
1215 
1222 static bool is_function(const char *name)
1223 {
1224  for (enum MenuType i = 0; i < MENU_MAX; i++)
1225  {
1226  const struct Binding *b = km_get_table(Menus[i].value);
1227  if (!b)
1228  continue;
1229 
1230  for (int j = 0; b[j].name; j++)
1231  if (mutt_str_strcmp(name, b[j].name) == 0)
1232  return true;
1233  }
1234  return false;
1235 }
1236 
1250 static enum CommandResult parse_ifdef(struct Buffer *buf, struct Buffer *s,
1251  unsigned long data, struct Buffer *err)
1252 {
1253  struct Buffer token = mutt_buffer_make(0);
1254 
1256 
1257  // is the item defined as:
1258  bool res = cs_get_elem(Config, buf->data) // a variable?
1259  || feature_enabled(buf->data) // a compiled-in feature?
1260  || is_function(buf->data) // a function?
1261  || mutt_command_get(buf->data) // a command?
1262  || myvar_get(buf->data) // a my_ variable?
1263  || mutt_str_getenv(buf->data); // an environment variable?
1264 
1265  if (!MoreArgs(s))
1266  {
1267  mutt_buffer_printf(err, _("%s: too few arguments"), (data ? "ifndef" : "ifdef"));
1268  return MUTT_CMD_WARNING;
1269  }
1271 
1272  /* ifdef KNOWN_SYMBOL or ifndef UNKNOWN_SYMBOL */
1273  if ((res && (data == 0)) || (!res && (data == 1)))
1274  {
1275  enum CommandResult rc = mutt_parse_rc_line(buf->data, &token, err);
1276  if (rc == MUTT_CMD_ERROR)
1277  {
1278  mutt_error(_("Error: %s"), err->data);
1279  FREE(&token.data);
1280  return MUTT_CMD_ERROR;
1281  }
1282  FREE(&token.data);
1283  return rc;
1284  }
1285  return MUTT_CMD_SUCCESS;
1286 }
1287 
1291 static enum CommandResult parse_ignore(struct Buffer *buf, struct Buffer *s,
1292  unsigned long data, struct Buffer *err)
1293 {
1294  do
1295  {
1298  add_to_stailq(&Ignore, buf->data);
1299  } while (MoreArgs(s));
1300 
1301  return MUTT_CMD_SUCCESS;
1302 }
1303 
1307 static enum CommandResult parse_lists(struct Buffer *buf, struct Buffer *s,
1308  unsigned long data, struct Buffer *err)
1309 {
1310  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
1311 
1312  do
1313  {
1315 
1316  if (parse_grouplist(&gl, buf, s, data, err) == -1)
1317  goto bail;
1318 
1320 
1321  if (mutt_regexlist_add(&MailLists, buf->data, REG_ICASE, err) != 0)
1322  goto bail;
1323 
1324  if (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0)
1325  goto bail;
1326  } while (MoreArgs(s));
1327 
1329  return MUTT_CMD_SUCCESS;
1330 
1331 bail:
1333  return MUTT_CMD_ERROR;
1334 }
1335 
1341 static enum CommandResult parse_mailboxes(struct Buffer *buf, struct Buffer *s,
1342  unsigned long data, struct Buffer *err)
1343 {
1344  while (MoreArgs(s))
1345  {
1346  struct Mailbox *m = mailbox_new();
1347 
1348  if (data & MUTT_NAMED)
1349  {
1351  if (buf->data && (*buf->data != '\0'))
1352  {
1353  m->name = mutt_str_strdup(buf->data);
1354  }
1355  else
1356  {
1357  mailbox_free(&m);
1358  continue;
1359  }
1360  }
1361 
1363  if (mutt_buffer_is_empty(buf))
1364  {
1365  /* Skip empty tokens. */
1366  mailbox_free(&m);
1367  continue;
1368  }
1369 
1370  mutt_buffer_strcpy(&m->pathbuf, buf->data);
1371  /* int rc = */ mx_path_canon2(m, C_Folder);
1372 
1373  if (m->magic <= MUTT_UNKNOWN)
1374  {
1375  mutt_error("Unknown Mailbox: %s", m->realpath);
1376  mailbox_free(&m);
1377  return MUTT_CMD_ERROR;
1378  }
1379 
1380  bool new_account = false;
1381  struct Account *a = mx_ac_find(m);
1382  if (!a)
1383  {
1384  a = account_new(NULL, NeoMutt->sub);
1385  a->magic = m->magic;
1386  new_account = true;
1387  }
1388 
1389  if (!new_account)
1390  {
1391  struct Mailbox *m_old = mx_mbox_find(a, m->realpath);
1392  if (m_old)
1393  {
1394  if (m_old->flags == MB_HIDDEN)
1395  {
1396  m_old->flags = MB_NORMAL;
1397  mutt_sb_notify_mailbox(m_old, true);
1398  }
1399  mailbox_free(&m);
1400  continue;
1401  }
1402  }
1403 
1404  if (mx_ac_add(a, m) < 0)
1405  {
1406  //error
1407  mailbox_free(&m);
1408  if (new_account)
1409  {
1410  cs_subset_free(&a->sub);
1411  FREE(&a->name);
1412  notify_free(&a->notify);
1413  FREE(&a);
1414  }
1415  continue;
1416  }
1417  if (new_account)
1418  {
1420  }
1421 
1422 #ifdef USE_SIDEBAR
1423  mutt_sb_notify_mailbox(m, true);
1424 #endif
1425 #ifdef USE_INOTIFY
1426  mutt_monitor_add(m);
1427 #endif
1428  }
1429  return MUTT_CMD_SUCCESS;
1430 }
1431 
1435 static enum CommandResult parse_my_hdr(struct Buffer *buf, struct Buffer *s,
1436  unsigned long data, struct Buffer *err)
1437 {
1438  struct ListNode *n = NULL;
1439  size_t keylen;
1440 
1442  char *p = strpbrk(buf->data, ": \t");
1443  if (!p || (*p != ':'))
1444  {
1445  mutt_buffer_strcpy(err, _("invalid header field"));
1446  return MUTT_CMD_WARNING;
1447  }
1448  keylen = p - buf->data + 1;
1449 
1450  STAILQ_FOREACH(n, &UserHeader, entries)
1451  {
1452  /* see if there is already a field by this name */
1453  if (mutt_str_strncasecmp(buf->data, n->data, keylen) == 0)
1454  {
1455  break;
1456  }
1457  }
1458 
1459  if (!n)
1460  {
1461  /* not found, allocate memory for a new node and add it to the list */
1462  n = mutt_list_insert_tail(&UserHeader, NULL);
1463  }
1464  else
1465  {
1466  /* found, free the existing data */
1467  FREE(&n->data);
1468  }
1469 
1470  n->data = buf->data;
1471  mutt_buffer_init(buf);
1472 
1473  return MUTT_CMD_SUCCESS;
1474 }
1475 
1476 #ifdef USE_SIDEBAR
1477 
1480 static enum CommandResult parse_path_list(struct Buffer *buf, struct Buffer *s,
1481  unsigned long data, struct Buffer *err)
1482 {
1483  struct Buffer *path = mutt_buffer_pool_get();
1484 
1485  do
1486  {
1489  add_to_stailq((struct ListHead *) data, mutt_b2s(path));
1490  } while (MoreArgs(s));
1491  mutt_buffer_pool_release(&path);
1492 
1493  return MUTT_CMD_SUCCESS;
1494 }
1495 #endif
1496 
1497 #ifdef USE_SIDEBAR
1498 
1501 static enum CommandResult parse_path_unlist(struct Buffer *buf, struct Buffer *s,
1502  unsigned long data, struct Buffer *err)
1503 {
1504  struct Buffer *path = mutt_buffer_pool_get();
1505 
1506  do
1507  {
1509  /* Check for deletion of entire list */
1510  if (mutt_str_strcmp(mutt_b2s(path), "*") == 0)
1511  {
1512  mutt_list_free((struct ListHead *) data);
1513  break;
1514  }
1516  remove_from_stailq((struct ListHead *) data, mutt_b2s(path));
1517  } while (MoreArgs(s));
1518  mutt_buffer_pool_release(&path);
1519 
1520  return MUTT_CMD_SUCCESS;
1521 }
1522 #endif
1523 
1529 static enum CommandResult parse_set(struct Buffer *buf, struct Buffer *s,
1530  unsigned long data, struct Buffer *err)
1531 {
1532  /* The order must match `enum MuttSetCommand` */
1533  static const char *set_commands[] = { "set", "toggle", "unset", "reset" };
1534 
1535  int rc = 0;
1536 
1537  while (MoreArgs(s))
1538  {
1539  bool prefix = false;
1540  bool query = false;
1541  bool inv = (data == MUTT_SET_INV);
1542  bool reset = (data == MUTT_SET_RESET);
1543  bool unset = (data == MUTT_SET_UNSET);
1544 
1545  if (*s->dptr == '?')
1546  {
1547  prefix = true;
1548  query = true;
1549  s->dptr++;
1550  }
1551  else if (mutt_str_startswith(s->dptr, "no", CASE_MATCH))
1552  {
1553  prefix = true;
1554  unset = !unset;
1555  s->dptr += 2;
1556  }
1557  else if (mutt_str_startswith(s->dptr, "inv", CASE_MATCH))
1558  {
1559  prefix = true;
1560  inv = !inv;
1561  s->dptr += 3;
1562  }
1563  else if (*s->dptr == '&')
1564  {
1565  prefix = true;
1566  reset = true;
1567  s->dptr++;
1568  }
1569 
1570  if (prefix && (data != MUTT_SET_SET))
1571  {
1572  mutt_buffer_printf(err, "ERR22 can't use 'inv', 'no', '&' or '?' with the '%s' command",
1573  set_commands[data]);
1574  return MUTT_CMD_WARNING;
1575  }
1576 
1577  /* get the variable name */
1579 
1580  bool bq = false;
1581  bool equals = false;
1582 
1583  struct HashElem *he = NULL;
1584  bool my = mutt_str_startswith(buf->data, "my_", CASE_MATCH);
1585  if (!my)
1586  {
1587  he = cs_get_elem(Config, buf->data);
1588  if (!he)
1589  {
1590  if (reset && (mutt_str_strcmp(buf->data, "all") == 0))
1591  {
1592  struct HashElem **list = get_elem_list(Config);
1593  if (!list)
1594  return MUTT_CMD_ERROR;
1595 
1596  for (size_t i = 0; list[i]; i++)
1597  cs_he_reset(Config, list[i], NULL);
1598 
1599  FREE(&list);
1600  break;
1601  }
1602  else
1603  {
1604  mutt_buffer_printf(err, "ERR01 unknown variable: %s", buf->data);
1605  return MUTT_CMD_ERROR;
1606  }
1607  }
1608 
1609  bq = ((DTYPE(he->type) == DT_BOOL) || (DTYPE(he->type) == DT_QUAD));
1610  }
1611 
1612  if (*s->dptr == '?')
1613  {
1614  if (prefix)
1615  {
1616  mutt_buffer_printf(err,
1617  "ERR02 can't use a prefix when querying a variable");
1618  return MUTT_CMD_WARNING;
1619  }
1620 
1621  if (reset || unset || inv)
1622  {
1623  mutt_buffer_printf(err, "ERR03 can't query a variable with the '%s' command",
1624  set_commands[data]);
1625  return MUTT_CMD_WARNING;
1626  }
1627 
1628  query = true;
1629  s->dptr++;
1630  }
1631  else if (*s->dptr == '=')
1632  {
1633  if (prefix)
1634  {
1635  mutt_buffer_printf(err,
1636  "ERR04 can't use prefix when setting a variable");
1637  return MUTT_CMD_WARNING;
1638  }
1639 
1640  if (reset || unset || inv)
1641  {
1642  mutt_buffer_printf(err, "ERR05 can't set a variable with the '%s' command",
1643  set_commands[data]);
1644  return MUTT_CMD_WARNING;
1645  }
1646 
1647  equals = true;
1648  s->dptr++;
1649  }
1650 
1651  if (!bq && (inv || (unset && prefix)))
1652  {
1653  if (data == MUTT_SET_SET)
1654  {
1655  mutt_buffer_printf(err, "ERR06 prefixes 'no' and 'inv' may only be "
1656  "used with bool/quad variables");
1657  }
1658  else
1659  {
1660  mutt_buffer_printf(err, "ERR07 command '%s' can only be used with bool/quad variables",
1661  set_commands[data]);
1662  }
1663  return MUTT_CMD_WARNING;
1664  }
1665 
1666  if (reset)
1667  {
1668  // mutt_buffer_printf(err, "ACT24 reset variable %s", buf->data);
1669  if (he)
1670  {
1671  rc = cs_he_reset(Config, he, err);
1672  if (CSR_RESULT(rc) != CSR_SUCCESS)
1673  return MUTT_CMD_ERROR;
1674  }
1675  else
1676  {
1677  myvar_del(buf->data);
1678  }
1679  continue;
1680  }
1681 
1682  if ((data == MUTT_SET_SET) && !inv && !unset)
1683  {
1684  if (query)
1685  {
1686  // mutt_buffer_printf(err, "ACT08 query variable %s", buf->data);
1687  if (he)
1688  {
1689  mutt_buffer_addstr(err, buf->data);
1690  mutt_buffer_addch(err, '=');
1691  mutt_buffer_reset(buf);
1692  rc = cs_he_string_get(Config, he, buf);
1693  if (CSR_RESULT(rc) != CSR_SUCCESS)
1694  {
1695  mutt_buffer_addstr(err, buf->data);
1696  return MUTT_CMD_ERROR;
1697  }
1698  pretty_var(buf->data, err);
1699  }
1700  else
1701  {
1702  const char *val = myvar_get(buf->data);
1703  if (val)
1704  {
1705  mutt_buffer_addstr(err, buf->data);
1706  mutt_buffer_addch(err, '=');
1707  pretty_var(val, err);
1708  }
1709  else
1710  {
1711  mutt_buffer_printf(err, _("%s: unknown variable"), buf->data);
1712  return MUTT_CMD_ERROR;
1713  }
1714  }
1715  break;
1716  }
1717  else if (equals)
1718  {
1719  // mutt_buffer_printf(err, "ACT11 set variable %s to ", buf->data);
1720  const char *name = NULL;
1721  if (my)
1722  {
1723  name = mutt_str_strdup(buf->data);
1724  }
1726  if (my)
1727  {
1728  myvar_set(name, buf->data);
1729  FREE(&name);
1730  }
1731  else
1732  {
1733  if (IS_PATH(he))
1734  {
1736  struct Buffer scratch = mutt_buffer_make(1024);
1737  mutt_buffer_copy(&scratch, buf);
1738  size_t scratchlen = mutt_buffer_len(&scratch);
1739  if (!(he->type & DT_MAILBOX) && (scratchlen != 0))
1740  {
1741  if ((mutt_b2s(&scratch)[scratchlen - 1] != '|') && /* not a command */
1742  (url_check_scheme(mutt_b2s(&scratch)) == U_UNKNOWN)) /* probably a local file */
1743  {
1744  struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
1745  if (mutt_path_to_absolute(scratch.data, np ? NONULL(np->data) : "./"))
1746  {
1747  mutt_buffer_reset(buf);
1748  mutt_buffer_addstr(buf, mutt_b2s(&scratch));
1749  }
1750  else
1751  {
1752  mutt_error(_("Error: Can't build path of '%s'"), mutt_b2s(&scratch));
1753  }
1754  }
1755  }
1756  mutt_buffer_dealloc(&scratch);
1757  }
1758  else if (IS_COMMAND(he))
1759  {
1760  struct Buffer scratch = mutt_buffer_make(1024);
1761  mutt_buffer_copy(&scratch, buf);
1762 
1763  if (mutt_str_strcmp(buf->data, "builtin") != 0)
1764  {
1765  mutt_buffer_expand_path(&scratch);
1766  }
1767  mutt_buffer_reset(buf);
1768  mutt_buffer_addstr(buf, mutt_b2s(&scratch));
1769  mutt_buffer_dealloc(&scratch);
1770  }
1771 
1772  rc = cs_he_string_set(Config, he, buf->data, err);
1773  if (CSR_RESULT(rc) != CSR_SUCCESS)
1774  return MUTT_CMD_ERROR;
1775  }
1776  continue;
1777  }
1778  else
1779  {
1780  if (bq)
1781  {
1782  // mutt_buffer_printf(err, "ACT23 set variable %s to 'yes'", buf->data);
1783  rc = cs_he_native_set(Config, he, true, err);
1784  if (CSR_RESULT(rc) != CSR_SUCCESS)
1785  return MUTT_CMD_ERROR;
1786  continue;
1787  }
1788  else
1789  {
1790  // mutt_buffer_printf(err, "ACT10 query variable %s", buf->data);
1791  if (he)
1792  {
1793  mutt_buffer_addstr(err, buf->data);
1794  mutt_buffer_addch(err, '=');
1795  mutt_buffer_reset(buf);
1796  rc = cs_he_string_get(Config, he, buf);
1797  if (CSR_RESULT(rc) != CSR_SUCCESS)
1798  {
1799  mutt_buffer_addstr(err, buf->data);
1800  return MUTT_CMD_ERROR;
1801  }
1802  pretty_var(buf->data, err);
1803  }
1804  else
1805  {
1806  const char *val = myvar_get(buf->data);
1807  if (val)
1808  {
1809  mutt_buffer_addstr(err, buf->data);
1810  mutt_buffer_addch(err, '=');
1811  pretty_var(val, err);
1812  }
1813  else
1814  {
1815  mutt_buffer_printf(err, _("%s: unknown variable"), buf->data);
1816  return MUTT_CMD_ERROR;
1817  }
1818  }
1819  break;
1820  }
1821  }
1822  }
1823 
1824  if (my)
1825  {
1826  myvar_del(buf->data);
1827  }
1828  else if (bq)
1829  {
1830  if (inv)
1831  {
1832  // mutt_buffer_printf(err, "ACT25 TOGGLE bool/quad variable %s", buf->data);
1833  if (DTYPE(he->type) == DT_BOOL)
1834  bool_he_toggle(Config, he, err);
1835  else
1836  quad_he_toggle(Config, he, err);
1837  }
1838  else
1839  {
1840  // mutt_buffer_printf(err, "ACT26 UNSET bool/quad variable %s", buf->data);
1841  rc = cs_he_native_set(Config, he, false, err);
1842  if (CSR_RESULT(rc) != CSR_SUCCESS)
1843  return MUTT_CMD_ERROR;
1844  }
1845  continue;
1846  }
1847  else
1848  {
1849  rc = cs_he_string_set(Config, he, NULL, err);
1850  if (CSR_RESULT(rc) != CSR_SUCCESS)
1851  return MUTT_CMD_ERROR;
1852  }
1853  }
1854 
1855  return MUTT_CMD_SUCCESS;
1856 }
1857 
1861 static enum CommandResult parse_setenv(struct Buffer *buf, struct Buffer *s,
1862  unsigned long data, struct Buffer *err)
1863 {
1864  char **envp = mutt_envlist_getlist();
1865 
1866  bool query = false;
1867  bool unset = (data == MUTT_SET_UNSET);
1868 
1869  if (!MoreArgs(s))
1870  {
1871  mutt_buffer_printf(err, _("%s: too few arguments"), "setenv");
1872  return MUTT_CMD_WARNING;
1873  }
1874 
1875  if (*s->dptr == '?')
1876  {
1877  query = true;
1878  s->dptr++;
1879  }
1880 
1881  /* get variable name */
1883 
1884  if (query)
1885  {
1886  bool found = false;
1887  while (envp && *envp)
1888  {
1889  /* This will display all matches for "^QUERY" */
1890  if (mutt_str_startswith(*envp, buf->data, CASE_MATCH))
1891  {
1892  if (!found)
1893  {
1894  mutt_endwin();
1895  found = true;
1896  }
1897  puts(*envp);
1898  }
1899  envp++;
1900  }
1901 
1902  if (found)
1903  {
1905  return MUTT_CMD_SUCCESS;
1906  }
1907 
1908  mutt_buffer_printf(err, _("%s is unset"), buf->data);
1909  return MUTT_CMD_WARNING;
1910  }
1911 
1912  if (unset)
1913  {
1914  if (mutt_envlist_unset(buf->data))
1915  return MUTT_CMD_SUCCESS;
1916  return MUTT_CMD_ERROR;
1917  }
1918 
1919  /* set variable */
1920 
1921  if (*s->dptr == '=')
1922  {
1923  s->dptr++;
1924  SKIPWS(s->dptr);
1925  }
1926 
1927  if (!MoreArgs(s))
1928  {
1929  mutt_buffer_printf(err, _("%s: too few arguments"), "setenv");
1930  return MUTT_CMD_WARNING;
1931  }
1932 
1933  char *name = mutt_str_strdup(buf->data);
1935  mutt_envlist_set(name, buf->data, true);
1936  FREE(&name);
1937 
1938  return MUTT_CMD_SUCCESS;
1939 }
1940 
1944 static enum CommandResult parse_source(struct Buffer *buf, struct Buffer *s,
1945  unsigned long data, struct Buffer *err)
1946 {
1947  char path[PATH_MAX];
1948 
1949  do
1950  {
1951  if (mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS) != 0)
1952  {
1953  mutt_buffer_printf(err, _("source: error at %s"), s->dptr);
1954  return MUTT_CMD_ERROR;
1955  }
1956  mutt_str_strfcpy(path, buf->data, sizeof(path));
1957  mutt_expand_path(path, sizeof(path));
1958 
1959  if (source_rc(path, err) < 0)
1960  {
1961  mutt_buffer_printf(err, _("source: file %s could not be sourced"), path);
1962  return MUTT_CMD_ERROR;
1963  }
1964 
1965  } while (MoreArgs(s));
1966 
1967  return MUTT_CMD_SUCCESS;
1968 }
1969 
1973 static enum CommandResult parse_spam_list(struct Buffer *buf, struct Buffer *s,
1974  unsigned long data, struct Buffer *err)
1975 {
1976  struct Buffer templ;
1977 
1978  mutt_buffer_init(&templ);
1979 
1980  /* Insist on at least one parameter */
1981  if (!MoreArgs(s))
1982  {
1983  if (data == MUTT_SPAM)
1984  mutt_buffer_strcpy(err, _("spam: no matching pattern"));
1985  else
1986  mutt_buffer_strcpy(err, _("nospam: no matching pattern"));
1987  return MUTT_CMD_ERROR;
1988  }
1989 
1990  /* Extract the first token, a regex */
1992 
1993  /* data should be either MUTT_SPAM or MUTT_NOSPAM. MUTT_SPAM is for spam commands. */
1994  if (data == MUTT_SPAM)
1995  {
1996  /* If there's a second parameter, it's a template for the spam tag. */
1997  if (MoreArgs(s))
1998  {
2000 
2001  /* Add to the spam list. */
2002  if (mutt_replacelist_add(&SpamList, buf->data, templ.data, err) != 0)
2003  {
2004  FREE(&templ.data);
2005  return MUTT_CMD_ERROR;
2006  }
2007  FREE(&templ.data);
2008  }
2009  /* If not, try to remove from the nospam list. */
2010  else
2011  {
2013  }
2014 
2015  return MUTT_CMD_SUCCESS;
2016  }
2017  /* MUTT_NOSPAM is for nospam commands. */
2018  else if (data == MUTT_NOSPAM)
2019  {
2020  /* nospam only ever has one parameter. */
2021 
2022  /* "*" is a special case. */
2023  if (mutt_str_strcmp(buf->data, "*") == 0)
2024  {
2027  return MUTT_CMD_SUCCESS;
2028  }
2029 
2030  /* If it's on the spam list, just remove it. */
2031  if (mutt_replacelist_remove(&SpamList, buf->data) != 0)
2032  return MUTT_CMD_SUCCESS;
2033 
2034  /* Otherwise, add it to the nospam list. */
2035  if (mutt_regexlist_add(&NoSpamList, buf->data, REG_ICASE, err) != 0)
2036  return MUTT_CMD_ERROR;
2037 
2038  return MUTT_CMD_SUCCESS;
2039  }
2040 
2041  /* This should not happen. */
2042  mutt_buffer_strcpy(err, "This is no good at all.");
2043  return MUTT_CMD_ERROR;
2044 }
2045 
2051 static enum CommandResult parse_stailq(struct Buffer *buf, struct Buffer *s,
2052  unsigned long data, struct Buffer *err)
2053 {
2054  do
2055  {
2057  add_to_stailq((struct ListHead *) data, buf->data);
2058  } while (MoreArgs(s));
2059 
2060  return MUTT_CMD_SUCCESS;
2061 }
2062 
2066 static enum CommandResult parse_subjectrx_list(struct Buffer *buf, struct Buffer *s,
2067  unsigned long data, struct Buffer *err)
2068 {
2069  enum CommandResult rc;
2070 
2071  rc = parse_replace_list(buf, s, data, err);
2072  if (rc == MUTT_CMD_SUCCESS)
2074  return rc;
2075 }
2076 
2080 static enum CommandResult parse_subscribe(struct Buffer *buf, struct Buffer *s,
2081  unsigned long data, struct Buffer *err)
2082 {
2083  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
2084 
2085  do
2086  {
2088 
2089  if (parse_grouplist(&gl, buf, s, data, err) == -1)
2090  goto bail;
2091 
2094 
2095  if (mutt_regexlist_add(&MailLists, buf->data, REG_ICASE, err) != 0)
2096  goto bail;
2097  if (mutt_regexlist_add(&SubscribedLists, buf->data, REG_ICASE, err) != 0)
2098  goto bail;
2099  if (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0)
2100  goto bail;
2101  } while (MoreArgs(s));
2102 
2104  return MUTT_CMD_SUCCESS;
2105 
2106 bail:
2108  return MUTT_CMD_ERROR;
2109 }
2110 
2111 #ifdef USE_IMAP
2112 
2119 static enum CommandResult parse_subscribe_to(struct Buffer *buf, struct Buffer *s,
2120  unsigned long data, struct Buffer *err)
2121 {
2122  if (!buf || !s || !err)
2123  return MUTT_CMD_ERROR;
2124 
2125  mutt_buffer_reset(err);
2126 
2127  if (MoreArgs(s))
2128  {
2130 
2131  if (MoreArgs(s))
2132  {
2133  mutt_buffer_printf(err, _("%s: too many arguments"), "subscribe-to");
2134  return MUTT_CMD_WARNING;
2135  }
2136 
2137  if (buf->data && (*buf->data != '\0'))
2138  {
2139  /* Expand and subscribe */
2140  if (imap_subscribe(mutt_expand_path(buf->data, buf->dsize), true) == 0)
2141  {
2142  mutt_message(_("Subscribed to %s"), buf->data);
2143  return MUTT_CMD_SUCCESS;
2144  }
2145 
2146  mutt_buffer_printf(err, _("Could not subscribe to %s"), buf->data);
2147  return MUTT_CMD_ERROR;
2148  }
2149 
2150  mutt_debug(LL_DEBUG1, "Corrupted buffer");
2151  return MUTT_CMD_ERROR;
2152  }
2153 
2154  mutt_buffer_addstr(err, _("No folder specified"));
2155  return MUTT_CMD_WARNING;
2156 }
2157 #endif
2158 
2162 static enum CommandResult parse_tag_formats(struct Buffer *buf, struct Buffer *s,
2163  unsigned long data, struct Buffer *err)
2164 {
2165  if (!buf || !s)
2166  return MUTT_CMD_ERROR;
2167 
2168  char *tmp = NULL;
2169 
2170  while (MoreArgs(s))
2171  {
2172  char *tag = NULL, *format = NULL;
2173 
2175  if (buf->data && (*buf->data != '\0'))
2176  tag = mutt_str_strdup(buf->data);
2177  else
2178  continue;
2179 
2181  format = mutt_str_strdup(buf->data);
2182 
2183  /* avoid duplicates */
2184  tmp = mutt_hash_find(TagFormats, format);
2185  if (tmp)
2186  {
2187  mutt_debug(LL_DEBUG3, "tag format '%s' already registered as '%s'\n", format, tmp);
2188  FREE(&tag);
2189  FREE(&format);
2190  continue;
2191  }
2192 
2193  mutt_hash_insert(TagFormats, format, tag);
2194  }
2195  return MUTT_CMD_SUCCESS;
2196 }
2197 
2201 static enum CommandResult parse_tag_transforms(struct Buffer *buf, struct Buffer *s,
2202  unsigned long data, struct Buffer *err)
2203 {
2204  if (!buf || !s)
2205  return MUTT_CMD_ERROR;
2206 
2207  char *tmp = NULL;
2208 
2209  while (MoreArgs(s))
2210  {
2211  char *tag = NULL, *transform = NULL;
2212 
2214  if (buf->data && (*buf->data != '\0'))
2215  tag = mutt_str_strdup(buf->data);
2216  else
2217  continue;
2218 
2220  transform = mutt_str_strdup(buf->data);
2221 
2222  /* avoid duplicates */
2223  tmp = mutt_hash_find(TagTransforms, tag);
2224  if (tmp)
2225  {
2226  mutt_debug(LL_DEBUG3, "tag transform '%s' already registered as '%s'\n", tag, tmp);
2227  FREE(&tag);
2228  FREE(&transform);
2229  continue;
2230  }
2231 
2232  mutt_hash_insert(TagTransforms, tag, transform);
2233  }
2234  return MUTT_CMD_SUCCESS;
2235 }
2236 
2240 static enum CommandResult parse_unalias(struct Buffer *buf, struct Buffer *s,
2241  unsigned long data, struct Buffer *err)
2242 {
2243  struct Alias *a = NULL;
2244 
2245  do
2246  {
2248 
2249  if (mutt_str_strcmp("*", buf->data) == 0)
2250  {
2251  if (CurrentMenu == MENU_ALIAS)
2252  {
2253  TAILQ_FOREACH(a, &Aliases, entries)
2254  {
2255  a->del = true;
2256  }
2258  }
2259  else
2260  mutt_aliaslist_free(&Aliases);
2261  break;
2262  }
2263  else
2264  {
2265  TAILQ_FOREACH(a, &Aliases, entries)
2266  {
2267  if (mutt_str_strcasecmp(buf->data, a->name) == 0)
2268  {
2269  if (CurrentMenu == MENU_ALIAS)
2270  {
2271  a->del = true;
2273  }
2274  else
2275  {
2276  TAILQ_REMOVE(&Aliases, a, entries);
2277  mutt_alias_free(&a);
2278  }
2279  break;
2280  }
2281  }
2282  }
2283  } while (MoreArgs(s));
2284  return MUTT_CMD_SUCCESS;
2285 }
2286 
2290 static enum CommandResult parse_unalternates(struct Buffer *buf, struct Buffer *s,
2291  unsigned long data, struct Buffer *err)
2292 {
2293  alternates_clean();
2294  do
2295  {
2297  mutt_regexlist_remove(&Alternates, buf->data);
2298 
2299  if ((mutt_str_strcmp(buf->data, "*") != 0) &&
2300  (mutt_regexlist_add(&UnAlternates, buf->data, REG_ICASE, err) != 0))
2301  {
2302  return MUTT_CMD_ERROR;
2303  }
2304 
2305  } while (MoreArgs(s));
2306 
2307  return MUTT_CMD_SUCCESS;
2308 }
2309 
2317 static void mutt_attachmatch_free(struct AttachMatch **ptr)
2318 {
2319  if (!ptr || !*ptr)
2320  return;
2321 
2322  struct AttachMatch *am = *ptr;
2323  regfree(&am->minor_regex);
2324  FREE(&am->major);
2325  FREE(ptr);
2326 }
2327 
2331 static enum CommandResult parse_unattachments(struct Buffer *buf, struct Buffer *s,
2332  unsigned long data, struct Buffer *err)
2333 {
2334  char op;
2335  char *p = NULL;
2336  struct ListHead *head = NULL;
2337 
2339  if (!buf->data || (*buf->data == '\0'))
2340  {
2341  mutt_buffer_strcpy(err, _("unattachments: no disposition"));
2342  return MUTT_CMD_WARNING;
2343  }
2344 
2345  p = buf->data;
2346  op = *p++;
2347 
2348  if (op == '*')
2349  {
2351  mutt_list_free_type(&AttachExclude, (list_free_t) mutt_attachmatch_free);
2352  mutt_list_free_type(&InlineAllow, (list_free_t) mutt_attachmatch_free);
2353  mutt_list_free_type(&InlineExclude, (list_free_t) mutt_attachmatch_free);
2355  return 0;
2356  }
2357 
2358  if ((op != '+') && (op != '-'))
2359  {
2360  op = '+';
2361  p--;
2362  }
2363  if (mutt_str_startswith("attachment", p, CASE_IGNORE))
2364  {
2365  if (op == '+')
2366  head = &AttachAllow;
2367  else
2368  head = &AttachExclude;
2369  }
2370  else if (mutt_str_startswith("inline", p, CASE_IGNORE))
2371  {
2372  if (op == '+')
2373  head = &InlineAllow;
2374  else
2375  head = &InlineExclude;
2376  }
2377  else
2378  {
2379  mutt_buffer_strcpy(err, _("unattachments: invalid disposition"));
2380  return MUTT_CMD_ERROR;
2381  }
2382 
2383  return parse_unattach_list(buf, s, head, err);
2384 }
2385 
2389 static enum CommandResult parse_unignore(struct Buffer *buf, struct Buffer *s,
2390  unsigned long data, struct Buffer *err)
2391 {
2392  do
2393  {
2395 
2396  /* don't add "*" to the unignore list */
2397  if (strcmp(buf->data, "*") != 0)
2398  add_to_stailq(&UnIgnore, buf->data);
2399 
2400  remove_from_stailq(&Ignore, buf->data);
2401  } while (MoreArgs(s));
2402 
2403  return MUTT_CMD_SUCCESS;
2404 }
2405 
2409 static enum CommandResult parse_unlists(struct Buffer *buf, struct Buffer *s,
2410  unsigned long data, struct Buffer *err)
2411 {
2413  do
2414  {
2418 
2419  if ((mutt_str_strcmp(buf->data, "*") != 0) &&
2420  (mutt_regexlist_add(&UnMailLists, buf->data, REG_ICASE, err) != 0))
2421  {
2422  return MUTT_CMD_ERROR;
2423  }
2424  } while (MoreArgs(s));
2425 
2426  return MUTT_CMD_SUCCESS;
2427 }
2428 
2434 static enum CommandResult parse_unmailboxes(struct Buffer *buf, struct Buffer *s,
2435  unsigned long data, struct Buffer *err)
2436 {
2437  bool tmp_valid = false;
2438  bool clear_all = false;
2439 
2440  while (!clear_all && MoreArgs(s))
2441  {
2443 
2444  if (mutt_str_strcmp(buf->data, "*") == 0)
2445  {
2446  clear_all = true;
2447  tmp_valid = false;
2448  }
2449  else
2450  {
2452  tmp_valid = true;
2453  }
2454 
2455  struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
2456  struct MailboxNode *np = NULL;
2457  struct MailboxNode *nptmp = NULL;
2458  STAILQ_FOREACH_SAFE(np, &ml, entries, nptmp)
2459  {
2460  /* Decide whether to delete all normal mailboxes or all virtual */
2461  bool virt = ((np->mailbox->magic == MUTT_NOTMUCH) && (data & MUTT_VIRTUAL));
2462  bool norm = ((np->mailbox->magic != MUTT_NOTMUCH) && !(data & MUTT_VIRTUAL));
2463  bool clear_this = clear_all && (virt || norm);
2464 
2465  /* Compare against path or desc? Ensure 'buf' is valid */
2466  if (!clear_this && tmp_valid)
2467  {
2468  clear_this =
2469  (mutt_str_strcasecmp(mutt_b2s(buf), mailbox_path(np->mailbox)) == 0) ||
2470  (mutt_str_strcasecmp(mutt_b2s(buf), np->mailbox->name) == 0);
2471  }
2472 
2473  if (clear_this)
2474  {
2475 #ifdef USE_SIDEBAR
2476  mutt_sb_notify_mailbox(np->mailbox, false);
2477 #endif
2478 #ifdef USE_INOTIFY
2480 #endif
2481  if (Context && (Context->mailbox == np->mailbox))
2482  {
2483  np->mailbox->flags |= MB_HIDDEN;
2484  }
2485  else
2486  {
2488  mailbox_free(&np->mailbox);
2489  }
2490  }
2491  }
2493  }
2494  return MUTT_CMD_SUCCESS;
2495 }
2496 
2500 static enum CommandResult parse_unmy_hdr(struct Buffer *buf, struct Buffer *s,
2501  unsigned long data, struct Buffer *err)
2502 {
2503  struct ListNode *np = NULL, *tmp = NULL;
2504  size_t l;
2505 
2506  do
2507  {
2509  if (mutt_str_strcmp("*", buf->data) == 0)
2510  {
2511  mutt_list_free(&UserHeader);
2512  continue;
2513  }
2514 
2515  l = mutt_str_strlen(buf->data);
2516  if (buf->data[l - 1] == ':')
2517  l--;
2518 
2519  STAILQ_FOREACH_SAFE(np, &UserHeader, entries, tmp)
2520  {
2521  if ((mutt_str_strncasecmp(buf->data, np->data, l) == 0) && (np->data[l] == ':'))
2522  {
2523  STAILQ_REMOVE(&UserHeader, np, ListNode, entries);
2524  FREE(&np->data);
2525  FREE(&np);
2526  }
2527  }
2528  } while (MoreArgs(s));
2529  return MUTT_CMD_SUCCESS;
2530 }
2531 
2537 static enum CommandResult parse_unstailq(struct Buffer *buf, struct Buffer *s,
2538  unsigned long data, struct Buffer *err)
2539 {
2540  do
2541  {
2543  /* Check for deletion of entire list */
2544  if (mutt_str_strcmp(buf->data, "*") == 0)
2545  {
2546  mutt_list_free((struct ListHead *) data);
2547  break;
2548  }
2549  remove_from_stailq((struct ListHead *) data, buf->data);
2550  } while (MoreArgs(s));
2551 
2552  return MUTT_CMD_SUCCESS;
2553 }
2554 
2558 static enum CommandResult parse_unsubjectrx_list(struct Buffer *buf, struct Buffer *s,
2559  unsigned long data, struct Buffer *err)
2560 {
2561  enum CommandResult rc;
2562 
2563  rc = parse_unreplace_list(buf, s, data, err);
2564  if (rc == MUTT_CMD_SUCCESS)
2566  return rc;
2567 }
2568 
2572 static enum CommandResult parse_unsubscribe(struct Buffer *buf, struct Buffer *s,
2573  unsigned long data, struct Buffer *err)
2574 {
2576  do
2577  {
2580 
2581  if ((mutt_str_strcmp(buf->data, "*") != 0) &&
2582  (mutt_regexlist_add(&UnSubscribedLists, buf->data, REG_ICASE, err) != 0))
2583  {
2584  return MUTT_CMD_ERROR;
2585  }
2586  } while (MoreArgs(s));
2587 
2588  return MUTT_CMD_SUCCESS;
2589 }
2590 
2591 #ifdef USE_IMAP
2592 
2599 static enum CommandResult parse_unsubscribe_from(struct Buffer *buf, struct Buffer *s,
2600  unsigned long data, struct Buffer *err)
2601 {
2602  if (!buf || !s || !err)
2603  return MUTT_CMD_ERROR;
2604 
2605  if (MoreArgs(s))
2606  {
2608 
2609  if (MoreArgs(s))
2610  {
2611  mutt_buffer_printf(err, _("%s: too many arguments"), "unsubscribe-from");
2612  return MUTT_CMD_WARNING;
2613  }
2614 
2615  if (buf->data && (*buf->data != '\0'))
2616  {
2617  /* Expand and subscribe */
2618  if (imap_subscribe(mutt_expand_path(buf->data, buf->dsize), false) == 0)
2619  {
2620  mutt_message(_("Unsubscribed from %s"), buf->data);
2621  return MUTT_CMD_SUCCESS;
2622  }
2623 
2624  mutt_buffer_printf(err, _("Could not unsubscribe from %s"), buf->data);
2625  return MUTT_CMD_ERROR;
2626  }
2627 
2628  mutt_debug(LL_DEBUG1, "Corrupted buffer");
2629  return MUTT_CMD_ERROR;
2630  }
2631 
2632  mutt_buffer_addstr(err, _("No folder specified"));
2633  return MUTT_CMD_WARNING;
2634 }
2635 #endif
2636 
2643 const struct Command *mutt_command_get(const char *s)
2644 {
2645  for (int i = 0; Commands[i].name; i++)
2646  if (mutt_str_strcmp(s, Commands[i].name) == 0)
2647  return &Commands[i];
2648  return NULL;
2649 }
2650 
2651 #ifdef USE_LUA
2652 
2659 void mutt_commands_apply(void *data, void (*application)(void *, const struct Command *))
2660 {
2661  for (int i = 0; Commands[i].name; i++)
2662  application(data, &Commands[i]);
2663 }
2664 #endif
2665 
2674 int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
2675 {
2676  if (!dest || !tok)
2677  return -1;
2678 
2679  char ch;
2680  char qc = '\0'; /* quote char */
2681  char *pc = NULL;
2682 
2683  mutt_buffer_reset(dest);
2684 
2685  SKIPWS(tok->dptr);
2686  while ((ch = *tok->dptr))
2687  {
2688  if (qc == '\0')
2689  {
2690  if ((IS_SPACE(ch) && !(flags & MUTT_TOKEN_SPACE)) ||
2691  ((ch == '#') && !(flags & MUTT_TOKEN_COMMENT)) ||
2692  ((ch == '=') && (flags & MUTT_TOKEN_EQUAL)) ||
2693  ((ch == '?') && (flags & MUTT_TOKEN_QUESTION)) ||
2694  ((ch == ';') && !(flags & MUTT_TOKEN_SEMICOLON)) ||
2695  ((flags & MUTT_TOKEN_PATTERN) && strchr("~%=!|", ch)))
2696  {
2697  break;
2698  }
2699  }
2700 
2701  tok->dptr++;
2702 
2703  if (ch == qc)
2704  qc = 0; /* end of quote */
2705  else if (!qc && ((ch == '\'') || (ch == '"')) && !(flags & MUTT_TOKEN_QUOTE))
2706  qc = ch;
2707  else if ((ch == '\\') && (qc != '\''))
2708  {
2709  if (tok->dptr[0] == '\0')
2710  return -1; /* premature end of token */
2711  switch (ch = *tok->dptr++)
2712  {
2713  case 'c':
2714  case 'C':
2715  if (tok->dptr[0] == '\0')
2716  return -1; /* premature end of token */
2717  mutt_buffer_addch(dest, (toupper((unsigned char) tok->dptr[0]) - '@') & 0x7f);
2718  tok->dptr++;
2719  break;
2720  case 'e':
2721  mutt_buffer_addch(dest, '\033'); // Escape
2722  break;
2723  case 'f':
2724  mutt_buffer_addch(dest, '\f');
2725  break;
2726  case 'n':
2727  mutt_buffer_addch(dest, '\n');
2728  break;
2729  case 'r':
2730  mutt_buffer_addch(dest, '\r');
2731  break;
2732  case 't':
2733  mutt_buffer_addch(dest, '\t');
2734  break;
2735  default:
2736  if (isdigit((unsigned char) ch) && isdigit((unsigned char) tok->dptr[0]) &&
2737  isdigit((unsigned char) tok->dptr[1]))
2738  {
2739  mutt_buffer_addch(dest, (ch << 6) + (tok->dptr[0] << 3) + tok->dptr[1] - 3504);
2740  tok->dptr += 2;
2741  }
2742  else
2743  mutt_buffer_addch(dest, ch);
2744  }
2745  }
2746  else if ((ch == '^') && (flags & MUTT_TOKEN_CONDENSE))
2747  {
2748  if (tok->dptr[0] == '\0')
2749  return -1; /* premature end of token */
2750  ch = *tok->dptr++;
2751  if (ch == '^')
2752  mutt_buffer_addch(dest, ch);
2753  else if (ch == '[')
2754  mutt_buffer_addch(dest, '\033'); // Escape
2755  else if (isalpha((unsigned char) ch))
2756  mutt_buffer_addch(dest, toupper((unsigned char) ch) - '@');
2757  else
2758  {
2759  mutt_buffer_addch(dest, '^');
2760  mutt_buffer_addch(dest, ch);
2761  }
2762  }
2763  else if ((ch == '`') && (!qc || (qc == '"')))
2764  {
2765  FILE *fp = NULL;
2766  pid_t pid;
2767  int line = 0;
2768 
2769  pc = tok->dptr;
2770  do
2771  {
2772  pc = strpbrk(pc, "\\`");
2773  if (pc)
2774  {
2775  /* skip any quoted chars */
2776  if (*pc == '\\')
2777  pc += 2;
2778  }
2779  } while (pc && (pc[0] != '`'));
2780  if (!pc)
2781  {
2782  mutt_debug(LL_DEBUG1, "mismatched backticks\n");
2783  return -1;
2784  }
2785  struct Buffer cmd;
2786  mutt_buffer_init(&cmd);
2787  *pc = '\0';
2788  if (flags & MUTT_TOKEN_BACKTICK_VARS)
2789  {
2790  /* recursively extract tokens to interpolate variables */
2791  mutt_extract_token(&cmd, tok,
2792  MUTT_TOKEN_QUOTE | MUTT_TOKEN_SPACE | MUTT_TOKEN_COMMENT |
2794  }
2795  else
2796  {
2797  cmd.data = mutt_str_strdup(tok->dptr);
2798  }
2799  *pc = '`';
2800  pid = mutt_create_filter(cmd.data, NULL, &fp, NULL);
2801  if (pid < 0)
2802  {
2803  mutt_debug(LL_DEBUG1, "unable to fork command: %s\n", cmd.data);
2804  FREE(&cmd.data);
2805  return -1;
2806  }
2807  FREE(&cmd.data);
2808 
2809  tok->dptr = pc + 1;
2810 
2811  /* read line */
2812  struct Buffer expn = mutt_buffer_make(0);
2813  expn.data = mutt_file_read_line(NULL, &expn.dsize, fp, &line, 0);
2814  mutt_file_fclose(&fp);
2815  mutt_wait_filter(pid);
2816 
2817  /* if we got output, make a new string consisting of the shell output
2818  * plus whatever else was left on the original line */
2819  /* BUT: If this is inside a quoted string, directly add output to
2820  * the token */
2821  if (expn.data)
2822  {
2823  if (qc)
2824  {
2825  mutt_buffer_addstr(dest, expn.data);
2826  }
2827  else
2828  {
2829  struct Buffer *copy = mutt_buffer_pool_get();
2830  mutt_buffer_fix_dptr(&expn);
2831  mutt_buffer_copy(copy, &expn);
2832  mutt_buffer_addstr(copy, tok->dptr);
2833  mutt_buffer_copy(tok, copy);
2834  tok->dptr = tok->data;
2835  mutt_buffer_pool_release(&copy);
2836  }
2837  FREE(&expn.data);
2838  }
2839  }
2840  else if ((ch == '$') && (!qc || (qc == '"')) &&
2841  ((tok->dptr[0] == '{') || isalpha((unsigned char) tok->dptr[0])))
2842  {
2843  const char *env = NULL;
2844  char *var = NULL;
2845 
2846  if (tok->dptr[0] == '{')
2847  {
2848  pc = strchr(tok->dptr, '}');
2849  if (pc)
2850  {
2851  var = mutt_str_substr_dup(tok->dptr + 1, pc);
2852  tok->dptr = pc + 1;
2853 
2854  if ((flags & MUTT_TOKEN_NOSHELL))
2855  {
2856  mutt_buffer_addch(dest, ch);
2857  mutt_buffer_addch(dest, '{');
2858  mutt_buffer_addstr(dest, var);
2859  mutt_buffer_addch(dest, '}');
2860  FREE(&var);
2861  }
2862  }
2863  }
2864  else
2865  {
2866  for (pc = tok->dptr; isalnum((unsigned char) *pc) || (pc[0] == '_'); pc++)
2867  ;
2868  var = mutt_str_substr_dup(tok->dptr, pc);
2869  tok->dptr = pc;
2870  }
2871  if (var)
2872  {
2873  struct Buffer result;
2874  mutt_buffer_init(&result);
2875  int rc = cs_str_string_get(Config, var, &result);
2876 
2877  if (CSR_RESULT(rc) == CSR_SUCCESS)
2878  {
2879  mutt_buffer_addstr(dest, result.data);
2880  FREE(&result.data);
2881  }
2882  else if ((env = myvar_get(var)))
2883  {
2884  mutt_buffer_addstr(dest, env);
2885  }
2886  else if (!(flags & MUTT_TOKEN_NOSHELL) && (env = mutt_str_getenv(var)))
2887  {
2888  mutt_buffer_addstr(dest, env);
2889  }
2890  else
2891  {
2892  mutt_buffer_addch(dest, ch);
2893  mutt_buffer_addstr(dest, var);
2894  }
2895  FREE(&var);
2896  }
2897  }
2898  else
2899  mutt_buffer_addch(dest, ch);
2900  }
2901  mutt_buffer_addch(dest, 0); /* terminate the string */
2902  SKIPWS(tok->dptr);
2903  return 0;
2904 }
2905 
2909 void mutt_opts_free(void)
2910 {
2912 
2913  FREE(&Matches);
2914 
2915  mutt_aliaslist_free(&Aliases);
2916 
2917  mutt_regexlist_free(&Alternates);
2921  mutt_regexlist_free(&UnAlternates);
2924 
2929 
2930  /* Lists of strings */
2931  mutt_list_free(&AlternativeOrderList);
2932  mutt_list_free(&AutoViewList);
2933  mutt_list_free(&HeaderOrderList);
2936  mutt_list_free(&MimeLookupList);
2937  mutt_list_free(&Muttrc);
2939 #ifdef USE_SIDEBAR
2940  mutt_list_free(&SidebarWhitelist);
2941 #endif
2943  mutt_list_free(&UserHeader);
2944 
2945  /* Lists of AttachMatch */
2947  mutt_list_free_type(&AttachExclude, (list_free_t) mutt_attachmatch_free);
2948  mutt_list_free_type(&InlineAllow, (list_free_t) mutt_attachmatch_free);
2949  mutt_list_free_type(&InlineExclude, (list_free_t) mutt_attachmatch_free);
2950 
2952 
2953  FREE(&CurrentFolder);
2954  FREE(&HomeDir);
2955  FREE(&LastFolder);
2956  FREE(&ShortHostname);
2957  FREE(&Username);
2958 
2961 
2963 
2964  mutt_hist_free();
2965  mutt_keys_free();
2966 
2968 }
2969 
2977 {
2978  for (const struct Command *c = Commands; c->name; c++)
2979  {
2980  if (((c->func == mutt_parse_hook) || (c->func == mutt_parse_idxfmt_hook)) &&
2981  (mutt_str_strcasecmp(c->name, name) == 0))
2982  {
2983  return c->data;
2984  }
2985  }
2986  return MUTT_HOOK_NO_FLAGS;
2987 }
2988 
2996 int mutt_init(bool skip_sys_rc, struct ListHead *commands)
2997 {
2998  char buf[1024];
2999  int need_pause = 0;
3000  int rc = 1;
3001  struct Buffer err = mutt_buffer_make(256);
3002 
3004  /* reverse alias keys need to be strdup'ed because of idna conversions */
3009 
3010  mutt_menu_init();
3011 
3012  snprintf(AttachmentMarker, sizeof(AttachmentMarker), "\033]9;%" PRIu64 "\a", // Escape
3013  mutt_rand64());
3014 
3015  snprintf(ProtectedHeaderMarker, sizeof(ProtectedHeaderMarker), "\033]8;%lld\a", // Escape
3016  (long long) mutt_date_epoch());
3017 
3018  /* "$spoolfile" precedence: config file, environment */
3019  const char *p = mutt_str_getenv("MAIL");
3020  if (!p)
3021  p = mutt_str_getenv("MAILDIR");
3022  if (!p)
3023  {
3024 #ifdef HOMESPOOL
3025  mutt_path_concat(buf, NONULL(HomeDir), MAILPATH, sizeof(buf));
3026 #else
3027  mutt_path_concat(buf, MAILPATH, NONULL(Username), sizeof(buf));
3028 #endif
3029  p = buf;
3030  }
3031  cs_str_initial_set(Config, "spoolfile", p, NULL);
3032  cs_str_reset(Config, "spoolfile", NULL);
3033 
3034  p = mutt_str_getenv("REPLYTO");
3035  if (p)
3036  {
3037  struct Buffer tmp, token;
3038 
3039  snprintf(buf, sizeof(buf), "Reply-To: %s", p);
3040 
3041  mutt_buffer_init(&tmp);
3042  tmp.data = buf;
3043  tmp.dptr = buf;
3044  tmp.dsize = mutt_str_strlen(buf);
3045 
3046  mutt_buffer_init(&token);
3047  parse_my_hdr(&token, &tmp, 0, &err); /* adds to UserHeader */
3048  FREE(&token.data);
3049  }
3050 
3051  p = mutt_str_getenv("EMAIL");
3052  if (p)
3053  {
3054  cs_str_initial_set(Config, "from", p, NULL);
3055  cs_str_reset(Config, "from", NULL);
3056  }
3057 
3058  /* "$mailcap_path" precedence: config file, environment, code */
3059  const char *env_mc = mutt_str_getenv("MAILCAPS");
3060  if (env_mc)
3061  cs_str_string_set(Config, "mailcap_path", env_mc, NULL);
3062 
3063  /* "$tmpdir" precedence: config file, environment, code */
3064  const char *env_tmp = mutt_str_getenv("TMPDIR");
3065  if (env_tmp)
3066  cs_str_string_set(Config, "tmpdir", env_tmp, NULL);
3067 
3068  /* "$visual", "$editor" precedence: config file, environment, code */
3069  const char *env_ed = mutt_str_getenv("VISUAL");
3070  if (!env_ed)
3071  env_ed = mutt_str_getenv("EDITOR");
3072  if (env_ed)
3073  {
3074  cs_str_string_set(Config, "editor", env_ed, NULL);
3075  cs_str_string_set(Config, "visual", env_ed, NULL);
3076  }
3077 
3079  cs_str_initial_set(Config, "charset", C_Charset, NULL);
3081 
3082  Matches = mutt_mem_calloc(MatchesListsize, sizeof(char *));
3083 
3085 
3086 #ifdef HAVE_GETSID
3087  /* Unset suspend by default if we're the session leader */
3088  if (getsid(0) == getpid())
3089  C_Suspend = false;
3090 #endif
3091 
3092  /* RFC2368, "4. Unsafe headers"
3093  * The creator of a mailto URL can't expect the resolver of a URL to
3094  * understand more than the "subject" and "body" headers. Clients that
3095  * resolve mailto URLs into mail messages should be able to correctly
3096  * create RFC822-compliant mail messages using the "subject" and "body"
3097  * headers. */
3098  add_to_stailq(&MailToAllow, "body");
3099  add_to_stailq(&MailToAllow, "subject");
3100  /* Cc, In-Reply-To, and References help with not breaking threading on
3101  * mailing lists, see https://github.com/neomutt/neomutt/issues/115 */
3102  add_to_stailq(&MailToAllow, "cc");
3103  add_to_stailq(&MailToAllow, "in-reply-to");
3104  add_to_stailq(&MailToAllow, "references");
3105 
3106  if (STAILQ_EMPTY(&Muttrc))
3107  {
3108  const char *xdg_cfg_home = mutt_str_getenv("XDG_CONFIG_HOME");
3109 
3110  if (!xdg_cfg_home && HomeDir)
3111  {
3112  snprintf(buf, sizeof(buf), "%s/.config", HomeDir);
3113  xdg_cfg_home = buf;
3114  }
3115 
3116  char *config = find_cfg(HomeDir, xdg_cfg_home);
3117  if (config)
3118  {
3119  mutt_list_insert_tail(&Muttrc, config);
3120  }
3121  }
3122  else
3123  {
3124  struct ListNode *np = NULL;
3125  STAILQ_FOREACH(np, &Muttrc, entries)
3126  {
3127  mutt_str_strfcpy(buf, np->data, sizeof(buf));
3128  FREE(&np->data);
3129  mutt_expand_path(buf, sizeof(buf));
3130  np->data = mutt_str_strdup(buf);
3131  if (access(np->data, F_OK))
3132  {
3133  mutt_perror(np->data);
3134  goto done; // TEST10: neomutt -F missing
3135  }
3136  }
3137  }
3138 
3139  if (!STAILQ_EMPTY(&Muttrc))
3140  {
3141  cs_str_string_set(Config, "alias_file", STAILQ_FIRST(&Muttrc)->data, NULL);
3142  }
3143 
3144  /* Process the global rc file if it exists and the user hasn't explicitly
3145  * requested not to via "-n". */
3146  if (!skip_sys_rc)
3147  {
3148  do
3149  {
3150  if (mutt_set_xdg_path(XDG_CONFIG_DIRS, buf, sizeof(buf)))
3151  break;
3152 
3153  snprintf(buf, sizeof(buf), "%s/neomuttrc", SYSCONFDIR);
3154  if (access(buf, F_OK) == 0)
3155  break;
3156 
3157  snprintf(buf, sizeof(buf), "%s/Muttrc", SYSCONFDIR);
3158  if (access(buf, F_OK) == 0)
3159  break;
3160 
3161  snprintf(buf, sizeof(buf), "%s/neomuttrc", PKGDATADIR);
3162  if (access(buf, F_OK) == 0)
3163  break;
3164 
3165  snprintf(buf, sizeof(buf), "%s/Muttrc", PKGDATADIR);
3166  } while (false);
3167 
3168  if (access(buf, F_OK) == 0)
3169  {
3170  if (source_rc(buf, &err) != 0)
3171  {
3172  mutt_error("%s", err.data);
3173  need_pause = 1; // TEST11: neomutt (error in /etc/neomuttrc)
3174  }
3175  }
3176  }
3177 
3178  /* Read the user's initialization file. */
3179  struct ListNode *np = NULL;
3180  STAILQ_FOREACH(np, &Muttrc, entries)
3181  {
3182  if (np->data)
3183  {
3184  if (source_rc(np->data, &err) != 0)
3185  {
3186  mutt_error("%s", err.data);
3187  need_pause = 1; // TEST12: neomutt (error in ~/.neomuttrc)
3188  }
3189  }
3190  }
3191 
3192  if (execute_commands(commands) != 0)
3193  need_pause = 1; // TEST13: neomutt -e broken
3194 
3195  if (!get_hostname())
3196  goto done;
3197 
3198  if (!C_Realname)
3199  {
3200  struct passwd *pw = getpwuid(getuid());
3201  if (pw)
3202  {
3203  char name[256];
3204  C_Realname = mutt_str_strdup(mutt_gecos_name(name, sizeof(name), pw));
3205  }
3206  }
3207  cs_str_initial_set(Config, "realname", C_Realname, NULL);
3208 
3209  if (need_pause && !OptNoCurses)
3210  {
3212  if (mutt_any_key_to_continue(NULL) == 'q')
3213  goto done; // TEST14: neomutt -e broken (press 'q')
3214  }
3215 
3216  mutt_file_mkdir(C_Tmpdir, S_IRWXU);
3217 
3218  mutt_hist_init();
3220 
3221 #ifdef USE_NOTMUCH
3222  if (C_VirtualSpoolfile)
3223  {
3224  /* Find the first virtual folder and open it */
3225  struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_NOTMUCH);
3226  struct MailboxNode *mp = STAILQ_FIRST(&ml);
3227  if (mp)
3228  cs_str_string_set(Config, "spoolfile", mailbox_path(mp->mailbox), NULL);
3230  }
3231 #endif
3232  rc = 0;
3233 
3234 done:
3235  FREE(&err.data);
3236  return rc;
3237 }
3238 
3251 enum CommandResult mutt_parse_rc_line(/* const */ char *line,
3252  struct Buffer *token, struct Buffer *err)
3253 {
3254  if (!line || !*line)
3255  return 0;
3256 
3257  int i;
3258  enum CommandResult rc = MUTT_CMD_SUCCESS;
3259 
3260  struct Buffer expn = mutt_buffer_make(128);
3261  mutt_buffer_addstr(&expn, line);
3262  expn.dptr = expn.data;
3263 
3264  *err->data = 0;
3265 
3266  SKIPWS(expn.dptr);
3267  while (*expn.dptr != '\0')
3268  {
3269  if (*expn.dptr == '#')
3270  break; /* rest of line is a comment */
3271  if (*expn.dptr == ';')
3272  {
3273  expn.dptr++;
3274  continue;
3275  }
3276  mutt_extract_token(token, &expn, MUTT_TOKEN_NO_FLAGS);
3277  for (i = 0; Commands[i].name; i++)
3278  {
3279  if (mutt_str_strcmp(token->data, Commands[i].name) == 0)
3280  {
3281  rc = Commands[i].func(token, &expn, Commands[i].data, err);
3282  if (rc != MUTT_CMD_SUCCESS)
3283  { /* -1 Error, +1 Finish */
3284  goto finish; /* Propagate return code */
3285  }
3287  break; /* Continue with next command */
3288  }
3289  }
3290  if (!Commands[i].name)
3291  {
3292  mutt_buffer_printf(err, _("%s: unknown command"), NONULL(token->data));
3293  rc = MUTT_CMD_ERROR;
3294  break; /* Ignore the rest of the line */
3295  }
3296  }
3297 finish:
3298  mutt_buffer_dealloc(&expn);
3299  return rc;
3300 }
3301 
3308 int mutt_query_variables(struct ListHead *queries)
3309 {
3310  struct Buffer value = mutt_buffer_make(256);
3311  struct Buffer tmp = mutt_buffer_make(256);
3312  int rc = 0;
3313 
3314  struct ListNode *np = NULL;
3315  STAILQ_FOREACH(np, queries, entries)
3316  {
3317  mutt_buffer_reset(&value);
3318 
3319  struct HashElem *he = cs_get_elem(Config, np->data);
3320  if (!he)
3321  {
3322  rc = 1;
3323  continue;
3324  }
3325 
3326  int rv = cs_he_string_get(Config, he, &value);
3327  if (CSR_RESULT(rv) != CSR_SUCCESS)
3328  {
3329  rc = 1;
3330  continue;
3331  }
3332 
3333  int type = DTYPE(he->type);
3334  if (IS_PATH(he) && !(he->type & DT_MAILBOX))
3335  mutt_pretty_mailbox(value.data, value.dsize);
3336 
3337  if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_LONG) && (type != DT_QUAD))
3338  {
3339  mutt_buffer_reset(&tmp);
3340  pretty_var(value.data, &tmp);
3341  mutt_buffer_strcpy(&value, tmp.data);
3342  }
3343 
3344  dump_config_neo(Config, he, &value, NULL, CS_DUMP_NO_FLAGS, stdout);
3345  }
3346 
3347  mutt_buffer_dealloc(&value);
3348  mutt_buffer_dealloc(&tmp);
3349 
3350  return rc; // TEST16: neomutt -Q charset
3351 }
3352 
3359 enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
3360 {
3361  switch (opt)
3362  {
3363  case MUTT_YES:
3364  case MUTT_NO:
3365  return opt;
3366 
3367  default:
3368  opt = mutt_yesorno(prompt, (opt == MUTT_ASKYES) ? MUTT_YES : MUTT_NO);
3370  return opt;
3371  }
3372 
3373  /* not reached */
3374 }
3375 
3385 int mutt_command_complete(char *buf, size_t buflen, int pos, int numtabs)
3386 {
3387  char *pt = buf;
3388  int num;
3389  int spaces; /* keep track of the number of leading spaces on the line */
3390  struct MyVar *myv = NULL;
3391 
3392  SKIPWS(buf);
3393  spaces = buf - pt;
3394 
3395  pt = buf + pos - spaces;
3396  while ((pt > buf) && !isspace((unsigned char) *pt))
3397  pt--;
3398 
3399  if (pt == buf) /* complete cmd */
3400  {
3401  /* first TAB. Collect all the matches */
3402  if (numtabs == 1)
3403  {
3404  NumMatched = 0;
3405  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
3406  memset(Matches, 0, MatchesListsize);
3407  memset(Completed, 0, sizeof(Completed));
3408  for (num = 0; Commands[num].name; num++)
3412 
3413  /* All matches are stored. Longest non-ambiguous string is ""
3414  * i.e. don't change 'buf'. Fake successful return this time */
3415  if (UserTyped[0] == '\0')
3416  return 1;
3417  }
3418 
3419  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3420  return 0;
3421 
3422  /* NumMatched will _always_ be at least 1 since the initial
3423  * user-typed string is always stored */
3424  if ((numtabs == 1) && (NumMatched == 2))
3425  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3426  else if ((numtabs > 1) && (NumMatched > 2))
3427  {
3428  /* cycle through all the matches */
3429  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3430  }
3431 
3432  /* return the completed command */
3433  strncpy(buf, Completed, buflen - spaces);
3434  }
3435  else if (mutt_str_startswith(buf, "set", CASE_MATCH) ||
3436  mutt_str_startswith(buf, "unset", CASE_MATCH) ||
3437  mutt_str_startswith(buf, "reset", CASE_MATCH) ||
3438  mutt_str_startswith(buf, "toggle", CASE_MATCH))
3439  { /* complete variables */
3440  static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
3441 
3442  pt++;
3443  /* loop through all the possible prefixes (no, inv, ...) */
3444  if (mutt_str_startswith(buf, "set", CASE_MATCH))
3445  {
3446  for (num = 0; prefixes[num]; num++)
3447  {
3448  if (mutt_str_startswith(pt, prefixes[num], CASE_MATCH))
3449  {
3450  pt += mutt_str_strlen(prefixes[num]);
3451  break;
3452  }
3453  }
3454  }
3455 
3456  /* first TAB. Collect all the matches */
3457  if (numtabs == 1)
3458  {
3459  NumMatched = 0;
3460  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
3461  memset(Matches, 0, MatchesListsize);
3462  memset(Completed, 0, sizeof(Completed));
3463  for (num = 0; MuttVars[num].name; num++)
3465  TAILQ_FOREACH(myv, &MyVars, entries)
3466  {
3467  candidate(UserTyped, myv->name, Completed, sizeof(Completed));
3468  }
3471 
3472  /* All matches are stored. Longest non-ambiguous string is ""
3473  * i.e. don't change 'buf'. Fake successful return this time */
3474  if (UserTyped[0] == '\0')
3475  return 1;
3476  }
3477 
3478  if ((Completed[0] == 0) && UserTyped[0])
3479  return 0;
3480 
3481  /* NumMatched will _always_ be at least 1 since the initial
3482  * user-typed string is always stored */
3483  if ((numtabs == 1) && (NumMatched == 2))
3484  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3485  else if ((numtabs > 1) && (NumMatched > 2))
3486  {
3487  /* cycle through all the matches */
3488  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3489  }
3490 
3491  strncpy(pt, Completed, buf + buflen - pt - spaces);
3492  }
3493  else if (mutt_str_startswith(buf, "exec", CASE_MATCH))
3494  {
3495  const struct Binding *menu = km_get_table(CurrentMenu);
3496 
3497  if (!menu && (CurrentMenu != MENU_PAGER))
3498  menu = OpGeneric;
3499 
3500  pt++;
3501  /* first TAB. Collect all the matches */
3502  if (numtabs == 1)
3503  {
3504  NumMatched = 0;
3505  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
3506  memset(Matches, 0, MatchesListsize);
3507  memset(Completed, 0, sizeof(Completed));
3508  for (num = 0; menu[num].name; num++)
3509  candidate(UserTyped, menu[num].name, Completed, sizeof(Completed));
3510  /* try the generic menu */
3511  if ((Completed[0] == '\0') && (CurrentMenu != MENU_PAGER))
3512  {
3513  menu = OpGeneric;
3514  for (num = 0; menu[num].name; num++)
3515  candidate(UserTyped, menu[num].name, Completed, sizeof(Completed));
3516  }
3519 
3520  /* All matches are stored. Longest non-ambiguous string is ""
3521  * i.e. don't change 'buf'. Fake successful return this time */
3522  if (UserTyped[0] == '\0')
3523  return 1;
3524  }
3525 
3526  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3527  return 0;
3528 
3529  /* NumMatched will _always_ be at least 1 since the initial
3530  * user-typed string is always stored */
3531  if ((numtabs == 1) && (NumMatched == 2))
3532  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3533  else if ((numtabs > 1) && (NumMatched > 2))
3534  {
3535  /* cycle through all the matches */
3536  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3537  }
3538 
3539  strncpy(pt, Completed, buf + buflen - pt - spaces);
3540  }
3541  else
3542  return 0;
3543 
3544  return 1;
3545 }
3546 
3555 int mutt_label_complete(char *buf, size_t buflen, int numtabs)
3556 {
3557  char *pt = buf;
3558  int spaces; /* keep track of the number of leading spaces on the line */
3559 
3560  if (!Context || !Context->mailbox->label_hash)
3561  return 0;
3562 
3563  SKIPWS(buf);
3564  spaces = buf - pt;
3565 
3566  /* first TAB. Collect all the matches */
3567  if (numtabs == 1)
3568  {
3569  struct HashElem *entry = NULL;
3570  struct HashWalkState state = { 0 };
3571 
3572  NumMatched = 0;
3573  mutt_str_strfcpy(UserTyped, buf, sizeof(UserTyped));
3574  memset(Matches, 0, MatchesListsize);
3575  memset(Completed, 0, sizeof(Completed));
3576  while ((entry = mutt_hash_walk(Context->mailbox->label_hash, &state)))
3577  candidate(UserTyped, entry->key.strkey, Completed, sizeof(Completed));
3579  qsort(Matches, NumMatched, sizeof(char *), (sort_t) mutt_str_strcasecmp);
3581 
3582  /* All matches are stored. Longest non-ambiguous string is ""
3583  * i.e. don't change 'buf'. Fake successful return this time */
3584  if (UserTyped[0] == '\0')
3585  return 1;
3586  }
3587 
3588  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3589  return 0;
3590 
3591  /* NumMatched will _always_ be at least 1 since the initial
3592  * user-typed string is always stored */
3593  if ((numtabs == 1) && (NumMatched == 2))
3594  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3595  else if ((numtabs > 1) && (NumMatched > 2))
3596  {
3597  /* cycle through all the matches */
3598  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3599  }
3600 
3601  /* return the completed label */
3602  strncpy(buf, Completed, buflen - spaces);
3603 
3604  return 1;
3605 }
3606 
3607 #ifdef USE_NOTMUCH
3608 
3619 bool mutt_nm_query_complete(char *buf, size_t buflen, int pos, int numtabs)
3620 {
3621  char *pt = buf;
3622  int spaces;
3623 
3624  SKIPWS(buf);
3625  spaces = buf - pt;
3626 
3627  pt = (char *) mutt_str_rstrnstr((char *) buf, pos, "tag:");
3628  if (pt)
3629  {
3630  pt += 4;
3631  if (numtabs == 1)
3632  {
3633  /* First TAB. Collect all the matches */
3635 
3636  /* All matches are stored. Longest non-ambiguous string is ""
3637  * i.e. don't change 'buf'. Fake successful return this time. */
3638  if (UserTyped[0] == '\0')
3639  return true;
3640  }
3641 
3642  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3643  return false;
3644 
3645  /* NumMatched will _always_ be at least 1 since the initial
3646  * user-typed string is always stored */
3647  if ((numtabs == 1) && (NumMatched == 2))
3648  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3649  else if ((numtabs > 1) && (NumMatched > 2))
3650  {
3651  /* cycle through all the matches */
3652  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3653  }
3654 
3655  /* return the completed query */
3656  strncpy(pt, Completed, buf + buflen - pt - spaces);
3657  }
3658  else
3659  return false;
3660 
3661  return true;
3662 }
3663 #endif
3664 
3665 #ifdef USE_NOTMUCH
3666 
3676 bool mutt_nm_tag_complete(char *buf, size_t buflen, int numtabs)
3677 {
3678  if (!buf)
3679  return false;
3680 
3681  char *pt = buf;
3682 
3683  /* Only examine the last token */
3684  char *last_space = strrchr(buf, ' ');
3685  if (last_space)
3686  pt = (last_space + 1);
3687 
3688  /* Skip the +/- */
3689  if ((pt[0] == '+') || (pt[0] == '-'))
3690  pt++;
3691 
3692  if (numtabs == 1)
3693  {
3694  /* First TAB. Collect all the matches */
3696 
3697  /* All matches are stored. Longest non-ambiguous string is ""
3698  * i.e. don't change 'buf'. Fake successful return this time. */
3699  if (UserTyped[0] == '\0')
3700  return true;
3701  }
3702 
3703  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3704  return false;
3705 
3706  /* NumMatched will _always_ be at least 1 since the initial
3707  * user-typed string is always stored */
3708  if ((numtabs == 1) && (NumMatched == 2))
3709  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3710  else if ((numtabs > 1) && (NumMatched > 2))
3711  {
3712  /* cycle through all the matches */
3713  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3714  }
3715 
3716  /* return the completed query */
3717  strncpy(pt, Completed, buf + buflen - pt);
3718 
3719  return true;
3720 }
3721 #endif
3722 
3729 int mutt_var_value_complete(char *buf, size_t buflen, int pos)
3730 {
3731  char *pt = buf;
3732 
3733  if (buf[0] == '\0')
3734  return 0;
3735 
3736  SKIPWS(buf);
3737  const int spaces = buf - pt;
3738 
3739  pt = buf + pos - spaces;
3740  while ((pt > buf) && !isspace((unsigned char) *pt))
3741  pt--;
3742  pt++; /* move past the space */
3743  if (*pt == '=') /* abort if no var before the '=' */
3744  return 0;
3745 
3746  if (mutt_str_startswith(buf, "set", CASE_MATCH))
3747  {
3748  const char *myvarval = NULL;
3749  char var[256];
3750  mutt_str_strfcpy(var, pt, sizeof(var));
3751  /* ignore the trailing '=' when comparing */
3752  int vlen = mutt_str_strlen(var);
3753  if (vlen == 0)
3754  return 0;
3755 
3756  var[vlen - 1] = '\0';
3757 
3758  struct HashElem *he = cs_get_elem(Config, var);
3759  if (!he)
3760  {
3761  myvarval = myvar_get(var);
3762  if (myvarval)
3763  {
3764  struct Buffer pretty = mutt_buffer_make(256);
3765  pretty_var(myvarval, &pretty);
3766  snprintf(pt, buflen - (pt - buf), "%s=%s", var, pretty.data);
3767  mutt_buffer_dealloc(&pretty);
3768  return 1;
3769  }
3770  return 0; /* no such variable. */
3771  }
3772  else
3773  {
3774  struct Buffer value = mutt_buffer_make(256);
3775  struct Buffer pretty = mutt_buffer_make(256);
3776  int rc = cs_he_string_get(Config, he, &value);
3777  if (CSR_RESULT(rc) == CSR_SUCCESS)
3778  {
3779  pretty_var(value.data, &pretty);
3780  snprintf(pt, buflen - (pt - buf), "%s=%s", var, pretty.data);
3781  mutt_buffer_dealloc(&value);
3782  mutt_buffer_dealloc(&pretty);
3783  return 0;
3784  }
3785  mutt_buffer_dealloc(&value);
3786  mutt_buffer_dealloc(&pretty);
3787  return 1;
3788  }
3789  }
3790  return 0;
3791 }
3792 
3797 struct ConfigSet *init_config(size_t size)
3798 {
3799  struct ConfigSet *cs = cs_new(size);
3800 
3801  address_init(cs);
3802  bool_init(cs);
3803  enum_init(cs);
3804  long_init(cs);
3805  mbtable_init(cs);
3806  number_init(cs);
3807  quad_init(cs);
3808  regex_init(cs);
3809  slist_init(cs);
3810  sort_init(cs);
3811  string_init(cs);
3812 
3813  if (!cs_register_variables(cs, MuttVars, 0))
3814  {
3815  mutt_error("cs_register_variables() failed");
3816  cs_free(&cs);
3817  return NULL;
3818  }
3819 
3820  return cs;
3821 }
3822 
3826 int charset_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3827  intptr_t value, struct Buffer *err)
3828 {
3829  if (value == 0)
3830  return CSR_SUCCESS;
3831 
3832  const char *str = (const char *) value;
3833 
3834  if ((strcmp(cdef->name, "charset") == 0) && strchr(str, ':'))
3835  {
3837  err, _("'charset' must contain exactly one character set name"));
3838  return CSR_ERR_INVALID;
3839  }
3840 
3841  int rc = CSR_SUCCESS;
3842  bool strict = (strcmp(cdef->name, "send_charset") == 0);
3843  char *q = NULL;
3844  char *s = mutt_str_strdup(str);
3845 
3846  for (char *p = strtok_r(s, ":", &q); p; p = strtok_r(NULL, ":", &q))
3847  {
3848  if (!*p)
3849  continue;
3850  if (!mutt_ch_check_charset(p, strict))
3851  {
3852  rc = CSR_ERR_INVALID;
3853  mutt_buffer_printf(err, _("Invalid value for option %s: %s"), cdef->name, p);
3854  break;
3855  }
3856  }
3857 
3858  FREE(&s);
3859  return rc;
3860 }
3861 
3862 #ifdef USE_HCACHE
3863 
3866 int hcache_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3867  intptr_t value, struct Buffer *err)
3868 {
3869  if (value == 0)
3870  return CSR_SUCCESS;
3871 
3872  const char *str = (const char *) value;
3873 
3875  return CSR_SUCCESS;
3876 
3877  mutt_buffer_printf(err, _("Invalid value for option %s: %s"), cdef->name, str);
3878  return CSR_ERR_INVALID;
3879 }
3880 #endif
3881 
3885 int pager_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3886  intptr_t value, struct Buffer *err)
3887 {
3888  if (CurrentMenu == MENU_PAGER)
3889  {
3890  mutt_buffer_printf(err, _("Option %s may not be set or reset from the pager"),
3891  cdef->name);
3892  return CSR_ERR_INVALID;
3893  }
3894 
3895  return CSR_SUCCESS;
3896 }
3897 
3901 int multipart_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3902  intptr_t value, struct Buffer *err)
3903 {
3904  if (value == 0)
3905  return CSR_SUCCESS;
3906 
3907  const char *str = (const char *) value;
3908 
3909  if ((mutt_str_strcmp(str, "inline") == 0) || (mutt_str_strcmp(str, "info") == 0))
3910  return CSR_SUCCESS;
3911 
3912  mutt_buffer_printf(err, _("Invalid value for option %s: %s"), cdef->name, str);
3913  return CSR_ERR_INVALID;
3914 }
3915 
3919 int reply_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3920  intptr_t value, struct Buffer *err)
3921 {
3922  if (pager_validator(cs, cdef, value, err) != CSR_SUCCESS)
3923  return CSR_ERR_INVALID;
3924 
3925  if (!OptAttachMsg)
3926  return CSR_SUCCESS;
3927 
3928  mutt_buffer_printf(err, _("Option %s may not be set when in attach-message mode"),
3929  cdef->name);
3930  return CSR_ERR_INVALID;
3931 }
3932 
3936 int wrapheaders_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3937  intptr_t value, struct Buffer *err)
3938 {
3939  if ((value >= 78) && (value <= 998)) // Recommendation from RFC5233
3940  return CSR_SUCCESS;
3941 
3942  // L10N: This applies to the "$wrap_headers" config variable
3943  mutt_buffer_printf(err, _("Option %s must between 78 and 998 inclusive"), cdef->name);
3944  return CSR_ERR_INVALID;
3945 }
pid_t mutt_create_filter(const char *s, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:209
void mutt_alias_delete_reverse(struct Alias *t)
Remove an email address lookup for an Alias.
Definition: alias.c:575
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:411
A shortcut for an email address.
Definition: alias.h:37
WHERE char * Username
User&#39;s login name.
Definition: globals.h:53
#define mutt_warning(...)
Definition: logging.h:82
struct ConfigSet * init_config(size_t size)
Initialise the config system.
Definition: init.c:3797
The "current" mailbox.
Definition: context.h:36
static struct AttachMatch * mutt_attachmatch_new(void)
Create a new AttachMatch.
Definition: init.c:483
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:194
char * mutt_path_concat(char *d, const char *dir, const char *fname, size_t l)
Join a directory name and a filename.
Definition: path.c:329
union HashKey key
Definition: hash.h:45
Log at debug level 4.
Definition: logging.h:43
static enum CommandResult parse_path_unlist(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unsidebar_whitelist&#39; command - Implements command_t.
Definition: init.c:1501
Notmuch virtual mailbox type.
char * name
Short name.
Definition: alias.h:39
Container for lots of config items.
Definition: set.h:187
void quad_init(struct ConfigSet *cs)
Register the Quad-option config type.
Definition: quad.c:191
Manage keymappings.
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int mutt_ch_convert_string(char **ps, const char *from, const char *to, int flags)
Convert a string between encodings.
Definition: charset.c:748
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:400
static bool is_function(const char *name)
Is the argument a neomutt function?
Definition: init.c:1222
Miscellaneous email parsing routines.
int msg_count
Total number of messages.
Definition: mailbox.h:102
void bool_init(struct ConfigSet *cs)
Register the Bool config type.
Definition: bool.c:189
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
int mutt_replacelist_add(struct ReplaceList *rl, const char *pat, const char *templ, struct Buffer *err)
Add a pattern and a template to a list.
Definition: regex.c:262
char * name
Name of Account.
Definition: account.h:39
#define MUTT_UNGROUP
&#39;ungroup&#39; config command
Definition: group.h:33
static void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: init.c:753
static enum CommandResult parse_unalternates(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unalternates&#39; command - Implements command_t.
Definition: init.c:2290
The envelope/body of an email.
Definition: email.h:37
int mutt_regexlist_remove(struct RegexList *rl, const char *str)
Remove a Regex from a list.
Definition: regex.c:226
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define DT_LONG
a number (long)
Definition: types.h:33
intptr_t data
Data or flags to pass to the command.
Definition: mutt_commands.h:58
#define IS_PATH(x)
Definition: types.h:55
static enum CommandResult parse_unattach_list(struct Buffer *buf, struct Buffer *s, struct ListHead *head, struct Buffer *err)
Parse the "unattachments" command.
Definition: init.c:643
#define mutt_perror(...)
Definition: logging.h:85
CommandResult
Error codes for command_t parse functions.
Definition: mutt_commands.h:33
bool feature_enabled(const char *name)
Test if a compile-time feature is enabled.
Definition: version.c:500
void mutt_aliaslist_free(struct AliasList *a_list)
Free a List of Aliases.
Definition: alias.c:758
#define CSR_RESULT(x)
Definition: set.h:62
Definitions of user variables, sort methods and commands.
static enum CommandResult parse_source(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;source&#39; command - Implements command_t.
Definition: init.c:1944
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:719
Window management.
Error: Can&#39;t help the user.
Definition: mutt_commands.h:35
int mx_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Wrapper for MxOps::ac_add()
Definition: mx.c:1613
int mx_path_canon2(struct Mailbox *m, const char *folder)
XXX canonicalise the path to realpath.
Definition: mx.c:1447
Structs that make up an email.
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer&#39;s contents to another Buffer.
Definition: buffer.c:445
static enum CommandResult parse_finish(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;finish&#39; command - Implements command_t.
Definition: init.c:1118
The "currently-open" mailbox.
static enum CommandResult parse_stailq(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse a list command - Implements command_t.
Definition: init.c:2051
struct Hash * TagTransforms
Lookup table of alternative tag names.
Definition: tags.c:38
#define MUTT_NOSPAM
Definition: mutt.h:119
static char * getmailname(void)
Try to retrieve the FQDN from mailname files.
Definition: init.c:378
int cs_str_string_set(const struct ConfigSet *cs, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition: set.c:627
static int NumMatched
Definition: init.c:90
void mutt_grouplist_clear(struct GroupList *gl)
Clear a GroupList.
Definition: group.c:104
struct ListHead InlineAllow
List of inline types to counted.
Definition: mutt_parse.c:41
MenuType
Types of GUI selections.
Definition: keymap.h:69
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: init.c:3359
#define mutt_message(...)
Definition: logging.h:83
int mutt_label_complete(char *buf, size_t buflen, int numtabs)
Complete a label name.
Definition: init.c:3555
static struct ListHead MuttrcStack
Definition: init.c:79
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:134
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1381
static enum CommandResult parse_replace_list(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse a string replacement rule - Implements command_t.
Definition: init.c:603
struct ReplaceList SpamList
List of regexes and patterns to match spam emails.
Definition: email_globals.c:44
static void clear_subject_mods(void)
Clear out all modified email subjects.
Definition: init.c:223
char * disp_subj
Display subject (modified copy of subject)
Definition: envelope.h:68
static enum CommandResult parse_attachments(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;attachments&#39; command - Implements command_t.
Definition: init.c:1033
WHERE bool OptMenuCaller
(pseudo) tell menu to give caller a take
Definition: options.h:38
static enum CommandResult parse_unsubjectrx_list(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unsubjectrx&#39; command - Implements command_t.
Definition: init.c:2558
static enum CommandResult parse_subscribe_to(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;subscribe-to&#39; command - Implements command_t.
Definition: init.c:2119
Pass files through external commands (filters)
int mutt_monitor_remove(struct Mailbox *m)
Remove a watch for a mailbox.
Definition: monitor.c:521
struct Notify * notify
Notifications handler.
Definition: account.h:42
void mutt_list_free_type(struct ListHead *h, list_free_t fn)
Free a List of type.
Definition: list.c:145
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:95
Cursor to iterate through a Hash Table.
Definition: hash.h:96
int log_disp_terminal(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Save a log line to the terminal - Implements log_dispatcher_t.
Definition: logging.c:450
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
void number_init(struct ConfigSet *cs)
Register the Number config type.
Definition: number.c:192
command_t func
Function to parse the command.
Definition: mutt_commands.h:57
Match any Mailbox type.
Definition: mailbox.h:44
void mutt_grouplist_add(struct GroupList *gl, struct Group *group)
Add a Group to a GroupList.
Definition: group.c:138
WHERE char AttachmentMarker[256]
Unique ANSI string to mark PGP messages in an email.
Definition: globals.h:47
#define MUTT_TOKEN_COMMENT
Don&#39;t reap comments.
Definition: mutt.h:82
void * mutt_hash_find(const struct Hash *table, const char *strkey)
Find the HashElem data in a Hash table element using a key.
Definition: hash.c:378
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
A group of associated Mailboxes.
Definition: account.h:36
HookFlags mutt_get_hook_type(const char *name)
Find a hook by name.
Definition: init.c:2976
WHERE enum MenuType CurrentMenu
Current Menu, e.g. MENU_PAGER.
Definition: globals.h:83
bool mutt_nm_tag_complete(char *buf, size_t buflen, int numtabs)
Complete to the nearest notmuch tag.
Definition: init.c:3676
struct ListHead MailToAllow
List of permitted fields in a mailto: uri.
Definition: email_globals.c:47
String manipulation buffer.
Definition: buffer.h:33
static int execute_commands(struct ListHead *p)
Execute a set of NeoMutt commands.
Definition: init.c:302
static void alternates_clean(void)
Clear the recipient valid flag of all emails.
Definition: init.c:139
static int source_rc(const char *rcfile_path, struct Buffer *err)
Read an initialization file.
Definition: init.c:779
Url wasn&#39;t recognised.
Definition: url.h:34
WHERE struct Hash * TagFormats
Hash table of tag-formats (tag -> format string)
Definition: globals.h:61
enum ContentType major_int
Definition: mutt_parse.h:38
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:46
WHERE struct ConfigSet * Config
Wrapper around the user&#39;s config settings.
Definition: globals.h:40
void mutt_delete_hooks(HookFlags type)
Delete matching hooks.
Definition: hook.c:302
int mutt_command_complete(char *buf, size_t buflen, int pos, int numtabs)
Complete a command name.
Definition: init.c:3385
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:664
bool mutt_ch_check_charset(const char *cs, bool strict)
Does iconv understand a character set?
Definition: charset.c:812
static enum CommandResult parse_alias(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;alias&#39; command - Implements command_t.
Definition: init.c:914
static enum CommandResult parse_alternates(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;alternates&#39; command - Implements command_t.
Definition: init.c:999
An email address.
Definition: address.h:34
struct Mailbox * mx_mbox_find(struct Account *a, const char *path)
XXX.
Definition: mx.c:1550
static char ** nm_tags
Definition: init.c:98
WHERE bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:46
A user-callable command.
Definition: mutt_commands.h:54
WHERE char * LastFolder
Previously selected mailbox.
Definition: globals.h:56
const char * name
Name of the command.
Definition: mutt_commands.h:56
char * mailbox
Mailbox and host address.
Definition: address.h:37
void sort_init(struct ConfigSet *cs)
Register the Sort config type.
Definition: sort.c:381
static enum CommandResult parse_unmy_hdr(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unmy_hdr&#39; command - Implements command_t.
Definition: init.c:2500
static enum CommandResult parse_lists(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;lists&#39; command - Implements command_t.
Definition: init.c:1307
Match case when comparing strings.
Definition: string2.h:67
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
Index panel (list of emails)
Definition: keymap.h:77
void cs_subset_free(struct ConfigSubset **ptr)
Free a Config Subset.
Definition: subset.c:44
A Command has been executed.
Definition: notify_type.h:40
#define STAILQ_REMOVE_HEAD(head, field)
Definition: queue.h:420
void mutt_ch_set_charset(const char *charset)
Update the records for a new character set.
Definition: charset.c:997
static enum CommandResult parse_my_hdr(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;my_hdr&#39; command - Implements command_t.
Definition: init.c:1435
void mutt_grouplist_add_addrlist(struct GroupList *gl, struct AddressList *al)
Add Address list to a GroupList.
Definition: group.c:227
struct HashElem ** get_elem_list(struct ConfigSet *cs)
Create a sorted list of all config items.
Definition: dump.c:118
struct ListHead UnIgnore
List of header patterns to unignore (see)
Definition: email_globals.c:46
bool mutt_nm_query_complete(char *buf, size_t buflen, int pos, int numtabs)
Complete to the nearest notmuch tag.
Definition: init.c:3619
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int bool_he_toggle(struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:210
uint32_t HookFlags
Flags for mutt_parse_hook(), e.g. MUTT_FOLDER_HOOK.
Definition: hook.h:43
void log_queue_flush(log_dispatcher_t disp)
Replay the log queue.
Definition: logging.c:357
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:606
int mutt_monitor_add(struct Mailbox *m)
Add a watch for a mailbox.
Definition: monitor.c:476
char * name
Name of user variable.
Definition: myvar.h:33
Config item definition.
Definition: set.h:153
static enum CommandResult parse_unalias(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unalias&#39; command - Implements command_t.
Definition: init.c:2240
void enum_init(struct ConfigSet *cs)
Register the Enumeration config type.
Definition: enum.c:218
static char * find_cfg(const char *home, const char *xdg_cfg_home)
Find a config file.
Definition: init.c:337
struct Account * mx_ac_find(struct Mailbox *m)
XXX.
Definition: mx.c:1527
int getdnsdomainname(char *buf, size_t buflen)
Lookup the host&#39;s name using DNS.
Definition: getdomain.c:45
#define DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:36
#define MUTT_CONT
-continuation
Definition: file.h:38
void mutt_replacelist_free(struct ReplaceList *rl)
Free a ReplaceList object.
Definition: regex.c:447
Container for Accounts, Notifications.
Definition: neomutt.h:35
enum UrlScheme url_check_scheme(const char *s)
Check the protocol of a URL.
Definition: url.c:132
const struct Command * mutt_command_get(const char *s)
Get a Command by its name.
Definition: init.c:2643
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:43
Representation of a single alias to an email address.
int charset_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Validate the "charset" config variable - Implements cs_validator()
Definition: init.c:3826
#define MAX_ERRS
Definition: init.c:81
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
static enum CommandResult parse_ignore(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;ignore&#39; command - Implements command_t.
Definition: init.c:1291
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1048
Email Address Handling.
WHERE char * C_ConfigCharset
Config: Character set that the config files are in.
Definition: globals.h:105
Ask the user, defaulting to &#39;Yes&#39;.
Definition: quad.h:41
struct Hash * AutoSubscribeCache
Hash table of auto-subscribed mailing lists.
Definition: email_globals.c:48
#define MAX(a, b)
Definition: memory.h:30
int cs_str_initial_set(const struct ConfigSet *cs, const char *name, const char *value, struct Buffer *err)
Set the initial value of a config item.
Definition: set.c:487
#define mutt_array_size(x)
Definition: memory.h:33
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition: hash.h:75
const struct Command Commands[]
Definition: init.h:5002
Pager pager (email viewer)
Definition: keymap.h:78
int mutt_query_variables(struct ListHead *queries)
Implement the -Q command line flag.
Definition: init.c:3308
size_t dsize
Length of data.
Definition: buffer.h:37
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:134
#define MoreArgs(buf)
Definition: buffer.h:43
void mutt_hist_read_file(void)
Read the History from a file.
Definition: history.c:584
char * name
A short name for the Mailbox.
Definition: mailbox.h:96
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1546
int mutt_grouplist_add_regex(struct GroupList *gl, const char *s, int flags, struct Buffer *err)
Add matching Addresses to a GroupList.
Definition: group.c:277
bool del
Is it deleted?
Definition: alias.h:42
void dump_config_neo(struct ConfigSet *cs, struct HashElem *he, struct Buffer *value, struct Buffer *initial, ConfigDumpFlags flags, FILE *fp)
Dump the config in the style of NeoMutt.
Definition: dump.c:150
static enum CommandResult parse_unmailboxes(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unmailboxes&#39; command - Implements command_t.
Definition: init.c:2434
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:375
struct Mailbox * mailbox
Definition: context.h:50
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
static enum CommandResult parse_unattachments(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unattachments&#39; command - Implements command_t.
Definition: init.c:2331
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:59
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:319
enum MailboxType magic
Mailbox type.
Definition: mailbox.h:116
enum CommandResult mutt_parse_hook(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;hook&#39; family of commands - Implements command_t.
Definition: hook.c:85
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:873
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:95
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
const char * name
Definition: pgpmicalg.c:46
#define MUTT_SPAM
Definition: mutt.h:118
struct MailboxList neomutt_mailboxlist_get_all(struct NeoMutt *n, enum MailboxType magic)
Get a List of all Mailboxes.
Definition: neomutt.c:156
IMAP network mailbox.
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
A user-set variable.
Definition: myvar.h:31
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:52
struct MyVarList MyVars
List of all the user&#39;s custom config variables.
Definition: myvar.c:34
WHERE char * HomeDir
User&#39;s home directory.
Definition: globals.h:50
const char * name
name of the function
Definition: keymap.h:116
int cs_he_reset(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
Reset a config item to its initial value.
Definition: set.c:375
int cs_str_string_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
Get a config item as a string.
Definition: set.c:698
struct Alias * mutt_alias_new(void)
Create a new Alias.
Definition: alias.c:144
#define SKIPWS(ch)
Definition: string2.h:47
int mutt_grouplist_remove_regex(struct GroupList *gl, const char *s)
Remove matching addresses from a GroupList.
Definition: group.c:301
void mbtable_init(struct ConfigSet *cs)
Register the MbTable config type.
Definition: mbtable.c:293
default is to invert all vars
Definition: init.h:107
static enum CommandResult parse_tag_formats(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;tag-formats&#39; command - Implements command_t.
Definition: init.c:2162
int mutt_regexlist_add(struct RegexList *rl, const char *str, int flags, struct Buffer *err)
Compile a regex string and add it to a list.
Definition: regex.c:132
static enum CommandResult parse_unignore(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unignore&#39; command - Implements command_t.
Definition: init.c:2389
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
int cs_he_native_set(const struct ConfigSet *cs, struct HashElem *he, intptr_t value, struct Buffer *err)
Natively set the value of a HashElem config item.
Definition: set.c:721
const char * major
Definition: mutt_parse.h:37
static char Completed[256]
Definition: init.c:91
#define MUTT_TOKEN_NOSHELL
Don&#39;t expand environment variables.
Definition: mutt.h:85
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:55
void slist_init(struct ConfigSet *cs)
Register the Slist config type.
Definition: slist.c:267
int hcache_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Validate the "header_cache_backend" config variable - Implements cs_validator()
Definition: init.c:3866
const char * name
User-visible name.
Definition: set.h:155
default is to set all vars
Definition: init.h:106
struct HashElem * cs_get_elem(const struct ConfigSet *cs, const char *name)
Get the HashElem representing a config item.
Definition: set.c:216
static enum CommandResult parse_unstailq(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse an unlist command - Implements command_t.
Definition: init.c:2537
static enum CommandResult parse_mailboxes(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;mailboxes&#39; command - Implements command_t.
Definition: init.c:1341
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:360
void myvar_del(const char *var)
Unset the value of a "my_" variable.
Definition: myvar.c:112
struct Account * account_new(const char *name, struct ConfigSubset *sub)
Create a new Account.
Definition: account.c:42
int mutt_grouplist_remove_addrlist(struct GroupList *gl, struct AddressList *al)
Remove an AddressList from a GroupList.
Definition: group.c:246
int flags
e.g. MB_NORMAL
Definition: mailbox.h:145
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:46
static enum CommandResult parse_setenv(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;setenv&#39; and &#39;unsetenv&#39; commands - Implements command_t.
Definition: init.c:1861
int(* sort_t)(const void *a, const void *b)
typedef sort_t - Prototype for a function to compare two emails
Definition: sort.h:48
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:821
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:65
#define mutt_b2s(buf)
Definition: buffer.h:41
#define MUTT_TOKEN_QUOTE
Don&#39;t interpret quotes.
Definition: mutt.h:80
static void attachments_clean(void)
always wise to do what someone else did before
Definition: init.c:157
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: nm_db.c:304
WHERE bool OptForceRefresh
(pseudo) refresh even during macros
Definition: options.h:35
Prototypes for many functions.
#define MUTT_TOKEN_CONDENSE
^(char) to control chars (macros)
Definition: mutt.h:78
const struct Mapping Menus[]
Menu name lookup table.
Definition: keymap.c:62
static int parse_grouplist(struct GroupList *gl, struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse a group context.
Definition: init.c:573
const char * line
Definition: common.c:36
#define MB_HIDDEN
Definition: mailbox.h:37
struct ListHead InlineExclude
List of inline types to ignore.
Definition: mutt_parse.c:42
int mutt_str_strncasecmp(const char *a, const char *b, size_t l)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:656
static enum CommandResult parse_unlists(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unlists&#39; command - Implements command_t.
Definition: init.c:2409
short C_DebugLevel
Config: Logging level for debug logs.
Definition: mutt_logging.c:50
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:77
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:544
#define CSR_ERR_INVALID
Value hasn&#39;t been set.
Definition: set.h:49
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:120
static int MatchesListsize
Definition: init.c:94
void mutt_grouplist_init(void)
Initialize the GroupList singleton.
Definition: group.c:45
static enum CommandResult parse_path_list(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;sidebar_whitelist&#39; command - Implements command_t.
Definition: init.c:1480
static enum CommandResult parse_subjectrx_list(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;subjectrx&#39; command - Implements command_t.
Definition: init.c:2066
void mutt_keys_free(void)
Free the key maps.
Definition: keymap.c:1601
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
static void matches_ensure_morespace(int current)
Allocate more space for auto-completion.
Definition: init.c:176
A mailbox.
Definition: mailbox.h:92
#define PATH_MAX
Definition: mutt.h:50
enum MailboxType magic
Type of Mailboxes this Account contains.
Definition: account.h:38
const struct Binding OpGeneric[]
Key bindings for the generic menu.
Definition: functions.h:54
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
bool mutt_envlist_unset(const char *name)
Unset an environment variable.
Definition: envlist.c:132
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:2674
static enum CommandResult parse_set(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;set&#39; family of commands - Implements command_t.
Definition: init.c:1529
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define DT_MAILBOX
Don&#39;t perform path expansions.
Definition: types.h:47
#define CS_DUMP_NO_FLAGS
No flags are set.
Definition: dump.h:35
static void mutt_attachmatch_free(struct AttachMatch **ptr)
Free an AttachMatch - Implements list_free_t.
Definition: init.c:2317
bool mutt_path_to_absolute(char *path, const char *reference)
Convert relative filepath to an absolute path.
Definition: path.c:375
void mutt_alias_free(struct Alias **ptr)
Free an Alias.
Definition: alias.c:741
int pager_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Check for config variables that can&#39;t be set from the pager - Implements cs_validator() ...
Definition: init.c:3885
char * dptr
Current read/write position.
Definition: buffer.h:36
#define MUTT_TOKEN_SPACE
Don&#39;t treat whitespace as a term.
Definition: mutt.h:79
struct RegexList MailLists
List of regexes to match mailing lists.
Definition: email_globals.c:50
char * data
Pointer to data.
Definition: buffer.h:35
static enum CommandResult parse_unsubscribe(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unsubscribe&#39; command - Implements command_t.
Definition: init.c:2572
#define MUTT_VIRTUAL
Definition: init.h:114
#define NUM_VARS
Definition: init.c:83
#define MUTT_NAMED
Definition: init.h:113
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:275
void address_init(struct ConfigSet *cs)
Register the Address config type.
Definition: address.c:249
bool attach_valid
true when the attachment count is valid
Definition: email.h:70
int reply_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Validate the "reply_regex" config variable - Implements cs_validator()
Definition: init.c:3919
GUI present the user with a selectable list.
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:803
static enum CommandResult parse_group(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;group&#39; and &#39;ungroup&#39; commands - Implements command_t.
Definition: init.c:1133
void myvar_set(const char *var, const char *val)
Set the value of a "my_" variable.
Definition: myvar.c:91
Ignore case when comparing strings.
Definition: string2.h:68
Group is missing an argument.
Definition: init.c:106
struct RegexList SubscribedLists
List of regexes to match subscribed mailing lists.
Definition: email_globals.c:52
void string_init(struct ConfigSet *cs)
Register the String config type.
Definition: string.c:240
char * mutt_ch_get_langinfo_charset(void)
Get the user&#39;s choice of character set.
Definition: charset.c:456
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:46
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:628
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct Notify * notify
Notifications handler.
Definition: neomutt.h:37
Display version and copyright about NeoMutt.
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
bool mutt_hcache_is_valid_backend(const char *s)
Is the string a valid hcache backend.
Definition: hcache.c:496
Definition: color.h:130
void mutt_commands_apply(void *data, void(*application)(void *, const struct Command *))
void mutt_hist_init(void)
Create a set of empty History ring buffers.
Definition: history.c:462
void mutt_opts_free(void)
clean up before quitting
Definition: init.c:2909
Select an email address by its alias.
Definition: keymap.h:71
default is to reset all vars to default
Definition: init.h:109
void cs_free(struct ConfigSet **ptr)
Free a Config Set.
Definition: set.c:198
struct ConfigSet * cs_new(size_t size)
Create a new Config Set.
Definition: set.c:171
static enum CommandResult parse_ifdef(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;ifdef&#39; and &#39;ifndef&#39; commands - Implements command_t.
Definition: init.c:1250
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: muttlib.c:556
enum CommandResult mutt_parse_idxfmt_hook(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;index-format-hook&#39; command - Implements command_t.
Definition: hook.c:349
int nm_get_all_tags(struct Mailbox *m, char **tag_list, int *tag_count)
Fill a list with all notmuch tags.
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
int mutt_set_xdg_path(enum XdgType type, char *buf, size_t bufsize)
Find an XDG path or its fallback.
Definition: muttlib.c:1617
Header cache multiplexor.
const char * myvar_get(const char *var)
Get the value of a "my_" variable.
Definition: myvar.c:73
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:53
void notify_free(struct Notify **ptr)
Free a notification handler.
Definition: notify.c:69
WHERE char ProtectedHeaderMarker[256]
Unique ANSI string to mark protected headers in an email.
Definition: globals.h:48
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
int multipart_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Validate the "show_multipart_alternative" config variable - Implements cs_validator() ...
Definition: init.c:3901
int cs_he_string_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
Get a config item as a string.
Definition: set.c:650
void long_init(struct ConfigSet *cs)
Register the Long config type.
Definition: long.c:173
int cs_str_reset(const struct ConfigSet *cs, const char *name, struct Buffer *err)
Reset a config item to its initial value.
Definition: set.c:423
#define MUTT_TOKEN_SEMICOLON
Don&#39;t treat ; as special.
Definition: mutt.h:83
#define IS_SPACE(ch)
Definition: string2.h:38
WHERE struct Hash * ReverseAliases
Hash table of aliases (email address -> alias)
Definition: globals.h:60
void mutt_colors_free(struct Colors **ptr)
Free all the colours.
Definition: color.c:355
int mutt_any_key_to_continue(const char *s)
Prompt the user to &#39;press any key&#39; and wait.
Definition: curs_lib.c:577
size_t pretty_var(const char *str, struct Buffer *buf)
Escape and stringify a config item value.
Definition: dump.c:80
struct ListHead AttachExclude
List of attachment types to be ignored.
Definition: mutt_parse.c:40
static int complete_all_nm_tags(const char *pt)
Pass a list of Notmuch tags to the completion code.
Definition: init.c:245
struct Mailbox * mailbox_new(void)
Create a new Mailbox.
Definition: mailbox.c:41
struct HashElem * mutt_hash_walk(const struct Hash *table, struct HashWalkState *state)
Iterate through all the HashElem&#39;s in a Hash table.
Definition: hash.c:503
default is to unset all vars
Definition: init.h:108
Success: Command worked.
Definition: mutt_commands.h:37
char * data
String.
Definition: list.h:35
GroupState
Type of email address group.
Definition: init.c:104
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:343
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:142
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1216
WHERE bool C_Suspend
Config: Allow the user to suspend NeoMutt using &#39;^Z&#39;.
Definition: globals.h:264
Log at debug level 1.
Definition: logging.h:40
bool group
Group mailbox?
Definition: address.h:38
Warning: Help given to the user.
Definition: mutt_commands.h:36
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user&#39;s real name in /etc/passwd.
Definition: muttlib.c:382
static char UserTyped[1024]
Definition: init.c:88
int quad_he_toggle(struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
Toggle the value of a quad.
Definition: quad.c:226
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
int mutt_replacelist_remove(struct ReplaceList *rl, const char *pat)
Remove a pattern from a list.
Definition: regex.c:564
Entry is an address.
Definition: init.c:108
Finish: Stop processing this file.
Definition: mutt_commands.h:38
struct ConfigDef MuttVars[]
Definition: init.h:135
bool account_mailbox_remove(struct Account *a, struct Mailbox *m)
Remove a Mailbox from an Account.
Definition: account.c:84
regex_t minor_regex
Definition: mutt_parse.h:40
const char * strkey
Definition: hash.h:35
#define mutt_error(...)
Definition: logging.h:84
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:55
static enum CommandResult parse_attach_list(struct Buffer *buf, struct Buffer *s, struct ListHead *head, struct Buffer *err)
Parse the "attachments" command.
Definition: init.c:496
static const char ** Matches
Definition: init.c:92
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:628
void regex_init(struct ConfigSet *cs)
Register the Regex config type.
Definition: regex.c:315
static int print_attach_list(struct ListHead *h, const char op, const char *name)
Print a list of attachments.
Definition: init.c:733
const struct Binding * km_get_table(enum MenuType menu)
Lookup a menu&#39;s keybindings.
Definition: keymap.c:1223
void mutt_alias_add_reverse(struct Alias *t)
Add an email address lookup for an Alias.
Definition: alias.c:553
#define MUTT_TOKEN_BACKTICK_VARS
Expand variables within backticks.
Definition: mutt.h:84
void mutt_grouplist_destroy(struct GroupList *gl)
Free a GroupList.
Definition: group.c:158
int type
Definition: hash.h:44
WHERE char * ShortHostname
Short version of the hostname.
Definition: globals.h:51
#define FREE(x)
Definition: memory.h:40
WHERE char * C_Hostname
Config: Fully-qualified domain name of this machine.
Definition: globals.h:112
struct ConfigSubset * sub
Inherited config items.
Definition: account.h:40
#define NUM_COMMANDS
Definition: init.c:84
#define MUTT_HOOK_NO_FLAGS
No flags are set.
Definition: hook.h:44
Monitor files for changes.
#define STAILQ_EMPTY(head)
Definition: queue.h:346
int wrapheaders_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Validate the "wrap_headers" config variable - Implements cs_validator()
Definition: init.c:3936
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:322
static enum CommandResult parse_unsubscribe_from(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;unsubscribe-from&#39; command - Implements command_t.
Definition: init.c:2599
bool recip_valid
Is_recipient is valid.
Definition: email.h:58
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:54
static enum CommandResult parse_subscribe(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;subscribe&#39; command - Implements command_t.
Definition: init.c:2080
struct RegexList UnMailLists
List of regexes to blacklist false matches in MailLists.
Definition: email_globals.c:51
bool mutt_envlist_set(const char *name, const char *value, bool overwrite)
Set an environment variable.
Definition: envlist.c:85
The item stored in a Hash Table.
Definition: hash.h:42
struct MuttWindow * MuttMessageWindow
Message Window.
Definition: mutt_window.c:42
#define MUTT_TOKEN_EQUAL
Treat &#39;=&#39; as a special.
Definition: mutt.h:77
Entry is a regular expression.
Definition: init.c:107
static enum CommandResult parse_unreplace_list(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Remove a string replacement rule - Implements command_t.
Definition: init.c:701
bool cs_register_variables(const struct ConfigSet *cs, struct ConfigDef vars[], int flags)
Register a set of config items.
Definition: set.c:289
List of Mailboxes.
Definition: mailbox.h:156
WHERE char * C_Realname
Config: Real name of the user.
Definition: globals.h:139
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: nm_db.c:289
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:38
#define IS_COMMAND(x)
Definition: types.h:56
uint16_t TokenFlags
Flags for mutt_extract_token(), e.g. MUTT_TOKEN_EQUAL.
Definition: mutt.h:75
void mutt_grouplist_free(void)
Free GroupList singleton resource.
Definition: group.c:55
#define TAILQ_EMPTY(head)
Definition: queue.h:715
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
int mutt_init(bool skip_sys_rc, struct ListHead *commands)
Initialise NeoMutt.
Definition: init.c:2996
bool neomutt_account_add(struct NeoMutt *n, struct Account *a)
Add an Account to the global list.
Definition: neomutt.c:83
static enum CommandResult parse_echo(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;echo&#39; command - Implements command_t.
Definition: init.c:1094
An attachment matching a regex for attachment counter.
Definition: mutt_parse.h:35
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition: hash.h:76
struct AddressList addr
List of Addresses the Alias expands to.
Definition: alias.h:40
Log at debug level 5.
Definition: logging.h:44
struct Buffer pathbuf
Definition: mailbox.h:94
void mutt_hash_free(struct Hash **ptr)
Free a hash table.
Definition: hash.c:471
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:585
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:351
int mutt_wait_filter(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:220
A List node for strings.
Definition: list.h:33
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1404
static bool get_hostname(void)
Find the Fully-Qualified Domain Name.
Definition: init.c:410
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:631
#define MB_NORMAL
Definition: mailbox.h:36
XDG system dir: /etc/xdg.
Definition: protos.h:50
Handling of personal config (&#39;my&#39; variables)
struct ListHead AttachAllow
List of attachment types to be counted.
Definition: mutt_parse.c:39
static enum CommandResult parse_spam_list(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;spam&#39; and &#39;nospam&#39; commands - Implements command_t.
Definition: init.c:1973
char * mutt_str_substr_dup(const char *begin, const char *end)
Duplicate a sub-string.
Definition: string.c:579
bool notify_send(struct Notify *notify, int type, int subtype, intptr_t data)
Send out a notification message.
Definition: notify.c:145
#define MUTT_TOKEN_PATTERN
~%=!| are terms (for patterns)
Definition: mutt.h:81
struct RegexList NoSpamList
List of regexes to whitelist non-spam emails.
Definition: email_globals.c:43
const char * minor
Definition: mutt_parse.h:39
static enum CommandResult parse_tag_transforms(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;tag-transforms&#39; command - Implements command_t.
Definition: init.c:2201
#define STAILQ_FIRST(head)
Definition: queue.h:348
void mutt_hist_free(void)
Free all the history lists.
Definition: history.c:439
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:74
Convenience wrapper for the library headers.
struct ListHead Ignore
List of header patterns to ignore.
Definition: email_globals.c:45
Mapping between a user key and a function.
Definition: keymap.h:114
#define DT_NUMBER
a number
Definition: types.h:35
struct Group * mutt_pattern_group(const char *pat)
Match a pattern to a Group.
Definition: group.c:66
#define DT_BOOL
boolean option
Definition: types.h:30
struct Hash * label_hash
Hash table for x-labels.
Definition: mailbox.h:140
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
void mutt_regexlist_free(struct RegexList *rl)
Free a RegexList object.
Definition: regex.c:170
Log at debug level 3.
Definition: logging.h:42
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
static void candidate(char *user, const char *src, char *dest, size_t dlen)
helper function for completion
Definition: init.c:199
enum CommandResult mutt_parse_rc_line(char *line, struct Buffer *token, struct Buffer *err)
Parse a line of user config.
Definition: init.c:3251
static void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: init.c:118
const char * mutt_str_rstrnstr(const char *haystack, size_t haystack_length, const char *needle)
Find last instance of a substring.
Definition: string.c:938
int cs_he_string_set(const struct ConfigSet *cs, struct HashElem *he, const char *value, struct Buffer *err)
Set a config item by string.
Definition: set.c:576
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:76
#define MUTT_TOKEN_QUESTION
Treat &#39;?&#39; as a special.
Definition: mutt.h:86
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1391
ContentType
Content-Type.
Definition: mime.h:29
char ** mutt_envlist_getlist(void)
Get the private environment.
Definition: envlist.c:169
void(* list_free_t)(void **ptr)
typedef list_free_t - Prototype for a function to free List data
Definition: list.h:44
WHERE bool C_VirtualSpoolfile
Config: (notmuch) Use the first virtual mailbox as a spool file.
Definition: globals.h:295
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:158
#define MUTT_GROUP
&#39;group&#39; config command
Definition: group.h:32
struct RegexList UnSubscribedLists
List of regexes to blacklist false matches in SubscribedLists.
Definition: email_globals.c:49
struct ReplaceList SubjectRegexList
List of regexes to tidy the view of the email&#39;s subject.
Definition: email_globals.c:53
int mutt_var_value_complete(char *buf, size_t buflen, int pos)
Complete a variable/value.
Definition: init.c:3729