NeoMutt  2018-07-16 +2481-68dcde
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 <time.h>
44 #include <unistd.h>
45 #include <wchar.h>
46 #include "mutt/mutt.h"
47 #include "address/lib.h"
48 #include "email/lib.h"
49 #include "core/lib.h"
50 #include "mutt.h"
51 #include "init.h"
52 #include "alias.h"
53 #include "context.h"
54 #include "filter.h"
55 #include "hcache/hcache.h"
56 #include "keymap.h"
57 #include "monitor.h"
58 #include "mutt_curses.h"
59 #include "mutt_menu.h"
60 #include "mutt_window.h"
61 #include "mx.h"
62 #include "myvar.h"
63 #include "options.h"
64 #include "protos.h"
65 #include "sidebar.h"
66 #include "version.h"
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)
142  return;
143 
144  for (int i = 0; i < Context->mailbox->msg_count; i++)
145  Context->mailbox->emails[i]->recip_valid = false;
146 }
147 
151 static void attachments_clean(void)
152 {
153  if (!Context)
154  return;
155 
156  for (int i = 0; i < Context->mailbox->msg_count; i++)
157  Context->mailbox->emails[i]->attach_valid = false;
158 }
159 
164 static void matches_ensure_morespace(int current)
165 {
166  if (current <= (MatchesListsize - 2))
167  return;
168 
169  int base_space = MAX(NUM_VARS, NUM_COMMANDS) + 1;
170  int extra_space = MatchesListsize - base_space;
171  extra_space *= 2;
172  const int space = base_space + extra_space;
173  mutt_mem_realloc(&Matches, space * sizeof(char *));
174  memset(&Matches[current + 1], 0, space - current);
175  MatchesListsize = space;
176 }
177 
187 static void candidate(char *user, const char *src, char *dest, size_t dlen)
188 {
189  if (!dest || !user || !src)
190  return;
191 
192  if (strstr(src, user) != src)
193  return;
194 
196  Matches[NumMatched++] = src;
197  if (dest[0] == '\0')
198  mutt_str_strfcpy(dest, src, dlen);
199  else
200  {
201  int l;
202  for (l = 0; src[l] && src[l] == dest[l]; l++)
203  ;
204  dest[l] = '\0';
205  }
206 }
207 
211 static void clear_subject_mods(void)
212 {
213  if (!Context)
214  return;
215 
216  for (int i = 0; i < Context->mailbox->msg_count; i++)
218 }
219 
220 #ifdef USE_NOTMUCH
221 
227 static int complete_all_nm_tags(const char *pt)
228 {
229  int tag_count_1 = 0;
230  int tag_count_2 = 0;
231 
232  NumMatched = 0;
233  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
234  memset(Matches, 0, MatchesListsize);
235  memset(Completed, 0, sizeof(Completed));
236 
238 
239  /* Work out how many tags there are. */
240  if (nm_get_all_tags(Context->mailbox, NULL, &tag_count_1) || (tag_count_1 == 0))
241  goto done;
242 
243  /* Free the old list, if any. */
244  if (nm_tags)
245  {
246  for (int i = 0; nm_tags[i]; i++)
247  FREE(&nm_tags[i]);
248  FREE(&nm_tags);
249  }
250  /* Allocate a new list, with sentinel. */
251  nm_tags = mutt_mem_malloc((tag_count_1 + 1) * sizeof(char *));
252  nm_tags[tag_count_1] = NULL;
253 
254  /* Get all the tags. */
255  if (nm_get_all_tags(Context->mailbox, nm_tags, &tag_count_2) || (tag_count_1 != tag_count_2))
256  {
257  FREE(&nm_tags);
258  nm_tags = NULL;
260  return -1;
261  }
262 
263  /* Put them into the completion machinery. */
264  for (int num = 0; num < tag_count_1; num++)
265  {
266  candidate(UserTyped, nm_tags[num], Completed, sizeof(Completed));
267  }
268 
271 
272 done:
274  return 0;
275 }
276 #endif
277 
284 static int execute_commands(struct ListHead *p)
285 {
286  int rc = 0;
287  struct Buffer *err = mutt_buffer_pool_get();
288  struct Buffer *token = mutt_buffer_pool_get();
289 
290  struct ListNode *np = NULL;
291  STAILQ_FOREACH(np, p, entries)
292  {
293  if (mutt_parse_rc_line(np->data, token, err) == MUTT_CMD_ERROR)
294  {
295  mutt_error(_("Error in command line: %s"), mutt_b2s(err));
296  mutt_buffer_pool_release(&token);
298 
299  return -1;
300  }
301  }
302  mutt_buffer_pool_release(&token);
304 
305  return rc;
306 }
307 
315 static char *find_cfg(const char *home, const char *xdg_cfg_home)
316 {
317  const char *names[] = {
318  "neomuttrc",
319  "muttrc",
320  NULL,
321  };
322 
323  const char *locations[][2] = {
324  { xdg_cfg_home, "neomutt/" },
325  { xdg_cfg_home, "mutt/" },
326  { home, ".neomutt/" },
327  { home, ".mutt/" },
328  { home, "." },
329  { NULL, NULL },
330  };
331 
332  for (int i = 0; locations[i][0] || locations[i][1]; i++)
333  {
334  if (!locations[i][0])
335  continue;
336 
337  for (int j = 0; names[j]; j++)
338  {
339  char buf[256];
340 
341  snprintf(buf, sizeof(buf), "%s/%s%s", locations[i][0], locations[i][1], names[j]);
342  if (access(buf, F_OK) == 0)
343  return mutt_str_strdup(buf);
344  }
345  }
346 
347  return NULL;
348 }
349 
350 #ifndef DOMAIN
351 
356 static char *getmailname(void)
357 {
358  char *mailname = NULL;
359  static const char *mn_files[] = { "/etc/mailname", "/etc/mail/mailname" };
360 
361  for (size_t i = 0; i < mutt_array_size(mn_files); i++)
362  {
363  FILE *fp = mutt_file_fopen(mn_files[i], "r");
364  if (!fp)
365  continue;
366 
367  size_t len = 0;
368  mailname = mutt_file_read_line(NULL, &len, fp, NULL, 0);
369  mutt_file_fclose(&fp);
370  if (mailname && *mailname)
371  break;
372 
373  FREE(&mailname);
374  }
375 
376  return mailname;
377 }
378 #endif
379 
388 static bool get_hostname(void)
389 {
390  char *str = NULL;
391  struct utsname utsname;
392 
393  if (C_Hostname)
394  {
395  str = C_Hostname;
396  }
397  else
398  {
399  /* The call to uname() shouldn't fail, but if it does, the system is horribly
400  * broken, and the system's networking configuration is in an unreliable
401  * state. We should bail. */
402  if ((uname(&utsname)) == -1)
403  {
404  mutt_perror(_("unable to determine nodename via uname()"));
405  return false; // TEST09: can't test
406  }
407 
408  str = utsname.nodename;
409  }
410 
411  /* some systems report the FQDN instead of just the hostname */
412  char *dot = strchr(str, '.');
413  if (dot)
415  else
417 
418  if (!C_Hostname)
419  {
420  /* now get FQDN. Use configured domain first, DNS next, then uname */
421 #ifdef DOMAIN
422  /* we have a compile-time domain name, use that for C_Hostname */
423  C_Hostname =
425  sprintf((char *) C_Hostname, "%s.%s", NONULL(ShortHostname), DOMAIN);
426 #else
427  C_Hostname = getmailname();
428  if (!C_Hostname)
429  {
430  char buffer[1024];
431  if (getdnsdomainname(buffer, sizeof(buffer)) == 0)
432  {
433  C_Hostname = mutt_mem_malloc(mutt_str_strlen(buffer) +
435  sprintf((char *) C_Hostname, "%s.%s", NONULL(ShortHostname), buffer);
436  }
437  else
438  {
439  /* DNS failed, use the nodename. Whether or not the nodename had a '.'
440  * in it, we can use the nodename as the FQDN. On hosts where DNS is
441  * not being used, e.g. small network that relies on hosts files, a
442  * short host name is all that is required for SMTP to work correctly.
443  * It could be wrong, but we've done the best we can, at this point the
444  * onus is on the user to provide the correct hostname if the nodename
445  * won't work in their network. */
446  C_Hostname = mutt_str_strdup(utsname.nodename);
447  }
448  }
449 #endif
450  }
451  if (C_Hostname)
452  cs_str_initial_set(Config, "hostname", C_Hostname, NULL);
453 
454  return true;
455 }
456 
461 static struct AttachMatch *mutt_attachmatch_new(void)
462 {
463  return mutt_mem_calloc(1, sizeof(struct AttachMatch));
464 }
465 
474 static enum CommandResult parse_attach_list(struct Buffer *buf, struct Buffer *s,
475  struct ListHead *head, struct Buffer *err)
476 {
477  struct AttachMatch *a = NULL;
478  char *p = NULL;
479  char *tmpminor = NULL;
480  size_t len;
481  int ret;
482 
483  do
484  {
486 
487  if (!buf->data || (*buf->data == '\0'))
488  continue;
489 
490  a = mutt_attachmatch_new();
491 
492  /* some cheap hacks that I expect to remove */
493  if (mutt_str_strcasecmp(buf->data, "any") == 0)
494  a->major = mutt_str_strdup("*/.*");
495  else if (mutt_str_strcasecmp(buf->data, "none") == 0)
496  a->major = mutt_str_strdup("cheap_hack/this_should_never_match");
497  else
498  a->major = mutt_str_strdup(buf->data);
499 
500  p = strchr(a->major, '/');
501  if (p)
502  {
503  *p = '\0';
504  p++;
505  a->minor = p;
506  }
507  else
508  {
509  a->minor = "unknown";
510  }
511 
512  len = strlen(a->minor);
513  tmpminor = mutt_mem_malloc(len + 3);
514  strcpy(&tmpminor[1], a->minor);
515  tmpminor[0] = '^';
516  tmpminor[len + 1] = '$';
517  tmpminor[len + 2] = '\0';
518 
520  ret = REG_COMP(&a->minor_regex, tmpminor, REG_ICASE);
521 
522  FREE(&tmpminor);
523 
524  if (ret != 0)
525  {
526  regerror(ret, &a->minor_regex, err->data, err->dsize);
527  FREE(&a->major);
528  FREE(&a);
529  return MUTT_CMD_ERROR;
530  }
531 
532  mutt_debug(LL_DEBUG3, "added %s/%s [%d]\n", a->major, a->minor, a->major_int);
533 
534  mutt_list_insert_tail(head, (char *) a);
535  } while (MoreArgs(s));
536 
538  return MUTT_CMD_SUCCESS;
539 }
540 
551 static int parse_grouplist(struct GroupList *gl, struct Buffer *buf,
552  struct Buffer *s, unsigned long data, struct Buffer *err)
553 {
554  while (mutt_str_strcasecmp(buf->data, "-group") == 0)
555  {
556  if (!MoreArgs(s))
557  {
558  mutt_buffer_strcpy(err, _("-group: no group name"));
559  return -1;
560  }
561 
563 
565 
566  if (!MoreArgs(s))
567  {
568  mutt_buffer_strcpy(err, _("out of arguments"));
569  return -1;
570  }
571 
573  }
574 
575  return 0;
576 }
577 
581 static enum CommandResult parse_replace_list(struct Buffer *buf, struct Buffer *s,
582  unsigned long data, struct Buffer *err)
583 {
584  struct ReplaceList *list = (struct ReplaceList *) data;
585  struct Buffer templ = mutt_buffer_make(0);
586 
587  /* First token is a regex. */
588  if (!MoreArgs(s))
589  {
590  mutt_buffer_printf(err, _("%s: too few arguments"), "subjectrx");
591  return MUTT_CMD_WARNING;
592  }
594 
595  /* Second token is a replacement template */
596  if (!MoreArgs(s))
597  {
598  mutt_buffer_printf(err, _("%s: too few arguments"), "subjectrx");
599  return MUTT_CMD_WARNING;
600  }
602 
603  if (mutt_replacelist_add(list, buf->data, templ.data, err) != 0)
604  {
605  FREE(&templ.data);
606  return MUTT_CMD_ERROR;
607  }
608  FREE(&templ.data);
609 
610  return MUTT_CMD_SUCCESS;
611 }
612 
621 static enum CommandResult parse_unattach_list(struct Buffer *buf, struct Buffer *s,
622  struct ListHead *head, struct Buffer *err)
623 {
624  struct AttachMatch *a = NULL;
625  char *tmp = NULL;
626  char *minor = NULL;
627 
628  do
629  {
631  FREE(&tmp);
632 
633  if (mutt_str_strcasecmp(buf->data, "any") == 0)
634  tmp = mutt_str_strdup("*/.*");
635  else if (mutt_str_strcasecmp(buf->data, "none") == 0)
636  tmp = mutt_str_strdup("cheap_hack/this_should_never_match");
637  else
638  tmp = mutt_str_strdup(buf->data);
639 
640  minor = strchr(tmp, '/');
641  if (minor)
642  {
643  *minor = '\0';
644  minor++;
645  }
646  else
647  {
648  minor = "unknown";
649  }
650  const enum ContentType major = mutt_check_mime_type(tmp);
651 
652  struct ListNode *np = NULL, *tmp2 = NULL;
653  STAILQ_FOREACH_SAFE(np, head, entries, tmp2)
654  {
655  a = (struct AttachMatch *) np->data;
656  mutt_debug(LL_DEBUG3, "check %s/%s [%d] : %s/%s [%d]\n", a->major,
657  a->minor, a->major_int, tmp, minor, major);
658  if ((a->major_int == major) && (mutt_str_strcasecmp(minor, a->minor) == 0))
659  {
660  mutt_debug(LL_DEBUG3, "removed %s/%s [%d]\n", a->major, a->minor, a->major_int);
661  regfree(&a->minor_regex);
662  FREE(&a->major);
663  STAILQ_REMOVE(head, np, ListNode, entries);
664  FREE(&np->data);
665  FREE(&np);
666  }
667  }
668 
669  } while (MoreArgs(s));
670 
671  FREE(&tmp);
673  return MUTT_CMD_SUCCESS;
674 }
675 
679 static enum CommandResult parse_unreplace_list(struct Buffer *buf, struct Buffer *s,
680  unsigned long data, struct Buffer *err)
681 {
682  struct ReplaceList *list = (struct ReplaceList *) data;
683 
684  /* First token is a regex. */
685  if (!MoreArgs(s))
686  {
687  mutt_buffer_printf(err, _("%s: too few arguments"), "unsubjectrx");
688  return MUTT_CMD_WARNING;
689  }
690 
692 
693  /* "*" is a special case. */
694  if (mutt_str_strcmp(buf->data, "*") == 0)
695  {
696  mutt_replacelist_free(list);
697  return MUTT_CMD_SUCCESS;
698  }
699 
700  mutt_replacelist_remove(list, buf->data);
701  return MUTT_CMD_SUCCESS;
702 }
703 
711 static int print_attach_list(struct ListHead *h, const char op, const char *name)
712 {
713  struct ListNode *np = NULL;
714  STAILQ_FOREACH(np, h, entries)
715  {
716  printf("attachments %c%s %s/%s\n", op, name,
717  ((struct AttachMatch *) np->data)->major,
718  ((struct AttachMatch *) np->data)->minor);
719  }
720 
721  return 0;
722 }
723 
731 static void remove_from_stailq(struct ListHead *head, const char *str)
732 {
733  if (mutt_str_strcmp("*", str) == 0)
734  mutt_list_free(head); /* "unCMD *" means delete all current entries */
735  else
736  {
737  struct ListNode *np = NULL, *tmp = NULL;
738  STAILQ_FOREACH_SAFE(np, head, entries, tmp)
739  {
740  if (mutt_str_strcasecmp(str, np->data) == 0)
741  {
742  STAILQ_REMOVE(head, np, ListNode, entries);
743  FREE(&np->data);
744  FREE(&np);
745  break;
746  }
747  }
748  }
749 }
750 
757 static int source_rc(const char *rcfile_path, struct Buffer *err)
758 {
759  int line = 0, rc = 0, warnings = 0;
760  enum CommandResult line_rc;
761  struct Buffer token;
762  char *linebuf = NULL;
763  char *currentline = NULL;
764  char rcfile[PATH_MAX];
765  size_t buflen;
766 
767  pid_t pid;
768 
769  mutt_str_strfcpy(rcfile, rcfile_path, sizeof(rcfile));
770 
771  size_t rcfilelen = mutt_str_strlen(rcfile);
772  if (rcfilelen == 0)
773  return -1;
774 
775  bool ispipe = rcfile[rcfilelen - 1] == '|';
776 
777  if (!ispipe)
778  {
779  struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
780  if (!mutt_path_to_absolute(rcfile, np ? NONULL(np->data) : ""))
781  {
782  mutt_error(_("Error: Can't build path of '%s'"), rcfile_path);
783  return -1;
784  }
785 
786  STAILQ_FOREACH(np, &MuttrcStack, entries)
787  {
788  if (mutt_str_strcmp(np->data, rcfile) == 0)
789  {
790  break;
791  }
792  }
793  if (!np)
794  {
796  }
797  else
798  {
799  mutt_error(_("Error: Cyclic sourcing of configuration file '%s'"), rcfile);
800  return -1;
801  }
802  }
803 
804  mutt_debug(LL_DEBUG2, "Reading configuration file '%s'\n", rcfile);
805 
806  FILE *fp = mutt_open_read(rcfile, &pid);
807  if (!fp)
808  {
809  mutt_buffer_printf(err, "%s: %s", rcfile, strerror(errno));
810  return -1;
811  }
812 
813  mutt_buffer_init(&token);
814  while ((linebuf = mutt_file_read_line(linebuf, &buflen, fp, &line, MUTT_CONT)))
815  {
816  const bool conv = C_ConfigCharset && C_Charset;
817  if (conv)
818  {
819  currentline = mutt_str_strdup(linebuf);
820  if (!currentline)
821  continue;
822  mutt_ch_convert_string(&currentline, C_ConfigCharset, C_Charset, 0);
823  }
824  else
825  currentline = linebuf;
826  mutt_buffer_reset(err);
827  line_rc = mutt_parse_rc_line(currentline, &token, err);
828  if (line_rc == MUTT_CMD_ERROR)
829  {
830  mutt_error(_("Error in %s, line %d: %s"), rcfile, line, err->data);
831  if (--rc < -MAX_ERRS)
832  {
833  if (conv)
834  FREE(&currentline);
835  break;
836  }
837  }
838  else if (line_rc == MUTT_CMD_WARNING)
839  {
840  /* Warning */
841  mutt_warning(_("Warning in %s, line %d: %s"), rcfile, line, err->data);
842  warnings++;
843  }
844  else if (line_rc == MUTT_CMD_FINISH)
845  {
846  break; /* Found "finish" command */
847  }
848  else
849  {
850  if (rc < 0)
851  rc = -1;
852  }
853  if (conv)
854  FREE(&currentline);
855  }
856  FREE(&token.data);
857  FREE(&linebuf);
858  mutt_file_fclose(&fp);
859  if (pid != -1)
860  mutt_wait_filter(pid);
861  if (rc)
862  {
863  /* the neomuttrc source keyword */
864  mutt_buffer_reset(err);
865  mutt_buffer_printf(err, (rc >= -MAX_ERRS) ? _("source: errors in %s") : _("source: reading aborted due to too many errors in %s"),
866  rcfile);
867  rc = -1;
868  }
869  else
870  {
871  /* Don't alias errors with warnings */
872  if (warnings > 0)
873  {
874  mutt_buffer_printf(err, ngettext("source: %d warning in %s", "source: %d warnings in %s", warnings),
875  warnings, rcfile);
876  rc = -2;
877  }
878  }
879 
880  if (!ispipe && !STAILQ_EMPTY(&MuttrcStack))
881  {
882  struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
883  STAILQ_REMOVE_HEAD(&MuttrcStack, entries);
884  FREE(&np->data);
885  FREE(&np);
886  }
887 
888  return rc;
889 }
890 
894 static enum CommandResult parse_alias(struct Buffer *buf, struct Buffer *s,
895  unsigned long data, struct Buffer *err)
896 {
897  struct Alias *tmp = NULL;
898  char *estr = NULL;
899  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
900 
901  if (!MoreArgs(s))
902  {
903  mutt_buffer_strcpy(err, _("alias: no address"));
904  return MUTT_CMD_WARNING;
905  }
906 
908 
909  if (parse_grouplist(&gl, buf, s, data, err) == -1)
910  return MUTT_CMD_ERROR;
911 
912  /* check to see if an alias with this name already exists */
913  TAILQ_FOREACH(tmp, &Aliases, entries)
914  {
915  if (mutt_str_strcasecmp(tmp->name, buf->data) == 0)
916  break;
917  }
918 
919  if (!tmp)
920  {
921  /* create a new alias */
922  tmp = mutt_alias_new();
923  tmp->name = mutt_str_strdup(buf->data);
924  TAILQ_INSERT_TAIL(&Aliases, tmp, entries);
925  /* give the main addressbook code a chance */
926  if (CurrentMenu == MENU_ALIAS)
927  OptMenuCaller = true;
928  }
929  else
930  {
932  /* override the previous value */
933  mutt_addrlist_clear(&tmp->addr);
934  if (CurrentMenu == MENU_ALIAS)
936  }
937 
939  mutt_debug(LL_DEBUG5, "Second token is '%s'\n", buf->data);
940 
941  mutt_addrlist_parse2(&tmp->addr, buf->data);
942 
943  if (mutt_addrlist_to_intl(&tmp->addr, &estr))
944  {
945  mutt_buffer_printf(err, _("Warning: Bad IDN '%s' in alias '%s'"), estr, tmp->name);
946  FREE(&estr);
947  goto bail;
948  }
949 
950  mutt_grouplist_add_addrlist(&gl, &tmp->addr);
952 
953  if (C_DebugLevel > LL_DEBUG4)
954  {
955  /* A group is terminated with an empty address, so check a->mailbox */
956  struct Address *a = NULL;
957  TAILQ_FOREACH(a, &tmp->addr, entries)
958  {
959  if (!a->mailbox)
960  break;
961 
962  if (a->group)
963  mutt_debug(LL_DEBUG5, " Group %s\n", a->mailbox);
964  else
965  mutt_debug(LL_DEBUG5, " %s\n", a->mailbox);
966  }
967  }
969  return MUTT_CMD_SUCCESS;
970 
971 bail:
973  return MUTT_CMD_ERROR;
974 }
975 
979 static enum CommandResult parse_alternates(struct Buffer *buf, struct Buffer *s,
980  unsigned long data, struct Buffer *err)
981 {
982  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
983 
985 
986  do
987  {
989 
990  if (parse_grouplist(&gl, buf, s, data, err) == -1)
991  goto bail;
992 
993  mutt_regexlist_remove(&UnAlternates, buf->data);
994 
995  if (mutt_regexlist_add(&Alternates, buf->data, REG_ICASE, err) != 0)
996  goto bail;
997 
998  if (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0)
999  goto bail;
1000  } while (MoreArgs(s));
1001 
1003  return MUTT_CMD_SUCCESS;
1004 
1005 bail:
1007  return MUTT_CMD_ERROR;
1008 }
1009 
1013 static enum CommandResult parse_attachments(struct Buffer *buf, struct Buffer *s,
1014  unsigned long data, struct Buffer *err)
1015 {
1016  char op;
1017  char *category = NULL;
1018  struct ListHead *head = NULL;
1019 
1021  if (!buf->data || (*buf->data == '\0'))
1022  {
1023  mutt_buffer_strcpy(err, _("attachments: no disposition"));
1024  return MUTT_CMD_WARNING;
1025  }
1026 
1027  category = buf->data;
1028  op = *category++;
1029 
1030  if (op == '?')
1031  {
1032  mutt_endwin();
1033  fflush(stdout);
1034  printf("\n%s\n\n", _("Current attachments settings:"));
1035  print_attach_list(&AttachAllow, '+', "A");
1036  print_attach_list(&AttachExclude, '-', "A");
1037  print_attach_list(&InlineAllow, '+', "I");
1038  print_attach_list(&InlineExclude, '-', "I");
1040  return MUTT_CMD_SUCCESS;
1041  }
1042 
1043  if ((op != '+') && (op != '-'))
1044  {
1045  op = '+';
1046  category--;
1047  }
1048  if (mutt_str_startswith("attachment", category, CASE_IGNORE))
1049  {
1050  if (op == '+')
1051  head = &AttachAllow;
1052  else
1053  head = &AttachExclude;
1054  }
1055  else if (mutt_str_startswith("inline", category, CASE_IGNORE))
1056  {
1057  if (op == '+')
1058  head = &InlineAllow;
1059  else
1060  head = &InlineExclude;
1061  }
1062  else
1063  {
1064  mutt_buffer_strcpy(err, _("attachments: invalid disposition"));
1065  return MUTT_CMD_ERROR;
1066  }
1067 
1068  return parse_attach_list(buf, s, head, err);
1069 }
1070 
1074 static enum CommandResult parse_echo(struct Buffer *buf, struct Buffer *s,
1075  unsigned long data, struct Buffer *err)
1076 {
1077  if (!MoreArgs(s))
1078  {
1079  mutt_buffer_printf(err, _("%s: too few arguments"), "echo");
1080  return MUTT_CMD_WARNING;
1081  }
1083  OptForceRefresh = true;
1084  mutt_message("%s", buf->data);
1085  OptForceRefresh = false;
1086  mutt_sleep(0);
1087 
1088  return MUTT_CMD_SUCCESS;
1089 }
1090 
1098 static enum CommandResult parse_finish(struct Buffer *buf, struct Buffer *s,
1099  unsigned long data, struct Buffer *err)
1100 {
1101  if (MoreArgs(s))
1102  {
1103  mutt_buffer_printf(err, _("%s: too many arguments"), "finish");
1104  return MUTT_CMD_WARNING;
1105  }
1106 
1107  return MUTT_CMD_FINISH;
1108 }
1109 
1113 static enum CommandResult parse_group(struct Buffer *buf, struct Buffer *s,
1114  unsigned long data, struct Buffer *err)
1115 {
1116  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
1117  enum GroupState state = GS_NONE;
1118 
1119  do
1120  {
1122  if (parse_grouplist(&gl, buf, s, data, err) == -1)
1123  goto bail;
1124 
1125  if ((data == MUTT_UNGROUP) && (mutt_str_strcasecmp(buf->data, "*") == 0))
1126  {
1127  mutt_grouplist_clear(&gl);
1128  goto out;
1129  }
1130 
1131  if (mutt_str_strcasecmp(buf->data, "-rx") == 0)
1132  state = GS_RX;
1133  else if (mutt_str_strcasecmp(buf->data, "-addr") == 0)
1134  state = GS_ADDR;
1135  else
1136  {
1137  switch (state)
1138  {
1139  case GS_NONE:
1140  mutt_buffer_printf(err, _("%sgroup: missing -rx or -addr"),
1141  (data == MUTT_UNGROUP) ? "un" : "");
1142  goto warn;
1143 
1144  case GS_RX:
1145  if ((data == MUTT_GROUP) &&
1146  (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0))
1147  {
1148  goto bail;
1149  }
1150  else if ((data == MUTT_UNGROUP) &&
1151  (mutt_grouplist_remove_regex(&gl, buf->data) < 0))
1152  {
1153  goto bail;
1154  }
1155  break;
1156 
1157  case GS_ADDR:
1158  {
1159  char *estr = NULL;
1160  struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
1161  mutt_addrlist_parse2(&al, buf->data);
1162  if (TAILQ_EMPTY(&al))
1163  goto bail;
1164  if (mutt_addrlist_to_intl(&al, &estr))
1165  {
1166  mutt_buffer_printf(err, _("%sgroup: warning: bad IDN '%s'"),
1167  (data == 1) ? "un" : "", estr);
1168  mutt_addrlist_clear(&al);
1169  FREE(&estr);
1170  goto bail;
1171  }
1172  if (data == MUTT_GROUP)
1173  mutt_grouplist_add_addrlist(&gl, &al);
1174  else if (data == MUTT_UNGROUP)
1176  mutt_addrlist_clear(&al);
1177  break;
1178  }
1179  }
1180  }
1181  } while (MoreArgs(s));
1182 
1183 out:
1185  return MUTT_CMD_SUCCESS;
1186 
1187 bail:
1189  return MUTT_CMD_ERROR;
1190 
1191 warn:
1193  return MUTT_CMD_WARNING;
1194 }
1195 
1202 static bool is_function(const char *name)
1203 {
1204  for (enum MenuType i = 0; i < MENU_MAX; i++)
1205  {
1206  const struct Binding *b = km_get_table(Menus[i].value);
1207  if (!b)
1208  continue;
1209 
1210  for (int j = 0; b[j].name; j++)
1211  if (mutt_str_strcmp(name, b[j].name) == 0)
1212  return true;
1213  }
1214  return false;
1215 }
1216 
1230 static enum CommandResult parse_ifdef(struct Buffer *buf, struct Buffer *s,
1231  unsigned long data, struct Buffer *err)
1232 {
1233  struct Buffer token = mutt_buffer_make(0);
1234 
1236 
1237  // is the item defined as:
1238  bool res = cs_get_elem(Config, buf->data) // a variable?
1239  || feature_enabled(buf->data) // a compiled-in feature?
1240  || is_function(buf->data) // a function?
1241  || mutt_command_get(buf->data) // a command?
1242  || myvar_get(buf->data) // a my_ variable?
1243  || mutt_str_getenv(buf->data); // an environment variable?
1244 
1245  if (!MoreArgs(s))
1246  {
1247  mutt_buffer_printf(err, _("%s: too few arguments"), (data ? "ifndef" : "ifdef"));
1248  return MUTT_CMD_WARNING;
1249  }
1251 
1252  /* ifdef KNOWN_SYMBOL or ifndef UNKNOWN_SYMBOL */
1253  if ((res && (data == 0)) || (!res && (data == 1)))
1254  {
1255  enum CommandResult rc = mutt_parse_rc_line(buf->data, &token, err);
1256  if (rc == MUTT_CMD_ERROR)
1257  {
1258  mutt_error(_("Error: %s"), err->data);
1259  FREE(&token.data);
1260  return MUTT_CMD_ERROR;
1261  }
1262  FREE(&token.data);
1263  return rc;
1264  }
1265  return MUTT_CMD_SUCCESS;
1266 }
1267 
1271 static enum CommandResult parse_ignore(struct Buffer *buf, struct Buffer *s,
1272  unsigned long data, struct Buffer *err)
1273 {
1274  do
1275  {
1278  add_to_stailq(&Ignore, buf->data);
1279  } while (MoreArgs(s));
1280 
1281  return MUTT_CMD_SUCCESS;
1282 }
1283 
1287 static enum CommandResult parse_lists(struct Buffer *buf, struct Buffer *s,
1288  unsigned long data, struct Buffer *err)
1289 {
1290  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
1291 
1292  do
1293  {
1295 
1296  if (parse_grouplist(&gl, buf, s, data, err) == -1)
1297  goto bail;
1298 
1300 
1301  if (mutt_regexlist_add(&MailLists, buf->data, REG_ICASE, err) != 0)
1302  goto bail;
1303 
1304  if (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0)
1305  goto bail;
1306  } while (MoreArgs(s));
1307 
1309  return MUTT_CMD_SUCCESS;
1310 
1311 bail:
1313  return MUTT_CMD_ERROR;
1314 }
1315 
1321 static enum CommandResult parse_mailboxes(struct Buffer *buf, struct Buffer *s,
1322  unsigned long data, struct Buffer *err)
1323 {
1324  while (MoreArgs(s))
1325  {
1326  struct Mailbox *m = mailbox_new();
1327 
1328  if (data & MUTT_NAMED)
1329  {
1331  if (buf->data && (*buf->data != '\0'))
1332  {
1333  m->name = mutt_str_strdup(buf->data);
1334  }
1335  else
1336  {
1337  mailbox_free(&m);
1338  continue;
1339  }
1340  }
1341 
1343  if (mutt_buffer_is_empty(buf))
1344  {
1345  /* Skip empty tokens. */
1346  mailbox_free(&m);
1347  continue;
1348  }
1349 
1350  mutt_buffer_strcpy(&m->pathbuf, buf->data);
1351  /* int rc = */ mx_path_canon2(m, C_Folder);
1352 
1353  bool new_account = false;
1354  struct Account *a = mx_ac_find(m);
1355  if (!a)
1356  {
1357  a = account_new(NULL, NeoMutt->sub);
1358  a->magic = m->magic;
1359  new_account = true;
1360  }
1361 
1362  if (!new_account)
1363  {
1364  struct Mailbox *m_old = mx_mbox_find(a, m->realpath);
1365  if (m_old)
1366  {
1367  if (m_old->flags == MB_HIDDEN)
1368  {
1369  m_old->flags = MB_NORMAL;
1370  mutt_sb_notify_mailbox(m_old, true);
1371  }
1372  mailbox_free(&m);
1373  continue;
1374  }
1375  }
1376 
1377  if (mx_ac_add(a, m) < 0)
1378  {
1379  //error
1380  mailbox_free(&m);
1381  if (new_account)
1382  {
1383  FREE(&a);
1384  }
1385  continue;
1386  }
1387  if (new_account)
1388  {
1390  }
1391 
1392 #ifdef USE_SIDEBAR
1393  mutt_sb_notify_mailbox(m, true);
1394 #endif
1395 #ifdef USE_INOTIFY
1396  mutt_monitor_add(m);
1397 #endif
1398  }
1399  return MUTT_CMD_SUCCESS;
1400 }
1401 
1405 static enum CommandResult parse_my_hdr(struct Buffer *buf, struct Buffer *s,
1406  unsigned long data, struct Buffer *err)
1407 {
1408  struct ListNode *n = NULL;
1409  size_t keylen;
1410 
1412  char *p = strpbrk(buf->data, ": \t");
1413  if (!p || (*p != ':'))
1414  {
1415  mutt_buffer_strcpy(err, _("invalid header field"));
1416  return MUTT_CMD_WARNING;
1417  }
1418  keylen = p - buf->data + 1;
1419 
1420  STAILQ_FOREACH(n, &UserHeader, entries)
1421  {
1422  /* see if there is already a field by this name */
1423  if (mutt_str_strncasecmp(buf->data, n->data, keylen) == 0)
1424  {
1425  break;
1426  }
1427  }
1428 
1429  if (!n)
1430  {
1431  /* not found, allocate memory for a new node and add it to the list */
1432  n = mutt_list_insert_tail(&UserHeader, NULL);
1433  }
1434  else
1435  {
1436  /* found, free the existing data */
1437  FREE(&n->data);
1438  }
1439 
1440  n->data = buf->data;
1441  mutt_buffer_init(buf);
1442 
1443  return MUTT_CMD_SUCCESS;
1444 }
1445 
1446 #ifdef USE_SIDEBAR
1447 
1450 static enum CommandResult parse_path_list(struct Buffer *buf, struct Buffer *s,
1451  unsigned long data, struct Buffer *err)
1452 {
1453  char path[PATH_MAX];
1454 
1455  do
1456  {
1458  mutt_str_strfcpy(path, buf->data, sizeof(path));
1459  mutt_expand_path(path, sizeof(path));
1460  add_to_stailq((struct ListHead *) data, path);
1461  } while (MoreArgs(s));
1462 
1463  return MUTT_CMD_SUCCESS;
1464 }
1465 #endif
1466 
1467 #ifdef USE_SIDEBAR
1468 
1471 static enum CommandResult parse_path_unlist(struct Buffer *buf, struct Buffer *s,
1472  unsigned long data, struct Buffer *err)
1473 {
1474  char path[PATH_MAX];
1475 
1476  do
1477  {
1479  /* Check for deletion of entire list */
1480  if (mutt_str_strcmp(buf->data, "*") == 0)
1481  {
1482  mutt_list_free((struct ListHead *) data);
1483  break;
1484  }
1485  mutt_str_strfcpy(path, buf->data, sizeof(path));
1486  mutt_expand_path(path, sizeof(path));
1487  remove_from_stailq((struct ListHead *) data, path);
1488  } while (MoreArgs(s));
1489 
1490  return MUTT_CMD_SUCCESS;
1491 }
1492 #endif
1493 
1499 static enum CommandResult parse_set(struct Buffer *buf, struct Buffer *s,
1500  unsigned long data, struct Buffer *err)
1501 {
1502  /* The order must match `enum MuttSetCommand` */
1503  static const char *set_commands[] = { "set", "toggle", "unset", "reset" };
1504 
1505  int rc = 0;
1506 
1507  while (MoreArgs(s))
1508  {
1509  bool prefix = false;
1510  bool query = false;
1511  bool inv = (data == MUTT_SET_INV);
1512  bool reset = (data == MUTT_SET_RESET);
1513  bool unset = (data == MUTT_SET_UNSET);
1514 
1515  if (*s->dptr == '?')
1516  {
1517  prefix = true;
1518  query = true;
1519  s->dptr++;
1520  }
1521  else if (mutt_str_startswith(s->dptr, "no", CASE_MATCH))
1522  {
1523  prefix = true;
1524  unset = !unset;
1525  s->dptr += 2;
1526  }
1527  else if (mutt_str_startswith(s->dptr, "inv", CASE_MATCH))
1528  {
1529  prefix = true;
1530  inv = !inv;
1531  s->dptr += 3;
1532  }
1533  else if (*s->dptr == '&')
1534  {
1535  prefix = true;
1536  reset = true;
1537  s->dptr++;
1538  }
1539 
1540  if (prefix && (data != MUTT_SET_SET))
1541  {
1542  mutt_buffer_printf(err, "ERR22 can't use 'inv', 'no', '&' or '?' with the '%s' command",
1543  set_commands[data]);
1544  return MUTT_CMD_WARNING;
1545  }
1546 
1547  /* get the variable name */
1549 
1550  bool bq = false;
1551  bool equals = false;
1552 
1553  struct HashElem *he = NULL;
1554  bool my = mutt_str_startswith(buf->data, "my_", CASE_MATCH);
1555  if (!my)
1556  {
1557  he = cs_get_elem(Config, buf->data);
1558  if (!he)
1559  {
1560  if (reset && (mutt_str_strcmp(buf->data, "all") == 0))
1561  {
1562  struct HashElem **list = get_elem_list(Config);
1563  if (!list)
1564  return MUTT_CMD_ERROR;
1565 
1566  for (size_t i = 0; list[i]; i++)
1567  cs_he_reset(Config, list[i], NULL);
1568 
1569  FREE(&list);
1570  break;
1571  }
1572  else
1573  {
1574  mutt_buffer_printf(err, "ERR01 unknown variable: %s", buf->data);
1575  return MUTT_CMD_ERROR;
1576  }
1577  }
1578 
1579  bq = ((DTYPE(he->type) == DT_BOOL) || (DTYPE(he->type) == DT_QUAD));
1580  }
1581 
1582  if (*s->dptr == '?')
1583  {
1584  if (prefix)
1585  {
1586  mutt_buffer_printf(err,
1587  "ERR02 can't use a prefix when querying a variable");
1588  return MUTT_CMD_WARNING;
1589  }
1590 
1591  if (reset || unset || inv)
1592  {
1593  mutt_buffer_printf(err, "ERR03 can't query a variable with the '%s' command",
1594  set_commands[data]);
1595  return MUTT_CMD_WARNING;
1596  }
1597 
1598  query = true;
1599  s->dptr++;
1600  }
1601  else if (*s->dptr == '=')
1602  {
1603  if (prefix)
1604  {
1605  mutt_buffer_printf(err,
1606  "ERR04 can't use prefix when setting a variable");
1607  return MUTT_CMD_WARNING;
1608  }
1609 
1610  if (reset || unset || inv)
1611  {
1612  mutt_buffer_printf(err, "ERR05 can't set a variable with the '%s' command",
1613  set_commands[data]);
1614  return MUTT_CMD_WARNING;
1615  }
1616 
1617  equals = true;
1618  s->dptr++;
1619  }
1620 
1621  if (!bq && (inv || (unset && prefix)))
1622  {
1623  if (data == MUTT_SET_SET)
1624  {
1625  mutt_buffer_printf(err, "ERR06 prefixes 'no' and 'inv' may only be "
1626  "used with bool/quad variables");
1627  }
1628  else
1629  {
1630  mutt_buffer_printf(err, "ERR07 command '%s' can only be used with bool/quad variables",
1631  set_commands[data]);
1632  }
1633  return MUTT_CMD_WARNING;
1634  }
1635 
1636  if (reset)
1637  {
1638  // mutt_buffer_printf(err, "ACT24 reset variable %s", buf->data);
1639  if (he)
1640  {
1641  rc = cs_he_reset(Config, he, err);
1642  if (CSR_RESULT(rc) != CSR_SUCCESS)
1643  return MUTT_CMD_ERROR;
1644  }
1645  else
1646  {
1647  myvar_del(buf->data);
1648  }
1649  continue;
1650  }
1651 
1652  if ((data == MUTT_SET_SET) && !inv && !unset)
1653  {
1654  if (query)
1655  {
1656  // mutt_buffer_printf(err, "ACT08 query variable %s", buf->data);
1657  if (he)
1658  {
1659  mutt_buffer_addstr(err, buf->data);
1660  mutt_buffer_addch(err, '=');
1661  mutt_buffer_reset(buf);
1662  rc = cs_he_string_get(Config, he, buf);
1663  if (CSR_RESULT(rc) != CSR_SUCCESS)
1664  {
1665  mutt_buffer_addstr(err, buf->data);
1666  return MUTT_CMD_ERROR;
1667  }
1668  pretty_var(buf->data, err);
1669  }
1670  else
1671  {
1672  const char *val = myvar_get(buf->data);
1673  if (val)
1674  {
1675  mutt_buffer_addstr(err, buf->data);
1676  mutt_buffer_addch(err, '=');
1677  pretty_var(val, err);
1678  }
1679  else
1680  {
1681  mutt_buffer_printf(err, _("%s: unknown variable"), buf->data);
1682  return MUTT_CMD_ERROR;
1683  }
1684  }
1685  break;
1686  }
1687  else if (equals)
1688  {
1689  // mutt_buffer_printf(err, "ACT11 set variable %s to ", buf->data);
1690  const char *name = NULL;
1691  if (my)
1692  {
1693  name = mutt_str_strdup(buf->data);
1694  }
1696  if (my)
1697  {
1698  myvar_set(name, buf->data);
1699  FREE(&name);
1700  }
1701  else
1702  {
1703  if (IS_PATH(he))
1704  {
1705  mutt_expand_path(buf->data, buf->dsize);
1706  char scratch[PATH_MAX];
1707  mutt_str_strfcpy(scratch, buf->data, sizeof(scratch));
1708  size_t scratchlen = mutt_str_strlen(scratch);
1709  if (!(he->type & DT_MAILBOX) && (scratchlen != 0))
1710  {
1711  if ((scratch[scratchlen - 1] != '|') && /* not a command */
1712  (url_check_scheme(scratch) == U_UNKNOWN)) /* probably a local file */
1713  {
1714  struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
1715  if (mutt_path_to_absolute(scratch, np ? NONULL(np->data) : "./"))
1716  {
1717  mutt_buffer_reset(buf);
1718  mutt_buffer_addstr(buf, scratch);
1719  }
1720  else
1721  {
1722  mutt_error(_("Error: Can't build path of '%s'"), scratch);
1723  }
1724  }
1725  }
1726  }
1727  else if (IS_COMMAND(he))
1728  {
1729  char scratch[PATH_MAX];
1730  mutt_str_strfcpy(scratch, buf->data, sizeof(scratch));
1731 
1732  if (mutt_str_strcmp(buf->data, "builtin") != 0)
1733  {
1734  mutt_expand_path(scratch, sizeof(scratch));
1735  }
1736  mutt_buffer_reset(buf);
1737  mutt_buffer_addstr(buf, scratch);
1738  }
1739 
1740  rc = cs_he_string_set(Config, he, buf->data, err);
1741  if (CSR_RESULT(rc) != CSR_SUCCESS)
1742  return MUTT_CMD_ERROR;
1743  }
1744  continue;
1745  }
1746  else
1747  {
1748  if (bq)
1749  {
1750  // mutt_buffer_printf(err, "ACT23 set variable %s to 'yes'", buf->data);
1751  rc = cs_he_native_set(Config, he, true, err);
1752  if (CSR_RESULT(rc) != CSR_SUCCESS)
1753  return MUTT_CMD_ERROR;
1754  continue;
1755  }
1756  else
1757  {
1758  // mutt_buffer_printf(err, "ACT10 query variable %s", buf->data);
1759  if (he)
1760  {
1761  mutt_buffer_addstr(err, buf->data);
1762  mutt_buffer_addch(err, '=');
1763  mutt_buffer_reset(buf);
1764  rc = cs_he_string_get(Config, he, buf);
1765  if (CSR_RESULT(rc) != CSR_SUCCESS)
1766  {
1767  mutt_buffer_addstr(err, buf->data);
1768  return MUTT_CMD_ERROR;
1769  }
1770  pretty_var(buf->data, err);
1771  }
1772  else
1773  {
1774  const char *val = myvar_get(buf->data);
1775  if (val)
1776  {
1777  mutt_buffer_addstr(err, buf->data);
1778  mutt_buffer_addch(err, '=');
1779  pretty_var(val, err);
1780  }
1781  else
1782  {
1783  mutt_buffer_printf(err, _("%s: unknown variable"), buf->data);
1784  return MUTT_CMD_ERROR;
1785  }
1786  }
1787  break;
1788  }
1789  }
1790  }
1791 
1792  if (my)
1793  {
1794  myvar_del(buf->data);
1795  }
1796  else if (bq)
1797  {
1798  if (inv)
1799  {
1800  // mutt_buffer_printf(err, "ACT25 TOGGLE bool/quad variable %s", buf->data);
1801  if (DTYPE(he->type) == DT_BOOL)
1802  bool_he_toggle(Config, he, err);
1803  else
1804  quad_he_toggle(Config, he, err);
1805  }
1806  else
1807  {
1808  // mutt_buffer_printf(err, "ACT26 UNSET bool/quad variable %s", buf->data);
1809  rc = cs_he_native_set(Config, he, false, err);
1810  if (CSR_RESULT(rc) != CSR_SUCCESS)
1811  return MUTT_CMD_ERROR;
1812  }
1813  continue;
1814  }
1815  else
1816  {
1817  rc = cs_he_string_set(Config, he, NULL, err);
1818  if (CSR_RESULT(rc) != CSR_SUCCESS)
1819  return MUTT_CMD_ERROR;
1820  }
1821  }
1822 
1823  return MUTT_CMD_SUCCESS;
1824 }
1825 
1829 static enum CommandResult parse_setenv(struct Buffer *buf, struct Buffer *s,
1830  unsigned long data, struct Buffer *err)
1831 {
1832  char **envp = mutt_envlist_getlist();
1833 
1834  bool query = false;
1835  bool unset = (data == MUTT_SET_UNSET);
1836 
1837  if (!MoreArgs(s))
1838  {
1839  mutt_buffer_printf(err, _("%s: too few arguments"), "setenv");
1840  return MUTT_CMD_WARNING;
1841  }
1842 
1843  if (*s->dptr == '?')
1844  {
1845  query = true;
1846  s->dptr++;
1847  }
1848 
1849  /* get variable name */
1851 
1852  if (query)
1853  {
1854  bool found = false;
1855  while (envp && *envp)
1856  {
1857  /* This will display all matches for "^QUERY" */
1858  if (mutt_str_startswith(*envp, buf->data, CASE_MATCH))
1859  {
1860  if (!found)
1861  {
1862  mutt_endwin();
1863  found = true;
1864  }
1865  puts(*envp);
1866  }
1867  envp++;
1868  }
1869 
1870  if (found)
1871  {
1873  return MUTT_CMD_SUCCESS;
1874  }
1875 
1876  mutt_buffer_printf(err, _("%s is unset"), buf->data);
1877  return MUTT_CMD_WARNING;
1878  }
1879 
1880  if (unset)
1881  {
1882  if (mutt_envlist_unset(buf->data))
1883  return MUTT_CMD_SUCCESS;
1884  return MUTT_CMD_ERROR;
1885  }
1886 
1887  /* set variable */
1888 
1889  if (*s->dptr == '=')
1890  {
1891  s->dptr++;
1892  SKIPWS(s->dptr);
1893  }
1894 
1895  if (!MoreArgs(s))
1896  {
1897  mutt_buffer_printf(err, _("%s: too few arguments"), "setenv");
1898  return MUTT_CMD_WARNING;
1899  }
1900 
1901  char *name = mutt_str_strdup(buf->data);
1903  mutt_envlist_set(name, buf->data, true);
1904  FREE(&name);
1905 
1906  return MUTT_CMD_SUCCESS;
1907 }
1908 
1912 static enum CommandResult parse_source(struct Buffer *buf, struct Buffer *s,
1913  unsigned long data, struct Buffer *err)
1914 {
1915  char path[PATH_MAX];
1916 
1917  do
1918  {
1919  if (mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS) != 0)
1920  {
1921  mutt_buffer_printf(err, _("source: error at %s"), s->dptr);
1922  return MUTT_CMD_ERROR;
1923  }
1924  mutt_str_strfcpy(path, buf->data, sizeof(path));
1925  mutt_expand_path(path, sizeof(path));
1926 
1927  if (source_rc(path, err) < 0)
1928  {
1929  mutt_buffer_printf(err, _("source: file %s could not be sourced"), path);
1930  return MUTT_CMD_ERROR;
1931  }
1932 
1933  } while (MoreArgs(s));
1934 
1935  return MUTT_CMD_SUCCESS;
1936 }
1937 
1941 static enum CommandResult parse_spam_list(struct Buffer *buf, struct Buffer *s,
1942  unsigned long data, struct Buffer *err)
1943 {
1944  struct Buffer templ;
1945 
1946  mutt_buffer_init(&templ);
1947 
1948  /* Insist on at least one parameter */
1949  if (!MoreArgs(s))
1950  {
1951  if (data == MUTT_SPAM)
1952  mutt_buffer_strcpy(err, _("spam: no matching pattern"));
1953  else
1954  mutt_buffer_strcpy(err, _("nospam: no matching pattern"));
1955  return MUTT_CMD_ERROR;
1956  }
1957 
1958  /* Extract the first token, a regex */
1960 
1961  /* data should be either MUTT_SPAM or MUTT_NOSPAM. MUTT_SPAM is for spam commands. */
1962  if (data == MUTT_SPAM)
1963  {
1964  /* If there's a second parameter, it's a template for the spam tag. */
1965  if (MoreArgs(s))
1966  {
1968 
1969  /* Add to the spam list. */
1970  if (mutt_replacelist_add(&SpamList, buf->data, templ.data, err) != 0)
1971  {
1972  FREE(&templ.data);
1973  return MUTT_CMD_ERROR;
1974  }
1975  FREE(&templ.data);
1976  }
1977  /* If not, try to remove from the nospam list. */
1978  else
1979  {
1981  }
1982 
1983  return MUTT_CMD_SUCCESS;
1984  }
1985  /* MUTT_NOSPAM is for nospam commands. */
1986  else if (data == MUTT_NOSPAM)
1987  {
1988  /* nospam only ever has one parameter. */
1989 
1990  /* "*" is a special case. */
1991  if (mutt_str_strcmp(buf->data, "*") == 0)
1992  {
1995  return MUTT_CMD_SUCCESS;
1996  }
1997 
1998  /* If it's on the spam list, just remove it. */
1999  if (mutt_replacelist_remove(&SpamList, buf->data) != 0)
2000  return MUTT_CMD_SUCCESS;
2001 
2002  /* Otherwise, add it to the nospam list. */
2003  if (mutt_regexlist_add(&NoSpamList, buf->data, REG_ICASE, err) != 0)
2004  return MUTT_CMD_ERROR;
2005 
2006  return MUTT_CMD_SUCCESS;
2007  }
2008 
2009  /* This should not happen. */
2010  mutt_buffer_strcpy(err, "This is no good at all.");
2011  return MUTT_CMD_ERROR;
2012 }
2013 
2019 static enum CommandResult parse_stailq(struct Buffer *buf, struct Buffer *s,
2020  unsigned long data, struct Buffer *err)
2021 {
2022  do
2023  {
2025  add_to_stailq((struct ListHead *) data, buf->data);
2026  } while (MoreArgs(s));
2027 
2028  return MUTT_CMD_SUCCESS;
2029 }
2030 
2034 static enum CommandResult parse_subjectrx_list(struct Buffer *buf, struct Buffer *s,
2035  unsigned long data, struct Buffer *err)
2036 {
2037  enum CommandResult rc;
2038 
2039  rc = parse_replace_list(buf, s, data, err);
2040  if (rc == MUTT_CMD_SUCCESS)
2042  return rc;
2043 }
2044 
2048 static enum CommandResult parse_subscribe(struct Buffer *buf, struct Buffer *s,
2049  unsigned long data, struct Buffer *err)
2050 {
2051  struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
2052 
2053  do
2054  {
2056 
2057  if (parse_grouplist(&gl, buf, s, data, err) == -1)
2058  goto bail;
2059 
2062 
2063  if (mutt_regexlist_add(&MailLists, buf->data, REG_ICASE, err) != 0)
2064  goto bail;
2065  if (mutt_regexlist_add(&SubscribedLists, buf->data, REG_ICASE, err) != 0)
2066  goto bail;
2067  if (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0)
2068  goto bail;
2069  } while (MoreArgs(s));
2070 
2072  return MUTT_CMD_SUCCESS;
2073 
2074 bail:
2076  return MUTT_CMD_ERROR;
2077 }
2078 
2079 #ifdef USE_IMAP
2080 
2087 static enum CommandResult parse_subscribe_to(struct Buffer *buf, struct Buffer *s,
2088  unsigned long data, struct Buffer *err)
2089 {
2090  if (!buf || !s || !err)
2091  return MUTT_CMD_ERROR;
2092 
2093  mutt_buffer_reset(err);
2094 
2095  if (MoreArgs(s))
2096  {
2098 
2099  if (MoreArgs(s))
2100  {
2101  mutt_buffer_printf(err, _("%s: too many arguments"), "subscribe-to");
2102  return MUTT_CMD_WARNING;
2103  }
2104 
2105  if (buf->data && (*buf->data != '\0'))
2106  {
2107  /* Expand and subscribe */
2108  if (imap_subscribe(mutt_expand_path(buf->data, buf->dsize), true) != 0)
2109  {
2110  mutt_buffer_printf(err, _("Could not subscribe to %s"), buf->data);
2111  return MUTT_CMD_ERROR;
2112  }
2113  else
2114  {
2115  mutt_message(_("Subscribed to %s"), buf->data);
2116  return MUTT_CMD_SUCCESS;
2117  }
2118  }
2119  else
2120  {
2121  mutt_debug(LL_DEBUG1, "Corrupted buffer");
2122  return MUTT_CMD_ERROR;
2123  }
2124  }
2125 
2126  mutt_buffer_addstr(err, _("No folder specified"));
2127  return MUTT_CMD_WARNING;
2128 }
2129 #endif
2130 
2134 static enum CommandResult parse_tag_formats(struct Buffer *buf, struct Buffer *s,
2135  unsigned long data, struct Buffer *err)
2136 {
2137  if (!buf || !s)
2138  return MUTT_CMD_ERROR;
2139 
2140  char *tmp = NULL;
2141 
2142  while (MoreArgs(s))
2143  {
2144  char *tag = NULL, *format = NULL;
2145 
2147  if (buf->data && (*buf->data != '\0'))
2148  tag = mutt_str_strdup(buf->data);
2149  else
2150  continue;
2151 
2153  format = mutt_str_strdup(buf->data);
2154 
2155  /* avoid duplicates */
2156  tmp = mutt_hash_find(TagFormats, format);
2157  if (tmp)
2158  {
2159  mutt_debug(LL_DEBUG3, "tag format '%s' already registered as '%s'\n", format, tmp);
2160  FREE(&tag);
2161  FREE(&format);
2162  continue;
2163  }
2164 
2165  mutt_hash_insert(TagFormats, format, tag);
2166  }
2167  return MUTT_CMD_SUCCESS;
2168 }
2169 
2173 static enum CommandResult parse_tag_transforms(struct Buffer *buf, struct Buffer *s,
2174  unsigned long data, struct Buffer *err)
2175 {
2176  if (!buf || !s)
2177  return MUTT_CMD_ERROR;
2178 
2179  char *tmp = NULL;
2180 
2181  while (MoreArgs(s))
2182  {
2183  char *tag = NULL, *transform = NULL;
2184 
2186  if (buf->data && (*buf->data != '\0'))
2187  tag = mutt_str_strdup(buf->data);
2188  else
2189  continue;
2190 
2192  transform = mutt_str_strdup(buf->data);
2193 
2194  /* avoid duplicates */
2195  tmp = mutt_hash_find(TagTransforms, tag);
2196  if (tmp)
2197  {
2198  mutt_debug(LL_DEBUG3, "tag transform '%s' already registered as '%s'\n", tag, tmp);
2199  FREE(&tag);
2200  FREE(&transform);
2201  continue;
2202  }
2203 
2204  mutt_hash_insert(TagTransforms, tag, transform);
2205  }
2206  return MUTT_CMD_SUCCESS;
2207 }
2208 
2212 static enum CommandResult parse_unalias(struct Buffer *buf, struct Buffer *s,
2213  unsigned long data, struct Buffer *err)
2214 {
2215  struct Alias *a = NULL;
2216 
2217  do
2218  {
2220 
2221  if (mutt_str_strcmp("*", buf->data) == 0)
2222  {
2223  if (CurrentMenu == MENU_ALIAS)
2224  {
2225  TAILQ_FOREACH(a, &Aliases, entries)
2226  {
2227  a->del = true;
2228  }
2230  }
2231  else
2232  mutt_aliaslist_free(&Aliases);
2233  break;
2234  }
2235  else
2236  {
2237  TAILQ_FOREACH(a, &Aliases, entries)
2238  {
2239  if (mutt_str_strcasecmp(buf->data, a->name) == 0)
2240  {
2241  if (CurrentMenu == MENU_ALIAS)
2242  {
2243  a->del = true;
2245  }
2246  else
2247  {
2248  TAILQ_REMOVE(&Aliases, a, entries);
2249  mutt_alias_free(&a);
2250  }
2251  break;
2252  }
2253  }
2254  }
2255  } while (MoreArgs(s));
2256  return MUTT_CMD_SUCCESS;
2257 }
2258 
2262 static enum CommandResult parse_unalternates(struct Buffer *buf, struct Buffer *s,
2263  unsigned long data, struct Buffer *err)
2264 {
2265  alternates_clean();
2266  do
2267  {
2269  mutt_regexlist_remove(&Alternates, buf->data);
2270 
2271  if ((mutt_str_strcmp(buf->data, "*") != 0) &&
2272  (mutt_regexlist_add(&UnAlternates, buf->data, REG_ICASE, err) != 0))
2273  {
2274  return MUTT_CMD_ERROR;
2275  }
2276 
2277  } while (MoreArgs(s));
2278 
2279  return MUTT_CMD_SUCCESS;
2280 }
2281 
2290 {
2291  if (!ptr || !*ptr)
2292  return;
2293 
2294  struct AttachMatch *am = *ptr;
2295  regfree(&am->minor_regex);
2296  FREE(&am->major);
2297  FREE(ptr);
2298 }
2299 
2303 static enum CommandResult parse_unattachments(struct Buffer *buf, struct Buffer *s,
2304  unsigned long data, struct Buffer *err)
2305 {
2306  char op;
2307  char *p = NULL;
2308  struct ListHead *head = NULL;
2309 
2311  if (!buf->data || (*buf->data == '\0'))
2312  {
2313  mutt_buffer_strcpy(err, _("unattachments: no disposition"));
2314  return MUTT_CMD_WARNING;
2315  }
2316 
2317  p = buf->data;
2318  op = *p++;
2319 
2320  if (op == '*')
2321  {
2323  mutt_list_free_type(&AttachExclude, (list_free_t) mutt_attachmatch_free);
2324  mutt_list_free_type(&InlineAllow, (list_free_t) mutt_attachmatch_free);
2325  mutt_list_free_type(&InlineExclude, (list_free_t) mutt_attachmatch_free);
2327  return 0;
2328  }
2329 
2330  if ((op != '+') && (op != '-'))
2331  {
2332  op = '+';
2333  p--;
2334  }
2335  if (mutt_str_startswith("attachment", p, CASE_IGNORE))
2336  {
2337  if (op == '+')
2338  head = &AttachAllow;
2339  else
2340  head = &AttachExclude;
2341  }
2342  else if (mutt_str_startswith("inline", p, CASE_IGNORE))
2343  {
2344  if (op == '+')
2345  head = &InlineAllow;
2346  else
2347  head = &InlineExclude;
2348  }
2349  else
2350  {
2351  mutt_buffer_strcpy(err, _("unattachments: invalid disposition"));
2352  return MUTT_CMD_ERROR;
2353  }
2354 
2355  return parse_unattach_list(buf, s, head, err);
2356 }
2357 
2361 static enum CommandResult parse_unignore(struct Buffer *buf, struct Buffer *s,
2362  unsigned long data, struct Buffer *err)
2363 {
2364  do
2365  {
2367 
2368  /* don't add "*" to the unignore list */
2369  if (strcmp(buf->data, "*") != 0)
2370  add_to_stailq(&UnIgnore, buf->data);
2371 
2372  remove_from_stailq(&Ignore, buf->data);
2373  } while (MoreArgs(s));
2374 
2375  return MUTT_CMD_SUCCESS;
2376 }
2377 
2381 static enum CommandResult parse_unlists(struct Buffer *buf, struct Buffer *s,
2382  unsigned long data, struct Buffer *err)
2383 {
2385  do
2386  {
2390 
2391  if ((mutt_str_strcmp(buf->data, "*") != 0) &&
2392  (mutt_regexlist_add(&UnMailLists, buf->data, REG_ICASE, err) != 0))
2393  {
2394  return MUTT_CMD_ERROR;
2395  }
2396  } while (MoreArgs(s));
2397 
2398  return MUTT_CMD_SUCCESS;
2399 }
2400 
2406 static enum CommandResult parse_unmailboxes(struct Buffer *buf, struct Buffer *s,
2407  unsigned long data, struct Buffer *err)
2408 {
2409  bool tmp_valid = false;
2410  bool clear_all = false;
2411 
2412  while (!clear_all && MoreArgs(s))
2413  {
2415 
2416  if (mutt_str_strcmp(buf->data, "*") == 0)
2417  {
2418  clear_all = true;
2419  tmp_valid = false;
2420  }
2421  else
2422  {
2424  tmp_valid = true;
2425  }
2426 
2427  struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
2428  struct MailboxNode *np = NULL;
2429  struct MailboxNode *nptmp = NULL;
2430  STAILQ_FOREACH_SAFE(np, &ml, entries, nptmp)
2431  {
2432  /* Decide whether to delete all normal mailboxes or all virtual */
2433  bool virt = ((np->mailbox->magic == MUTT_NOTMUCH) && (data & MUTT_VIRTUAL));
2434  bool norm = ((np->mailbox->magic != MUTT_NOTMUCH) && !(data & MUTT_VIRTUAL));
2435  bool clear_this = clear_all && (virt || norm);
2436 
2437  /* Compare against path or desc? Ensure 'buf' is valid */
2438  if (!clear_this && tmp_valid)
2439  {
2440  clear_this =
2441  (mutt_str_strcasecmp(mutt_b2s(buf), mailbox_path(np->mailbox)) == 0) ||
2442  (mutt_str_strcasecmp(mutt_b2s(buf), np->mailbox->name) == 0);
2443  }
2444 
2445  if (clear_this)
2446  {
2447 #ifdef USE_SIDEBAR
2448  mutt_sb_notify_mailbox(np->mailbox, false);
2449 #endif
2450 #ifdef USE_INOTIFY
2452 #endif
2453  if (Context && (Context->mailbox == np->mailbox))
2454  {
2455  np->mailbox->flags |= MB_HIDDEN;
2456  }
2457  else
2458  {
2460  }
2461  }
2462  }
2464  }
2465  return MUTT_CMD_SUCCESS;
2466 }
2467 
2471 static enum CommandResult parse_unmy_hdr(struct Buffer *buf, struct Buffer *s,
2472  unsigned long data, struct Buffer *err)
2473 {
2474  struct ListNode *np = NULL, *tmp = NULL;
2475  size_t l;
2476 
2477  do
2478  {
2480  if (mutt_str_strcmp("*", buf->data) == 0)
2481  {
2482  mutt_list_free(&UserHeader);
2483  continue;
2484  }
2485 
2486  l = mutt_str_strlen(buf->data);
2487  if (buf->data[l - 1] == ':')
2488  l--;
2489 
2490  STAILQ_FOREACH_SAFE(np, &UserHeader, entries, tmp)
2491  {
2492  if ((mutt_str_strncasecmp(buf->data, np->data, l) == 0) && (np->data[l] == ':'))
2493  {
2494  STAILQ_REMOVE(&UserHeader, np, ListNode, entries);
2495  FREE(&np->data);
2496  FREE(&np);
2497  }
2498  }
2499  } while (MoreArgs(s));
2500  return MUTT_CMD_SUCCESS;
2501 }
2502 
2508 static enum CommandResult parse_unstailq(struct Buffer *buf, struct Buffer *s,
2509  unsigned long data, struct Buffer *err)
2510 {
2511  do
2512  {
2514  /* Check for deletion of entire list */
2515  if (mutt_str_strcmp(buf->data, "*") == 0)
2516  {
2517  mutt_list_free((struct ListHead *) data);
2518  break;
2519  }
2520  remove_from_stailq((struct ListHead *) data, buf->data);
2521  } while (MoreArgs(s));
2522 
2523  return MUTT_CMD_SUCCESS;
2524 }
2525 
2529 static enum CommandResult parse_unsubjectrx_list(struct Buffer *buf, struct Buffer *s,
2530  unsigned long data, struct Buffer *err)
2531 {
2532  enum CommandResult rc;
2533 
2534  rc = parse_unreplace_list(buf, s, data, err);
2535  if (rc == MUTT_CMD_SUCCESS)
2537  return rc;
2538 }
2539 
2543 static enum CommandResult parse_unsubscribe(struct Buffer *buf, struct Buffer *s,
2544  unsigned long data, struct Buffer *err)
2545 {
2547  do
2548  {
2551 
2552  if ((mutt_str_strcmp(buf->data, "*") != 0) &&
2553  (mutt_regexlist_add(&UnSubscribedLists, buf->data, REG_ICASE, err) != 0))
2554  {
2555  return MUTT_CMD_ERROR;
2556  }
2557  } while (MoreArgs(s));
2558 
2559  return MUTT_CMD_SUCCESS;
2560 }
2561 
2562 #ifdef USE_IMAP
2563 
2570 static enum CommandResult parse_unsubscribe_from(struct Buffer *buf, struct Buffer *s,
2571  unsigned long data, struct Buffer *err)
2572 {
2573  if (!buf || !s || !err)
2574  return MUTT_CMD_ERROR;
2575 
2576  if (MoreArgs(s))
2577  {
2579 
2580  if (MoreArgs(s))
2581  {
2582  mutt_buffer_printf(err, _("%s: too many arguments"), "unsubscribe-from");
2583  return MUTT_CMD_WARNING;
2584  }
2585 
2586  if (buf->data && (*buf->data != '\0'))
2587  {
2588  /* Expand and subscribe */
2589  if (imap_subscribe(mutt_expand_path(buf->data, buf->dsize), false) != 0)
2590  {
2591  mutt_buffer_printf(err, _("Could not unsubscribe from %s"), buf->data);
2592  return MUTT_CMD_ERROR;
2593  }
2594  else
2595  {
2596  mutt_message(_("Unsubscribed from %s"), buf->data);
2597  return MUTT_CMD_SUCCESS;
2598  }
2599  }
2600  else
2601  {
2602  mutt_debug(LL_DEBUG1, "Corrupted buffer");
2603  return MUTT_CMD_ERROR;
2604  }
2605  }
2606 
2607  mutt_buffer_addstr(err, _("No folder specified"));
2608  return MUTT_CMD_WARNING;
2609 }
2610 #endif
2611 
2618 const struct Command *mutt_command_get(const char *s)
2619 {
2620  for (int i = 0; Commands[i].name; i++)
2621  if (mutt_str_strcmp(s, Commands[i].name) == 0)
2622  return &Commands[i];
2623  return NULL;
2624 }
2625 
2626 #ifdef USE_LUA
2627 
2634 void mutt_commands_apply(void *data, void (*application)(void *, const struct Command *))
2635 {
2636  for (int i = 0; Commands[i].name; i++)
2637  application(data, &Commands[i]);
2638 }
2639 #endif
2640 
2649 int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
2650 {
2651  if (!dest || !tok)
2652  return -1;
2653 
2654  char ch;
2655  char qc = '\0'; /* quote char */
2656  char *pc = NULL;
2657 
2658  mutt_buffer_reset(dest);
2659 
2660  SKIPWS(tok->dptr);
2661  while ((ch = *tok->dptr))
2662  {
2663  if (qc == '\0')
2664  {
2665  if ((IS_SPACE(ch) && !(flags & MUTT_TOKEN_SPACE)) ||
2666  ((ch == '#') && !(flags & MUTT_TOKEN_COMMENT)) ||
2667  ((ch == '=') && (flags & MUTT_TOKEN_EQUAL)) ||
2668  ((ch == '?') && (flags & MUTT_TOKEN_QUESTION)) ||
2669  ((ch == ';') && !(flags & MUTT_TOKEN_SEMICOLON)) ||
2670  ((flags & MUTT_TOKEN_PATTERN) && strchr("~%=!|", ch)))
2671  {
2672  break;
2673  }
2674  }
2675 
2676  tok->dptr++;
2677 
2678  if (ch == qc)
2679  qc = 0; /* end of quote */
2680  else if (!qc && ((ch == '\'') || (ch == '"')) && !(flags & MUTT_TOKEN_QUOTE))
2681  qc = ch;
2682  else if ((ch == '\\') && (qc != '\''))
2683  {
2684  if (tok->dptr[0] == '\0')
2685  return -1; /* premature end of token */
2686  switch (ch = *tok->dptr++)
2687  {
2688  case 'c':
2689  case 'C':
2690  if (tok->dptr[0] == '\0')
2691  return -1; /* premature end of token */
2692  mutt_buffer_addch(dest, (toupper((unsigned char) tok->dptr[0]) - '@') & 0x7f);
2693  tok->dptr++;
2694  break;
2695  case 'e':
2696  mutt_buffer_addch(dest, '\033'); // Escape
2697  break;
2698  case 'f':
2699  mutt_buffer_addch(dest, '\f');
2700  break;
2701  case 'n':
2702  mutt_buffer_addch(dest, '\n');
2703  break;
2704  case 'r':
2705  mutt_buffer_addch(dest, '\r');
2706  break;
2707  case 't':
2708  mutt_buffer_addch(dest, '\t');
2709  break;
2710  default:
2711  if (isdigit((unsigned char) ch) && isdigit((unsigned char) tok->dptr[0]) &&
2712  isdigit((unsigned char) tok->dptr[1]))
2713  {
2714  mutt_buffer_addch(dest, (ch << 6) + (tok->dptr[0] << 3) + tok->dptr[1] - 3504);
2715  tok->dptr += 2;
2716  }
2717  else
2718  mutt_buffer_addch(dest, ch);
2719  }
2720  }
2721  else if ((ch == '^') && (flags & MUTT_TOKEN_CONDENSE))
2722  {
2723  if (tok->dptr[0] == '\0')
2724  return -1; /* premature end of token */
2725  ch = *tok->dptr++;
2726  if (ch == '^')
2727  mutt_buffer_addch(dest, ch);
2728  else if (ch == '[')
2729  mutt_buffer_addch(dest, '\033'); // Escape
2730  else if (isalpha((unsigned char) ch))
2731  mutt_buffer_addch(dest, toupper((unsigned char) ch) - '@');
2732  else
2733  {
2734  mutt_buffer_addch(dest, '^');
2735  mutt_buffer_addch(dest, ch);
2736  }
2737  }
2738  else if ((ch == '`') && (!qc || (qc == '"')))
2739  {
2740  FILE *fp = NULL;
2741  pid_t pid;
2742  char *ptr = NULL;
2743  size_t expnlen;
2744  struct Buffer expn;
2745  int line = 0;
2746 
2747  pc = tok->dptr;
2748  do
2749  {
2750  pc = strpbrk(pc, "\\`");
2751  if (pc)
2752  {
2753  /* skip any quoted chars */
2754  if (*pc == '\\')
2755  pc += 2;
2756  }
2757  } while (pc && (pc[0] != '`'));
2758  if (!pc)
2759  {
2760  mutt_debug(LL_DEBUG1, "mismatched backticks\n");
2761  return -1;
2762  }
2763  struct Buffer cmd;
2764  mutt_buffer_init(&cmd);
2765  *pc = '\0';
2766  if (flags & MUTT_TOKEN_BACKTICK_VARS)
2767  {
2768  /* recursively extract tokens to interpolate variables */
2769  mutt_extract_token(&cmd, tok,
2770  MUTT_TOKEN_QUOTE | MUTT_TOKEN_SPACE | MUTT_TOKEN_COMMENT |
2772  }
2773  else
2774  {
2775  cmd.data = mutt_str_strdup(tok->dptr);
2776  }
2777  *pc = '`';
2778  pid = mutt_create_filter(cmd.data, NULL, &fp, NULL);
2779  if (pid < 0)
2780  {
2781  mutt_debug(LL_DEBUG1, "unable to fork command: %s\n", cmd.data);
2782  FREE(&cmd.data);
2783  return -1;
2784  }
2785  FREE(&cmd.data);
2786 
2787  tok->dptr = pc + 1;
2788 
2789  /* read line */
2790  mutt_buffer_init(&expn);
2791  expn.data = mutt_file_read_line(NULL, &expn.dsize, fp, &line, 0);
2792  mutt_file_fclose(&fp);
2793  mutt_wait_filter(pid);
2794 
2795  /* if we got output, make a new string consisting of the shell output
2796  * plus whatever else was left on the original line */
2797  /* BUT: If this is inside a quoted string, directly add output to
2798  * the token */
2799  if (expn.data && qc)
2800  {
2801  mutt_buffer_addstr(dest, expn.data);
2802  FREE(&expn.data);
2803  }
2804  else if (expn.data)
2805  {
2806  expnlen = mutt_str_strlen(expn.data);
2807  tok->dsize = expnlen + mutt_str_strlen(tok->dptr) + 1;
2808  ptr = mutt_mem_malloc(tok->dsize);
2809  memcpy(ptr, expn.data, expnlen);
2810  strcpy(ptr + expnlen, tok->dptr);
2811  tok->data = mutt_str_strdup(ptr);
2812  tok->dptr = tok->data;
2813  ptr = NULL;
2814  FREE(&expn.data);
2815  }
2816  }
2817  else if ((ch == '$') && (!qc || (qc == '"')) &&
2818  ((tok->dptr[0] == '{') || isalpha((unsigned char) tok->dptr[0])))
2819  {
2820  const char *env = NULL;
2821  char *var = NULL;
2822 
2823  if (tok->dptr[0] == '{')
2824  {
2825  pc = strchr(tok->dptr, '}');
2826  if (pc)
2827  {
2828  var = mutt_str_substr_dup(tok->dptr + 1, pc);
2829  tok->dptr = pc + 1;
2830 
2831  if ((flags & MUTT_TOKEN_NOSHELL))
2832  {
2833  mutt_buffer_addch(dest, ch);
2834  mutt_buffer_addch(dest, '{');
2835  mutt_buffer_addstr(dest, var);
2836  mutt_buffer_addch(dest, '}');
2837  FREE(&var);
2838  }
2839  }
2840  }
2841  else
2842  {
2843  for (pc = tok->dptr; isalnum((unsigned char) *pc) || (pc[0] == '_'); pc++)
2844  ;
2845  var = mutt_str_substr_dup(tok->dptr, pc);
2846  tok->dptr = pc;
2847  }
2848  if (var)
2849  {
2850  struct Buffer result;
2851  mutt_buffer_init(&result);
2852  int rc = cs_str_string_get(Config, var, &result);
2853 
2854  if (CSR_RESULT(rc) == CSR_SUCCESS)
2855  {
2856  mutt_buffer_addstr(dest, result.data);
2857  FREE(&result.data);
2858  }
2859  else if ((env = myvar_get(var)))
2860  {
2861  mutt_buffer_addstr(dest, env);
2862  }
2863  else if (!(flags & MUTT_TOKEN_NOSHELL) && (env = mutt_str_getenv(var)))
2864  {
2865  mutt_buffer_addstr(dest, env);
2866  }
2867  else
2868  {
2869  mutt_buffer_addch(dest, ch);
2870  mutt_buffer_addstr(dest, var);
2871  }
2872  FREE(&var);
2873  }
2874  }
2875  else
2876  mutt_buffer_addch(dest, ch);
2877  }
2878  mutt_buffer_addch(dest, 0); /* terminate the string */
2879  SKIPWS(tok->dptr);
2880  return 0;
2881 }
2882 
2886 void mutt_opts_free(void)
2887 {
2889 
2890  FREE(&Matches);
2891 
2892  mutt_aliaslist_free(&Aliases);
2893 
2894  mutt_regexlist_free(&Alternates);
2898  mutt_regexlist_free(&UnAlternates);
2901 
2906 
2907  /* Lists of strings */
2908  mutt_list_free(&AlternativeOrderList);
2909  mutt_list_free(&AutoViewList);
2910  mutt_list_free(&HeaderOrderList);
2913  mutt_list_free(&MimeLookupList);
2914  mutt_list_free(&Muttrc);
2916 #ifdef USE_SIDEBAR
2917  mutt_list_free(&SidebarWhitelist);
2918 #endif
2920  mutt_list_free(&UserHeader);
2921 
2922  /* Lists of AttachMatch */
2924  mutt_list_free_type(&AttachExclude, (list_free_t) mutt_attachmatch_free);
2925  mutt_list_free_type(&InlineAllow, (list_free_t) mutt_attachmatch_free);
2926  mutt_list_free_type(&InlineExclude, (list_free_t) mutt_attachmatch_free);
2927 
2928  mutt_colors_free();
2929 
2930  FREE(&CurrentFolder);
2931  FREE(&HomeDir);
2932  FREE(&LastFolder);
2933  FREE(&ShortHostname);
2934  FREE(&Username);
2935 
2938 
2940 
2941  mutt_hist_free();
2942  mutt_keys_free();
2943 
2945 }
2946 
2954 {
2955  for (const struct Command *c = Commands; c->name; c++)
2956  {
2957  if (((c->func == mutt_parse_hook) || (c->func == mutt_parse_idxfmt_hook)) &&
2958  (mutt_str_strcasecmp(c->name, name) == 0))
2959  {
2960  return c->data;
2961  }
2962  }
2963  return MUTT_HOOK_NO_FLAGS;
2964 }
2965 
2973 int mutt_init(bool skip_sys_rc, struct ListHead *commands)
2974 {
2975  char buf[1024];
2976  int need_pause = 0;
2977  struct Buffer err = mutt_buffer_make(256);
2978 
2980  /* reverse alias keys need to be strdup'ed because of idna conversions */
2985 
2986  mutt_menu_init();
2987 
2988  snprintf(AttachmentMarker, sizeof(AttachmentMarker), "\033]9;%" PRIu64 "\a", // Escape
2989  mutt_rand64());
2990 
2991  snprintf(ProtectedHeaderMarker, sizeof(ProtectedHeaderMarker), "\033]8;%lld\a", // Escape
2992  (long long) mutt_date_epoch());
2993 
2994  /* "$spoolfile" precedence: config file, environment */
2995  const char *p = mutt_str_getenv("MAIL");
2996  if (!p)
2997  p = mutt_str_getenv("MAILDIR");
2998  if (!p)
2999  {
3000 #ifdef HOMESPOOL
3001  mutt_path_concat(buf, NONULL(HomeDir), MAILPATH, sizeof(buf));
3002 #else
3003  mutt_path_concat(buf, MAILPATH, NONULL(Username), sizeof(buf));
3004 #endif
3005  p = buf;
3006  }
3007  cs_str_initial_set(Config, "spoolfile", p, NULL);
3008  cs_str_reset(Config, "spoolfile", NULL);
3009 
3010  p = mutt_str_getenv("REPLYTO");
3011  if (p)
3012  {
3013  struct Buffer tmp, token;
3014 
3015  snprintf(buf, sizeof(buf), "Reply-To: %s", p);
3016 
3017  mutt_buffer_init(&tmp);
3018  tmp.data = buf;
3019  tmp.dptr = buf;
3020  tmp.dsize = mutt_str_strlen(buf);
3021 
3022  mutt_buffer_init(&token);
3023  parse_my_hdr(&token, &tmp, 0, &err); /* adds to UserHeader */
3024  FREE(&token.data);
3025  }
3026 
3027  p = mutt_str_getenv("EMAIL");
3028  if (p)
3029  {
3030  cs_str_initial_set(Config, "from", p, NULL);
3031  cs_str_reset(Config, "from", NULL);
3032  }
3033 
3034  /* "$mailcap_path" precedence: config file, environment, code */
3035  const char *env_mc = mutt_str_getenv("MAILCAPS");
3036  if (env_mc)
3037  cs_str_string_set(Config, "mailcap_path", env_mc, NULL);
3038 
3039  /* "$tmpdir" precedence: config file, environment, code */
3040  const char *env_tmp = mutt_str_getenv("TMPDIR");
3041  if (env_tmp)
3042  cs_str_string_set(Config, "tmpdir", env_tmp, NULL);
3043 
3044  /* "$visual", "$editor" precedence: config file, environment, code */
3045  const char *env_ed = mutt_str_getenv("VISUAL");
3046  if (!env_ed)
3047  env_ed = mutt_str_getenv("EDITOR");
3048  if (env_ed)
3049  {
3050  cs_str_string_set(Config, "editor", env_ed, NULL);
3051  cs_str_string_set(Config, "visual", env_ed, NULL);
3052  }
3053 
3056 
3057  Matches = mutt_mem_calloc(MatchesListsize, sizeof(char *));
3058 
3060 
3061 #ifdef HAVE_GETSID
3062  /* Unset suspend by default if we're the session leader */
3063  if (getsid(0) == getpid())
3064  C_Suspend = false;
3065 #endif
3066 
3067  /* RFC2368, "4. Unsafe headers"
3068  * The creator of a mailto URL can't expect the resolver of a URL to
3069  * understand more than the "subject" and "body" headers. Clients that
3070  * resolve mailto URLs into mail messages should be able to correctly
3071  * create RFC822-compliant mail messages using the "subject" and "body"
3072  * headers. */
3073  add_to_stailq(&MailToAllow, "body");
3074  add_to_stailq(&MailToAllow, "subject");
3075  /* Cc, In-Reply-To, and References help with not breaking threading on
3076  * mailing lists, see https://github.com/neomutt/neomutt/issues/115 */
3077  add_to_stailq(&MailToAllow, "cc");
3078  add_to_stailq(&MailToAllow, "in-reply-to");
3079  add_to_stailq(&MailToAllow, "references");
3080 
3081  if (STAILQ_EMPTY(&Muttrc))
3082  {
3083  const char *xdg_cfg_home = mutt_str_getenv("XDG_CONFIG_HOME");
3084 
3085  if (!xdg_cfg_home && HomeDir)
3086  {
3087  snprintf(buf, sizeof(buf), "%s/.config", HomeDir);
3088  xdg_cfg_home = buf;
3089  }
3090 
3091  char *config = find_cfg(HomeDir, xdg_cfg_home);
3092  if (config)
3093  {
3094  mutt_list_insert_tail(&Muttrc, config);
3095  }
3096  }
3097  else
3098  {
3099  struct ListNode *np = NULL;
3100  STAILQ_FOREACH(np, &Muttrc, entries)
3101  {
3102  mutt_str_strfcpy(buf, np->data, sizeof(buf));
3103  FREE(&np->data);
3104  mutt_expand_path(buf, sizeof(buf));
3105  np->data = mutt_str_strdup(buf);
3106  if (access(np->data, F_OK))
3107  {
3108  mutt_perror(np->data);
3109  return 1; // TEST10: neomutt -F missing
3110  }
3111  }
3112  }
3113 
3114  if (!STAILQ_EMPTY(&Muttrc))
3115  {
3116  cs_str_string_set(Config, "alias_file", STAILQ_FIRST(&Muttrc)->data, NULL);
3117  }
3118 
3119  /* Process the global rc file if it exists and the user hasn't explicitly
3120  * requested not to via "-n". */
3121  if (!skip_sys_rc)
3122  {
3123  do
3124  {
3125  if (mutt_set_xdg_path(XDG_CONFIG_DIRS, buf, sizeof(buf)))
3126  break;
3127 
3128  snprintf(buf, sizeof(buf), "%s/neomuttrc", SYSCONFDIR);
3129  if (access(buf, F_OK) == 0)
3130  break;
3131 
3132  snprintf(buf, sizeof(buf), "%s/Muttrc", SYSCONFDIR);
3133  if (access(buf, F_OK) == 0)
3134  break;
3135 
3136  snprintf(buf, sizeof(buf), "%s/neomuttrc", PKGDATADIR);
3137  if (access(buf, F_OK) == 0)
3138  break;
3139 
3140  snprintf(buf, sizeof(buf), "%s/Muttrc", PKGDATADIR);
3141  } while (false);
3142 
3143  if (access(buf, F_OK) == 0)
3144  {
3145  if (source_rc(buf, &err) != 0)
3146  {
3147  mutt_error("%s", err.data);
3148  need_pause = 1; // TEST11: neomutt (error in /etc/neomuttrc)
3149  }
3150  }
3151  }
3152 
3153  /* Read the user's initialization file. */
3154  struct ListNode *np = NULL;
3155  STAILQ_FOREACH(np, &Muttrc, entries)
3156  {
3157  if (np->data)
3158  {
3159  if (source_rc(np->data, &err) != 0)
3160  {
3161  mutt_error("%s", err.data);
3162  need_pause = 1; // TEST12: neomutt (error in ~/.neomuttrc)
3163  }
3164  }
3165  }
3166 
3167  if (execute_commands(commands) != 0)
3168  need_pause = 1; // TEST13: neomutt -e broken
3169 
3170  if (!get_hostname())
3171  return 1;
3172 
3173  if (!C_Realname)
3174  {
3175  struct passwd *pw = getpwuid(getuid());
3176  if (pw)
3177  {
3178  char name[256];
3179  C_Realname = mutt_str_strdup(mutt_gecos_name(name, sizeof(name), pw));
3180  }
3181  }
3182  cs_str_initial_set(Config, "realname", C_Realname, NULL);
3183 
3184  if (need_pause && !OptNoCurses)
3185  {
3187  if (mutt_any_key_to_continue(NULL) == 'q')
3188  return 1; // TEST14: neomutt -e broken (press 'q')
3189  }
3190 
3191  mutt_file_mkdir(C_Tmpdir, S_IRWXU);
3192 
3193  mutt_hist_init();
3195 
3196 #ifdef USE_NOTMUCH
3197  if (C_VirtualSpoolfile)
3198  {
3199  /* Find the first virtual folder and open it */
3200  struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_NOTMUCH);
3201  struct MailboxNode *mp = STAILQ_FIRST(&ml);
3202  if (mp)
3203  cs_str_string_set(Config, "spoolfile", mailbox_path(mp->mailbox), NULL);
3205  }
3206 #endif
3207 
3208  FREE(&err.data);
3209  return 0;
3210 }
3211 
3224 enum CommandResult mutt_parse_rc_line(/* const */ char *line,
3225  struct Buffer *token, struct Buffer *err)
3226 {
3227  if (!line || !*line)
3228  return 0;
3229 
3230  int i;
3231  enum CommandResult rc = MUTT_CMD_SUCCESS;
3232 
3233  struct Buffer expn = mutt_buffer_make(0);
3234  mutt_buffer_addstr(&expn, line);
3235  expn.dptr = expn.data;
3236 
3237  *err->data = 0;
3238 
3239  SKIPWS(expn.dptr);
3240  while (*expn.dptr != '\0')
3241  {
3242  if (*expn.dptr == '#')
3243  break; /* rest of line is a comment */
3244  if (*expn.dptr == ';')
3245  {
3246  expn.dptr++;
3247  continue;
3248  }
3249  mutt_extract_token(token, &expn, MUTT_TOKEN_NO_FLAGS);
3250  for (i = 0; Commands[i].name; i++)
3251  {
3252  if (mutt_str_strcmp(token->data, Commands[i].name) == 0)
3253  {
3254  rc = Commands[i].func(token, &expn, Commands[i].data, err);
3255  if (rc != MUTT_CMD_SUCCESS)
3256  { /* -1 Error, +1 Finish */
3257  goto finish; /* Propagate return code */
3258  }
3259  break; /* Continue with next command */
3260  }
3261  }
3262  if (!Commands[i].name)
3263  {
3264  mutt_buffer_printf(err, _("%s: unknown command"), NONULL(token->data));
3265  rc = MUTT_CMD_ERROR;
3266  break; /* Ignore the rest of the line */
3267  }
3268  }
3269 finish:
3270  mutt_buffer_dealloc(&expn);
3271  return rc;
3272 }
3273 
3280 int mutt_query_variables(struct ListHead *queries)
3281 {
3282  struct Buffer value = mutt_buffer_make(256);
3283  struct Buffer tmp = mutt_buffer_make(256);
3284  int rc = 0;
3285 
3286  struct ListNode *np = NULL;
3287  STAILQ_FOREACH(np, queries, entries)
3288  {
3289  mutt_buffer_reset(&value);
3290 
3291  struct HashElem *he = cs_get_elem(Config, np->data);
3292  if (!he)
3293  {
3294  rc = 1;
3295  continue;
3296  }
3297 
3298  int rv = cs_he_string_get(Config, he, &value);
3299  if (CSR_RESULT(rv) != CSR_SUCCESS)
3300  {
3301  rc = 1;
3302  continue;
3303  }
3304 
3305  int type = DTYPE(he->type);
3306  if (IS_PATH(he) && !(he->type & DT_MAILBOX))
3307  mutt_pretty_mailbox(value.data, value.dsize);
3308 
3309  if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_LONG) && (type != DT_QUAD))
3310  {
3311  mutt_buffer_reset(&tmp);
3312  pretty_var(value.data, &tmp);
3313  mutt_buffer_strcpy(&value, tmp.data);
3314  }
3315 
3316  dump_config_neo(Config, he, &value, NULL, CS_DUMP_NO_FLAGS, stdout);
3317  }
3318 
3319  mutt_buffer_dealloc(&value);
3320  mutt_buffer_dealloc(&tmp);
3321 
3322  return rc; // TEST16: neomutt -Q charset
3323 }
3324 
3331 enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
3332 {
3333  switch (opt)
3334  {
3335  case MUTT_YES:
3336  case MUTT_NO:
3337  return opt;
3338 
3339  default:
3340  opt = mutt_yesorno(prompt, (opt == MUTT_ASKYES) ? MUTT_YES : MUTT_NO);
3342  return opt;
3343  }
3344 
3345  /* not reached */
3346 }
3347 
3357 int mutt_command_complete(char *buf, size_t buflen, int pos, int numtabs)
3358 {
3359  char *pt = buf;
3360  int num;
3361  int spaces; /* keep track of the number of leading spaces on the line */
3362  struct MyVar *myv = NULL;
3363 
3364  SKIPWS(buf);
3365  spaces = buf - pt;
3366 
3367  pt = buf + pos - spaces;
3368  while ((pt > buf) && !isspace((unsigned char) *pt))
3369  pt--;
3370 
3371  if (pt == buf) /* complete cmd */
3372  {
3373  /* first TAB. Collect all the matches */
3374  if (numtabs == 1)
3375  {
3376  NumMatched = 0;
3377  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
3378  memset(Matches, 0, MatchesListsize);
3379  memset(Completed, 0, sizeof(Completed));
3380  for (num = 0; Commands[num].name; num++)
3384 
3385  /* All matches are stored. Longest non-ambiguous string is ""
3386  * i.e. don't change 'buf'. Fake successful return this time */
3387  if (UserTyped[0] == '\0')
3388  return 1;
3389  }
3390 
3391  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3392  return 0;
3393 
3394  /* NumMatched will _always_ be at least 1 since the initial
3395  * user-typed string is always stored */
3396  if ((numtabs == 1) && (NumMatched == 2))
3397  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3398  else if ((numtabs > 1) && (NumMatched > 2))
3399  {
3400  /* cycle through all the matches */
3401  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3402  }
3403 
3404  /* return the completed command */
3405  strncpy(buf, Completed, buflen - spaces);
3406  }
3407  else if (mutt_str_startswith(buf, "set", CASE_MATCH) ||
3408  mutt_str_startswith(buf, "unset", CASE_MATCH) ||
3409  mutt_str_startswith(buf, "reset", CASE_MATCH) ||
3410  mutt_str_startswith(buf, "toggle", CASE_MATCH))
3411  { /* complete variables */
3412  static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
3413 
3414  pt++;
3415  /* loop through all the possible prefixes (no, inv, ...) */
3416  if (mutt_str_startswith(buf, "set", CASE_MATCH))
3417  {
3418  for (num = 0; prefixes[num]; num++)
3419  {
3420  if (mutt_str_startswith(pt, prefixes[num], CASE_MATCH))
3421  {
3422  pt += mutt_str_strlen(prefixes[num]);
3423  break;
3424  }
3425  }
3426  }
3427 
3428  /* first TAB. Collect all the matches */
3429  if (numtabs == 1)
3430  {
3431  NumMatched = 0;
3432  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
3433  memset(Matches, 0, MatchesListsize);
3434  memset(Completed, 0, sizeof(Completed));
3435  for (num = 0; MuttVars[num].name; num++)
3437  TAILQ_FOREACH(myv, &MyVars, entries)
3438  {
3439  candidate(UserTyped, myv->name, Completed, sizeof(Completed));
3440  }
3443 
3444  /* All matches are stored. Longest non-ambiguous string is ""
3445  * i.e. don't change 'buf'. Fake successful return this time */
3446  if (UserTyped[0] == '\0')
3447  return 1;
3448  }
3449 
3450  if ((Completed[0] == 0) && UserTyped[0])
3451  return 0;
3452 
3453  /* NumMatched will _always_ be at least 1 since the initial
3454  * user-typed string is always stored */
3455  if ((numtabs == 1) && (NumMatched == 2))
3456  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3457  else if ((numtabs > 1) && (NumMatched > 2))
3458  {
3459  /* cycle through all the matches */
3460  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3461  }
3462 
3463  strncpy(pt, Completed, buf + buflen - pt - spaces);
3464  }
3465  else if (mutt_str_startswith(buf, "exec", CASE_MATCH))
3466  {
3467  const struct Binding *menu = km_get_table(CurrentMenu);
3468 
3469  if (!menu && (CurrentMenu != MENU_PAGER))
3470  menu = OpGeneric;
3471 
3472  pt++;
3473  /* first TAB. Collect all the matches */
3474  if (numtabs == 1)
3475  {
3476  NumMatched = 0;
3477  mutt_str_strfcpy(UserTyped, pt, sizeof(UserTyped));
3478  memset(Matches, 0, MatchesListsize);
3479  memset(Completed, 0, sizeof(Completed));
3480  for (num = 0; menu[num].name; num++)
3481  candidate(UserTyped, menu[num].name, Completed, sizeof(Completed));
3482  /* try the generic menu */
3483  if ((Completed[0] == '\0') && (CurrentMenu != MENU_PAGER))
3484  {
3485  menu = OpGeneric;
3486  for (num = 0; menu[num].name; num++)
3487  candidate(UserTyped, menu[num].name, Completed, sizeof(Completed));
3488  }
3491 
3492  /* All matches are stored. Longest non-ambiguous string is ""
3493  * i.e. don't change 'buf'. Fake successful return this time */
3494  if (UserTyped[0] == '\0')
3495  return 1;
3496  }
3497 
3498  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3499  return 0;
3500 
3501  /* NumMatched will _always_ be at least 1 since the initial
3502  * user-typed string is always stored */
3503  if ((numtabs == 1) && (NumMatched == 2))
3504  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3505  else if ((numtabs > 1) && (NumMatched > 2))
3506  {
3507  /* cycle through all the matches */
3508  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3509  }
3510 
3511  strncpy(pt, Completed, buf + buflen - pt - spaces);
3512  }
3513  else
3514  return 0;
3515 
3516  return 1;
3517 }
3518 
3527 int mutt_label_complete(char *buf, size_t buflen, int numtabs)
3528 {
3529  char *pt = buf;
3530  int spaces; /* keep track of the number of leading spaces on the line */
3531 
3532  if (!Context || !Context->mailbox->label_hash)
3533  return 0;
3534 
3535  SKIPWS(buf);
3536  spaces = buf - pt;
3537 
3538  /* first TAB. Collect all the matches */
3539  if (numtabs == 1)
3540  {
3541  struct HashElem *entry = NULL;
3542  struct HashWalkState state = { 0 };
3543 
3544  NumMatched = 0;
3545  mutt_str_strfcpy(UserTyped, buf, sizeof(UserTyped));
3546  memset(Matches, 0, MatchesListsize);
3547  memset(Completed, 0, sizeof(Completed));
3548  while ((entry = mutt_hash_walk(Context->mailbox->label_hash, &state)))
3549  candidate(UserTyped, entry->key.strkey, Completed, sizeof(Completed));
3551  qsort(Matches, NumMatched, sizeof(char *), (sort_t *) mutt_str_strcasecmp);
3553 
3554  /* All matches are stored. Longest non-ambiguous string is ""
3555  * i.e. don't change 'buf'. Fake successful return this time */
3556  if (UserTyped[0] == '\0')
3557  return 1;
3558  }
3559 
3560  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3561  return 0;
3562 
3563  /* NumMatched will _always_ be at least 1 since the initial
3564  * user-typed string is always stored */
3565  if ((numtabs == 1) && (NumMatched == 2))
3566  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3567  else if ((numtabs > 1) && (NumMatched > 2))
3568  {
3569  /* cycle through all the matches */
3570  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3571  }
3572 
3573  /* return the completed label */
3574  strncpy(buf, Completed, buflen - spaces);
3575 
3576  return 1;
3577 }
3578 
3579 #ifdef USE_NOTMUCH
3580 
3591 bool mutt_nm_query_complete(char *buf, size_t buflen, int pos, int numtabs)
3592 {
3593  char *pt = buf;
3594  int spaces;
3595 
3596  SKIPWS(buf);
3597  spaces = buf - pt;
3598 
3599  pt = (char *) mutt_str_rstrnstr((char *) buf, pos, "tag:");
3600  if (pt)
3601  {
3602  pt += 4;
3603  if (numtabs == 1)
3604  {
3605  /* First TAB. Collect all the matches */
3607 
3608  /* All matches are stored. Longest non-ambiguous string is ""
3609  * i.e. don't change 'buf'. Fake successful return this time. */
3610  if (UserTyped[0] == '\0')
3611  return true;
3612  }
3613 
3614  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3615  return false;
3616 
3617  /* NumMatched will _always_ be at least 1 since the initial
3618  * user-typed string is always stored */
3619  if ((numtabs == 1) && (NumMatched == 2))
3620  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3621  else if ((numtabs > 1) && (NumMatched > 2))
3622  {
3623  /* cycle through all the matches */
3624  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3625  }
3626 
3627  /* return the completed query */
3628  strncpy(pt, Completed, buf + buflen - pt - spaces);
3629  }
3630  else
3631  return false;
3632 
3633  return true;
3634 }
3635 #endif
3636 
3637 #ifdef USE_NOTMUCH
3638 
3648 bool mutt_nm_tag_complete(char *buf, size_t buflen, int numtabs)
3649 {
3650  if (!buf)
3651  return false;
3652 
3653  char *pt = buf;
3654 
3655  /* Only examine the last token */
3656  char *last_space = strrchr(buf, ' ');
3657  if (last_space)
3658  pt = (last_space + 1);
3659 
3660  /* Skip the +/- */
3661  if ((pt[0] == '+') || (pt[0] == '-'))
3662  pt++;
3663 
3664  if (numtabs == 1)
3665  {
3666  /* First TAB. Collect all the matches */
3668 
3669  /* All matches are stored. Longest non-ambiguous string is ""
3670  * i.e. don't change 'buf'. Fake successful return this time. */
3671  if (UserTyped[0] == '\0')
3672  return true;
3673  }
3674 
3675  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
3676  return false;
3677 
3678  /* NumMatched will _always_ be at least 1 since the initial
3679  * user-typed string is always stored */
3680  if ((numtabs == 1) && (NumMatched == 2))
3681  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
3682  else if ((numtabs > 1) && (NumMatched > 2))
3683  {
3684  /* cycle through all the matches */
3685  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
3686  }
3687 
3688  /* return the completed query */
3689  strncpy(pt, Completed, buf + buflen - pt);
3690 
3691  return true;
3692 }
3693 #endif
3694 
3701 int mutt_var_value_complete(char *buf, size_t buflen, int pos)
3702 {
3703  char *pt = buf;
3704 
3705  if (buf[0] == '\0')
3706  return 0;
3707 
3708  SKIPWS(buf);
3709  const int spaces = buf - pt;
3710 
3711  pt = buf + pos - spaces;
3712  while ((pt > buf) && !isspace((unsigned char) *pt))
3713  pt--;
3714  pt++; /* move past the space */
3715  if (*pt == '=') /* abort if no var before the '=' */
3716  return 0;
3717 
3718  if (mutt_str_startswith(buf, "set", CASE_MATCH))
3719  {
3720  const char *myvarval = NULL;
3721  char var[256];
3722  mutt_str_strfcpy(var, pt, sizeof(var));
3723  /* ignore the trailing '=' when comparing */
3724  int vlen = mutt_str_strlen(var);
3725  if (vlen == 0)
3726  return 0;
3727 
3728  var[vlen - 1] = '\0';
3729 
3730  struct HashElem *he = cs_get_elem(Config, var);
3731  if (!he)
3732  {
3733  myvarval = myvar_get(var);
3734  if (myvarval)
3735  {
3736  struct Buffer pretty = mutt_buffer_make(256);
3737  pretty_var(myvarval, &pretty);
3738  snprintf(pt, buflen - (pt - buf), "%s=%s", var, pretty.data);
3739  mutt_buffer_dealloc(&pretty);
3740  return 1;
3741  }
3742  return 0; /* no such variable. */
3743  }
3744  else
3745  {
3746  struct Buffer value = mutt_buffer_make(256);
3747  struct Buffer pretty = mutt_buffer_make(256);
3748  int rc = cs_he_string_get(Config, he, &value);
3749  if (CSR_RESULT(rc) == CSR_SUCCESS)
3750  {
3751  pretty_var(value.data, &pretty);
3752  snprintf(pt, buflen - (pt - buf), "%s=%s", var, pretty.data);
3753  mutt_buffer_dealloc(&value);
3754  mutt_buffer_dealloc(&pretty);
3755  return 0;
3756  }
3757  mutt_buffer_dealloc(&value);
3758  mutt_buffer_dealloc(&pretty);
3759  return 1;
3760  }
3761  }
3762  return 0;
3763 }
3764 
3769 struct ConfigSet *init_config(size_t size)
3770 {
3771  struct ConfigSet *cs = cs_new(size);
3772 
3773  address_init(cs);
3774  bool_init(cs);
3775  enum_init(cs);
3776  long_init(cs);
3777  mbtable_init(cs);
3778  number_init(cs);
3779  quad_init(cs);
3780  regex_init(cs);
3781  slist_init(cs);
3782  sort_init(cs);
3783  string_init(cs);
3784 
3785  if (!cs_register_variables(cs, MuttVars, 0))
3786  {
3787  mutt_error("cs_register_variables() failed");
3788  cs_free(&cs);
3789  return NULL;
3790  }
3791 
3792  return cs;
3793 }
3794 
3798 int charset_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3799  intptr_t value, struct Buffer *err)
3800 {
3801  if (value == 0)
3802  return CSR_SUCCESS;
3803 
3804  const char *str = (const char *) value;
3805 
3806  if ((strcmp(cdef->name, "charset") == 0) && strchr(str, ':'))
3807  {
3809  err, _("'charset' must contain exactly one character set name"));
3810  return CSR_ERR_INVALID;
3811  }
3812 
3813  int rc = CSR_SUCCESS;
3814  bool strict = (strcmp(cdef->name, "send_charset") == 0);
3815  char *q = NULL;
3816  char *s = mutt_str_strdup(str);
3817 
3818  for (char *p = strtok_r(s, ":", &q); p; p = strtok_r(NULL, ":", &q))
3819  {
3820  if (!*p)
3821  continue;
3822  if (!mutt_ch_check_charset(p, strict))
3823  {
3824  rc = CSR_ERR_INVALID;
3825  mutt_buffer_printf(err, _("Invalid value for option %s: %s"), cdef->name, p);
3826  break;
3827  }
3828  }
3829 
3830  FREE(&s);
3831  return rc;
3832 }
3833 
3834 #ifdef USE_HCACHE
3835 
3838 int hcache_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3839  intptr_t value, struct Buffer *err)
3840 {
3841  if (value == 0)
3842  return CSR_SUCCESS;
3843 
3844  const char *str = (const char *) value;
3845 
3847  return CSR_SUCCESS;
3848 
3849  mutt_buffer_printf(err, _("Invalid value for option %s: %s"), cdef->name, str);
3850  return CSR_ERR_INVALID;
3851 }
3852 #endif
3853 
3857 int pager_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3858  intptr_t value, struct Buffer *err)
3859 {
3860  if (CurrentMenu == MENU_PAGER)
3861  {
3862  mutt_buffer_printf(err, _("Option %s may not be set or reset from the pager"),
3863  cdef->name);
3864  return CSR_ERR_INVALID;
3865  }
3866 
3867  return CSR_SUCCESS;
3868 }
3869 
3873 int multipart_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3874  intptr_t value, struct Buffer *err)
3875 {
3876  if (value == 0)
3877  return CSR_SUCCESS;
3878 
3879  const char *str = (const char *) value;
3880 
3881  if ((mutt_str_strcmp(str, "inline") == 0) || (mutt_str_strcmp(str, "info") == 0))
3882  return CSR_SUCCESS;
3883 
3884  mutt_buffer_printf(err, _("Invalid value for option %s: %s"), cdef->name, str);
3885  return CSR_ERR_INVALID;
3886 }
3887 
3891 int reply_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
3892  intptr_t value, struct Buffer *err)
3893 {
3894  if (pager_validator(cs, cdef, value, err) != CSR_SUCCESS)
3895  return CSR_ERR_INVALID;
3896 
3897  if (!OptAttachMsg)
3898  return CSR_SUCCESS;
3899 
3900  mutt_buffer_printf(err, _("Option %s may not be set when in attach-message mode"),
3901  cdef->name);
3902  return CSR_ERR_INVALID;
3903 }
pid_t mutt_create_filter(const char *s, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:217
void mutt_alias_delete_reverse(struct Alias *t)
Remove an email address lookup for an Alias.
Definition: alias.c:576
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:410
A shortcut for an email address.
Definition: alias.h:37
WHERE char * Username
User&#39;s login name.
Definition: globals.h:52
#define mutt_warning(...)
Definition: logging.h:82
struct ConfigSet * init_config(size_t size)
Initialise the config system.
Definition: init.c:3769
The "current" mailbox.
Definition: context.h:36
static struct AttachMatch * mutt_attachmatch_new(void)
Create a new AttachMatch.
Definition: init.c:461
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:330
union HashKey key
Definition: hash.h:45
Log at debug level 4.
Definition: logging.h:59
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:1471
Notmuch virtual mailbox type.
char * name
Short name.
Definition: alias.h:39
int sort_t(const void *a, const void *b)
typedef sort_t - Prototype for a function to compare two emails
Definition: sort.h:48
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:190
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:1202
Define wrapper functions around Curses/Slang.
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:188
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
#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:731
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:2262
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:100
#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:621
#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:487
void mutt_aliaslist_free(struct AliasList *a_list)
Free a List of Aliases.
Definition: alias.c:759
#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:1912
#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:1559
int mx_path_canon2(struct Mailbox *m, const char *folder)
XXX canonicalise the path to realpath.
Definition: mx.c:1393
Structs that make up an email.
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:1098
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:2019
struct Hash * TagTransforms
Lookup table of alternative tag names.
Definition: tags.c:38
#define MUTT_NOSPAM
Definition: mutt.h:121
static char * getmailname(void)
Try to retrieve the FQDN from mailname files.
Definition: init.c:356
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:103
MenuType
Types of GUI selections.
Definition: keymap.h:60
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: init.c:3331
#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:3527
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:581
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:211
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:1013
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:2529
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:2087
Pass files through external commands (filters)
int mutt_monitor_remove(struct Mailbox *m)
Remove a watch for a mailbox.
Definition: monitor.c:506
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
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:111
void number_init(struct ConfigSet *cs)
Register the Number config type.
Definition: number.c:191
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:137
WHERE char AttachmentMarker[256]
Unique ANSI string to mark PGP messages in an email.
Definition: globals.h:46
#define MUTT_TOKEN_COMMENT
Don&#39;t reap comments.
Definition: mutt.h:84
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:379
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:2953
WHERE enum MenuType CurrentMenu
Current Menu, e.g. MENU_PAGER.
Definition: globals.h:87
bool mutt_nm_tag_complete(char *buf, size_t buflen, int numtabs)
Complete to the nearest notmuch tag.
Definition: init.c:3648
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:284
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:757
Url wasn&#39;t recognised.
Definition: url.h:34
WHERE struct Hash * TagFormats
Hash table of tag-formats (tag -> format string)
Definition: globals.h:60
enum ContentType major_int
Definition: mutt.h:134
#define _(a)
Definition: message.h:28
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:316
int mutt_command_complete(char *buf, size_t buflen, int pos, int numtabs)
Complete a command name.
Definition: init.c:3357
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:662
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:894
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:979
An email address.
Definition: address.h:34
struct Mailbox * mx_mbox_find(struct Account *a, const char *path)
XXX.
Definition: mx.c:1496
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:55
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:2471
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:1287
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:68
#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:1405
void mutt_grouplist_add_addrlist(struct GroupList *gl, struct AddressList *al)
Add Address list to a GroupList.
Definition: group.c:225
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:3591
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:209
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:356
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:461
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:2212
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:315
struct Account * mx_ac_find(struct Mailbox *m)
XXX.
Definition: mx.c:1473
int getdnsdomainname(char *buf, size_t buflen)
Lookup the host&#39;s name using DNS.
Definition: getdomain.c:44
#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:2618
#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:3798
#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:1271
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1049
Email Address Handling.
WHERE char * C_ConfigCharset
Config: Character set that the config files are in.
Definition: globals.h:109
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:4972
Pager pager (email viewer)
Definition: keymap.h:69
int mutt_query_variables(struct ListHead *queries)
Implement the -Q command line flag.
Definition: init.c:3280
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:128
#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:1509
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:275
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:2406
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:332
struct Mailbox * mailbox
Definition: context.h:50
Log at debug level 2.
Definition: logging.h:57
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:2303
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:55
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:317
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:871
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:150
const char * name
Definition: pgpmicalg.c:45
#define MUTT_SPAM
Definition: mutt.h:120
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:91
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:49
const char * name
name of the function
Definition: keymap.h:107
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:145
#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:299
void mbtable_init(struct ConfigSet *cs)
Register the MbTable config type.
Definition: mbtable.c:293
default is to invert all vars
Definition: init.h:103
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:2134
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:2361
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:293
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.h:133
static char Completed[256]
Definition: init.c:91
#define MUTT_TOKEN_NOSHELL
Don&#39;t expand environment variables.
Definition: mutt.h:87
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:54
void slist_init(struct ConfigSet *cs)
Register the Slist config type.
Definition: slist.c:266
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:3838
const char * name
User-visible name.
Definition: set.h:155
default is to set all vars
Definition: init.h:102
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:2508
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:1321
#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:244
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
#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:1829
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
void myvar_set(const char *var, const char *val)
Set the value of a "my_" variable.
Definition: myvar.c:91
#define mutt_b2s(buf)
Definition: buffer.h:41
#define MUTT_TOKEN_QUOTE
Don&#39;t interpret quotes.
Definition: mutt.h:82
static void attachments_clean(void)
always wise to do what someone else did before
Definition: init.c:151
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: nm_db.c:300
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:80
const struct Mapping Menus[]
Menu name lookup table.
Definition: keymap.c:60
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:551
const char * line
Definition: common.c:36
void mutt_attachmatch_free(struct AttachMatch **ptr)
Free an AttachMatch - Implements list_free_t.
Definition: init.c:2289
#define MB_HIDDEN
Definition: mailbox.h:37
void mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
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:2381
short C_DebugLevel
Config: Logging level for debug logs.
Definition: mutt_logging.c:48
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:499
#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:124
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:1450
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:2034
void mutt_keys_free(void)
Free the key maps.
Definition: keymap.c:1597
static void matches_ensure_morespace(int current)
Allocate more space for auto-completion.
Definition: init.c:164
A mailbox.
Definition: mailbox.h:92
#define PATH_MAX
Definition: mutt.h:52
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:131
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:2649
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:1499
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
bool mutt_path_to_absolute(char *path, const char *reference)
Convert relative filepath to an absolute path.
Definition: path.c:433
void mutt_alias_free(struct Alias **ptr)
Free an Alias.
Definition: alias.c:742
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:3857
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:81
struct RegexList MailLists
List of regexes to match mailing lists.
Definition: email_globals.c:50
char * data
Pointer to data.
Definition: buffer.h:35
void mutt_colors_free(void)
Free all the colours (on shutdown)
Definition: color.c:1263
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:2543
#define MUTT_VIRTUAL
Definition: init.h:110
#define NUM_VARS
Definition: init.c:83
#define MUTT_NAMED
Definition: init.h:109
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:276
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:72
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:3891
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:1113
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:239
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:612
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
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:492
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:2886
Select an email address by its alias.
Definition: keymap.h:62
default is to reset all vars to default
Definition: init.h:105
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:1230
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: muttlib.c:540
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:363
int nm_get_all_tags(struct Mailbox *m, char **tag_list, int *tag_count)
Fill a list with all notmuch tags.
int mutt_set_xdg_path(enum XdgType type, char *buf, size_t bufsize)
Find an XDG path or its fallback.
Definition: muttlib.c:1565
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
WHERE char ProtectedHeaderMarker[256]
Unique ANSI string to mark protected headers in an email.
Definition: globals.h:47
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:3873
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:172
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:85
#define IS_SPACE(ch)
Definition: string2.h:38
WHERE struct Hash * ReverseAliases
Hash table of aliases (email address -> alias)
Definition: globals.h:59
int mutt_any_key_to_continue(const char *s)
Prompt the user to &#39;press any key&#39; and wait.
Definition: curs_lib.c:532
size_t pretty_var(const char *str, struct Buffer *buf)
Escape and stringify a config item value.
Definition: dump.c:80
static int complete_all_nm_tags(const char *pt)
Pass a list of Notmuch tags to the completion code.
Definition: init.c:227
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:504
default is to unset all vars
Definition: init.h:104
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:323
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:267
Log at debug level 1.
Definition: logging.h:56
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:362
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:225
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:129
bool account_mailbox_remove(struct Account *a, struct Mailbox *m)
Remove a Mailbox from an Account.
Definition: account.c:83
regex_t minor_regex
Definition: mutt.h:136
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:53
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:474
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:314
static int print_attach_list(struct ListHead *h, const char op, const char *name)
Print a list of attachments.
Definition: init.c:711
const struct Binding * km_get_table(enum MenuType menu)
Lookup a menu&#39;s keybindings.
Definition: keymap.c:1219
void mutt_alias_add_reverse(struct Alias *t)
Add an email address lookup for an Alias.
Definition: alias.c:554
#define MUTT_TOKEN_BACKTICK_VARS
Expand variables within backticks.
Definition: mutt.h:86
void mutt_grouplist_destroy(struct GroupList *gl)
Free a GroupList.
Definition: group.c:157
int type
Definition: hash.h:44
WHERE char * ShortHostname
Short version of the hostname.
Definition: globals.h:50
#define FREE(x)
Definition: memory.h:40
WHERE char * C_Hostname
Config: Fully-qualified domain name of this machine.
Definition: globals.h:116
#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
#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:2570
bool recip_valid
Is_recipient is valid.
Definition: email.h:60
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:2048
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:84
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:79
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:679
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:143
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: nm_db.c:285
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:77
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:2973
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:1074
An attachment matching a regex.
Definition: mutt.h:131
#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:60
struct Buffer pathbuf
Definition: mailbox.h:94
void mutt_hash_free(struct Hash **ptr)
free_hdata a hash table
Definition: hash.c:472
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:583
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:352
int mutt_wait_filter(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:228
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:1370
static bool get_hostname(void)
Find the Fully-Qualified Domain Name.
Definition: init.c:388
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:49
Handling of personal config (&#39;my&#39; variables)
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:1941
char * mutt_str_substr_dup(const char *begin, const char *end)
Duplicate a sub-string.
Definition: string.c:579
#define MUTT_TOKEN_PATTERN
~%=!| are terms (for patterns)
Definition: mutt.h:83
struct RegexList NoSpamList
List of regexes to whitelist non-spam emails.
Definition: email_globals.c:43
const char * minor
Definition: mutt.h:135
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:2173
#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:105
#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
int log_disp_terminal(time_t stamp, const char *file, int line, const char *function, int level,...)
Save a log line to the terminal - Implements log_dispatcher_t.
Definition: logging.c:449
#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:58
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:187
enum CommandResult mutt_parse_rc_line(char *line, struct Buffer *token, struct Buffer *err)
Parse a line of user config.
Definition: init.c:3224
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:78
#define MUTT_TOKEN_QUESTION
Treat &#39;?&#39; as a special.
Definition: mutt.h:88
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1384
ContentType
Content-Type.
Definition: mime.h:29
char ** mutt_envlist_getlist(void)
Get the private environment.
Definition: envlist.c:167
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:298
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:3701