NeoMutt  2021-10-22-8-g9cb437
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 <pwd.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <sys/utsname.h>
39 #include <unistd.h>
40 #include "mutt/lib.h"
41 #include "address/lib.h"
42 #include "config/lib.h"
43 #include "email/lib.h"
44 #include "core/lib.h"
45 #include "alias/lib.h"
46 #include "conn/lib.h" // IWYU pragma: keep
47 #include "gui/lib.h"
48 #include "mutt.h"
49 #include "init.h"
50 #include "color/lib.h"
51 #include "history/lib.h"
52 #include "notmuch/lib.h"
53 #include "command_parse.h"
54 #include "context.h"
55 #include "functions.h"
56 #include "keymap.h"
57 #include "mutt_commands.h"
58 #include "mutt_globals.h"
59 #ifdef USE_LUA
60 #include "mutt_lua.h"
61 #endif
62 #include "menu/lib.h"
63 #include "muttlib.h"
64 #include "myvar.h"
65 #include "options.h"
66 #include "protos.h"
67 #include "sort.h"
68 #ifdef USE_SIDEBAR
69 #include "sidebar/lib.h"
70 #endif
71 #ifdef USE_COMP_MBOX
72 #include "compmbox/lib.h"
73 #endif
74 #ifdef USE_IMAP
75 #include "imap/lib.h"
76 #endif
77 
78 /* Initial string that starts completion. No telling how much the user has
79  * typed so far. Allocate 1024 just to be sure! */
80 static char UserTyped[1024] = { 0 };
81 
82 static int NumMatched = 0; /* Number of matches for completion */
83 static char Completed[256] = { 0 }; /* completed string (command or variable) */
84 static const char **Matches;
85 /* this is a lie until mutt_init runs: */
86 static int MatchesListsize = 512; // Enough space for all of the config items
87 
88 #ifdef USE_NOTMUCH
89 /* List of tags found in last call to mutt_nm_query_complete(). */
90 static char **nm_tags;
91 #endif
92 
97 static void matches_ensure_morespace(int current)
98 {
99  if (current <= (MatchesListsize - 2))
100  return;
101 
102  int base_space = 512; // Enough space for all of the config items
103  int extra_space = MatchesListsize - base_space;
104  extra_space *= 2;
105  const int space = base_space + extra_space;
106  mutt_mem_realloc(&Matches, space * sizeof(char *));
107  memset(&Matches[current + 1], 0, space - current);
108  MatchesListsize = space;
109 }
110 
120 static void candidate(char *user, const char *src, char *dest, size_t dlen)
121 {
122  if (!dest || !user || !src)
123  return;
124 
125  if (strstr(src, user) != src)
126  return;
127 
129  Matches[NumMatched++] = src;
130  if (dest[0] == '\0')
131  mutt_str_copy(dest, src, dlen);
132  else
133  {
134  int l;
135  for (l = 0; src[l] && src[l] == dest[l]; l++)
136  ; // do nothing
137 
138  dest[l] = '\0';
139  }
140 }
141 
142 #ifdef USE_NOTMUCH
149 static int complete_all_nm_tags(const char *pt)
150 {
151  struct Mailbox *m = ctx_mailbox(Context);
152  int tag_count_1 = 0;
153  int tag_count_2 = 0;
154 
155  NumMatched = 0;
156  mutt_str_copy(UserTyped, pt, sizeof(UserTyped));
157  memset(Matches, 0, MatchesListsize);
158  memset(Completed, 0, sizeof(Completed));
159 
160  nm_db_longrun_init(m, false);
161 
162  /* Work out how many tags there are. */
163  if (nm_get_all_tags(m, NULL, &tag_count_1) || (tag_count_1 == 0))
164  goto done;
165 
166  /* Free the old list, if any. */
167  if (nm_tags)
168  {
169  for (int i = 0; nm_tags[i]; i++)
170  FREE(&nm_tags[i]);
171  FREE(&nm_tags);
172  }
173  /* Allocate a new list, with sentinel. */
174  nm_tags = mutt_mem_malloc((tag_count_1 + 1) * sizeof(char *));
175  nm_tags[tag_count_1] = NULL;
176 
177  /* Get all the tags. */
178  if (nm_get_all_tags(m, nm_tags, &tag_count_2) || (tag_count_1 != tag_count_2))
179  {
180  FREE(&nm_tags);
181  nm_tags = NULL;
183  return -1;
184  }
185 
186  /* Put them into the completion machinery. */
187  for (int num = 0; num < tag_count_1; num++)
188  {
189  candidate(UserTyped, nm_tags[num], Completed, sizeof(Completed));
190  }
191 
194 
195 done:
197  return 0;
198 }
199 #endif
200 
207 static int execute_commands(struct ListHead *p)
208 {
209  int rc = 0;
210  struct Buffer *err = mutt_buffer_pool_get();
211 
212  struct ListNode *np = NULL;
213  STAILQ_FOREACH(np, p, entries)
214  {
215  enum CommandResult rc2 = mutt_parse_rc_line(np->data, err);
216  if (rc2 == MUTT_CMD_ERROR)
217  mutt_error(_("Error in command line: %s"), mutt_buffer_string(err));
218  else if (rc2 == MUTT_CMD_WARNING)
219  mutt_warning(_("Warning in command line: %s"), mutt_buffer_string(err));
220 
221  if ((rc2 == MUTT_CMD_ERROR) || (rc2 == MUTT_CMD_WARNING))
222  {
224  return -1;
225  }
226  }
228 
229  return rc;
230 }
231 
239 static char *find_cfg(const char *home, const char *xdg_cfg_home)
240 {
241  const char *names[] = {
242  "neomuttrc",
243  "muttrc",
244  NULL,
245  };
246 
247  const char *locations[][2] = {
248  { xdg_cfg_home, "neomutt/" },
249  { xdg_cfg_home, "mutt/" },
250  { home, ".neomutt/" },
251  { home, ".mutt/" },
252  { home, "." },
253  { NULL, NULL },
254  };
255 
256  for (int i = 0; locations[i][0] || locations[i][1]; i++)
257  {
258  if (!locations[i][0])
259  continue;
260 
261  for (int j = 0; names[j]; j++)
262  {
263  char buf[256];
264 
265  snprintf(buf, sizeof(buf), "%s/%s%s", locations[i][0], locations[i][1], names[j]);
266  if (access(buf, F_OK) == 0)
267  return mutt_str_dup(buf);
268  }
269  }
270 
271  return NULL;
272 }
273 
274 #ifndef DOMAIN
280 static char *getmailname(void)
281 {
282  char *mailname = NULL;
283  static const char *mn_files[] = { "/etc/mailname", "/etc/mail/mailname" };
284 
285  for (size_t i = 0; i < mutt_array_size(mn_files); i++)
286  {
287  FILE *fp = mutt_file_fopen(mn_files[i], "r");
288  if (!fp)
289  continue;
290 
291  size_t len = 0;
292  mailname = mutt_file_read_line(NULL, &len, fp, NULL, MUTT_RL_NO_FLAGS);
293  mutt_file_fclose(&fp);
294  if (mailname && *mailname)
295  break;
296 
297  FREE(&mailname);
298  }
299 
300  return mailname;
301 }
302 #endif
303 
312 static bool get_hostname(struct ConfigSet *cs)
313 {
314  const char *short_host = NULL;
315  struct utsname utsname;
316 
317  const char *const c_hostname = cs_subset_string(NeoMutt->sub, "hostname");
318  if (c_hostname)
319  {
320  short_host = c_hostname;
321  }
322  else
323  {
324  /* The call to uname() shouldn't fail, but if it does, the system is horribly
325  * broken, and the system's networking configuration is in an unreliable
326  * state. We should bail. */
327  if ((uname(&utsname)) == -1)
328  {
329  mutt_perror(_("unable to determine nodename via uname()"));
330  return false; // TEST09: can't test
331  }
332 
333  short_host = utsname.nodename;
334  }
335 
336  /* some systems report the FQDN instead of just the hostname */
337  char *dot = strchr(short_host, '.');
338  if (dot)
339  ShortHostname = mutt_strn_dup(short_host, dot - short_host);
340  else
341  ShortHostname = mutt_str_dup(short_host);
342 
343  // All the code paths from here alloc memory for the fqdn
344  char *fqdn = mutt_str_dup(c_hostname);
345  if (!fqdn)
346  {
347  mutt_debug(LL_DEBUG1, "Setting $hostname\n");
348  /* now get FQDN. Use configured domain first, DNS next, then uname */
349 #ifdef DOMAIN
350  /* we have a compile-time domain name, use that for `$hostname` */
352  sprintf((char *) fqdn, "%s.%s", NONULL(ShortHostname), DOMAIN);
353 #else
354  fqdn = getmailname();
355  if (!fqdn)
356  {
357  struct Buffer *domain = mutt_buffer_pool_get();
358  if (getdnsdomainname(domain) == 0)
359  {
361  sprintf((char *) fqdn, "%s.%s", NONULL(ShortHostname), mutt_buffer_string(domain));
362  }
363  else
364  {
365  /* DNS failed, use the nodename. Whether or not the nodename had a '.'
366  * in it, we can use the nodename as the FQDN. On hosts where DNS is
367  * not being used, e.g. small network that relies on hosts files, a
368  * short host name is all that is required for SMTP to work correctly.
369  * It could be wrong, but we've done the best we can, at this point the
370  * onus is on the user to provide the correct hostname if the nodename
371  * won't work in their network. */
372  fqdn = mutt_str_dup(utsname.nodename);
373  }
374  mutt_buffer_pool_release(&domain);
375  mutt_debug(LL_DEBUG1, "Hostname: %s\n", NONULL(fqdn));
376  }
377 #endif
378  }
379 
380  if (fqdn)
381  {
382  cs_str_initial_set(cs, "hostname", fqdn, NULL);
383  cs_str_reset(cs, "hostname", NULL);
384  FREE(&fqdn);
385  }
386 
387  return true;
388 }
389 
398 int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
399 {
400  if (!dest || !tok)
401  return -1;
402 
403  char ch;
404  char qc = '\0'; /* quote char */
405  char *pc = NULL;
406 
407  /* Some callers used to rely on the (bad) assumption that dest->data would be
408  * non-NULL after calling this function. Perhaps I've missed a few cases, or
409  * a future caller might make the same mistake. */
410  if (!dest->data)
411  mutt_buffer_alloc(dest, 256);
412 
413  mutt_buffer_reset(dest);
414 
415  SKIPWS(tok->dptr);
416  while ((ch = *tok->dptr))
417  {
418  if (qc == '\0')
419  {
420  if ((IS_SPACE(ch) && !(flags & MUTT_TOKEN_SPACE)) ||
421  ((ch == '#') && !(flags & MUTT_TOKEN_COMMENT)) ||
422  ((ch == '+') && (flags & MUTT_TOKEN_PLUS)) ||
423  ((ch == '-') && (flags & MUTT_TOKEN_MINUS)) ||
424  ((ch == '=') && (flags & MUTT_TOKEN_EQUAL)) ||
425  ((ch == '?') && (flags & MUTT_TOKEN_QUESTION)) ||
426  ((ch == ';') && !(flags & MUTT_TOKEN_SEMICOLON)) ||
427  ((flags & MUTT_TOKEN_PATTERN) && strchr("~%=!|", ch)))
428  {
429  break;
430  }
431  }
432 
433  tok->dptr++;
434 
435  if (ch == qc)
436  qc = 0; /* end of quote */
437  else if (!qc && ((ch == '\'') || (ch == '"')) && !(flags & MUTT_TOKEN_QUOTE))
438  qc = ch;
439  else if ((ch == '\\') && (qc != '\''))
440  {
441  if (tok->dptr[0] == '\0')
442  return -1; /* premature end of token */
443  switch (ch = *tok->dptr++)
444  {
445  case 'c':
446  case 'C':
447  if (tok->dptr[0] == '\0')
448  return -1; /* premature end of token */
449  mutt_buffer_addch(dest, (toupper((unsigned char) tok->dptr[0]) - '@') & 0x7f);
450  tok->dptr++;
451  break;
452  case 'e':
453  mutt_buffer_addch(dest, '\033'); // Escape
454  break;
455  case 'f':
456  mutt_buffer_addch(dest, '\f');
457  break;
458  case 'n':
459  mutt_buffer_addch(dest, '\n');
460  break;
461  case 'r':
462  mutt_buffer_addch(dest, '\r');
463  break;
464  case 't':
465  mutt_buffer_addch(dest, '\t');
466  break;
467  default:
468  if (isdigit((unsigned char) ch) && isdigit((unsigned char) tok->dptr[0]) &&
469  isdigit((unsigned char) tok->dptr[1]))
470  {
471  mutt_buffer_addch(dest, (ch << 6) + (tok->dptr[0] << 3) + tok->dptr[1] - 3504);
472  tok->dptr += 2;
473  }
474  else
475  mutt_buffer_addch(dest, ch);
476  }
477  }
478  else if ((ch == '^') && (flags & MUTT_TOKEN_CONDENSE))
479  {
480  if (tok->dptr[0] == '\0')
481  return -1; /* premature end of token */
482  ch = *tok->dptr++;
483  if (ch == '^')
484  mutt_buffer_addch(dest, ch);
485  else if (ch == '[')
486  mutt_buffer_addch(dest, '\033'); // Escape
487  else if (isalpha((unsigned char) ch))
488  mutt_buffer_addch(dest, toupper((unsigned char) ch) - '@');
489  else
490  {
491  mutt_buffer_addch(dest, '^');
492  mutt_buffer_addch(dest, ch);
493  }
494  }
495  else if ((ch == '`') && (!qc || (qc == '"')))
496  {
497  FILE *fp = NULL;
498  pid_t pid;
499 
500  pc = tok->dptr;
501  do
502  {
503  pc = strpbrk(pc, "\\`");
504  if (pc)
505  {
506  /* skip any quoted chars */
507  if (*pc == '\\')
508  pc += 2;
509  }
510  } while (pc && (pc[0] != '`'));
511  if (!pc)
512  {
513  mutt_debug(LL_DEBUG1, "mismatched backticks\n");
514  return -1;
515  }
516  struct Buffer cmd;
517  mutt_buffer_init(&cmd);
518  *pc = '\0';
519  if (flags & MUTT_TOKEN_BACKTICK_VARS)
520  {
521  /* recursively extract tokens to interpolate variables */
522  mutt_extract_token(&cmd, tok,
525  }
526  else
527  {
528  cmd.data = mutt_str_dup(tok->dptr);
529  }
530  *pc = '`';
531  pid = filter_create(cmd.data, NULL, &fp, NULL);
532  if (pid < 0)
533  {
534  mutt_debug(LL_DEBUG1, "unable to fork command: %s\n", cmd.data);
535  FREE(&cmd.data);
536  return -1;
537  }
538 
539  tok->dptr = pc + 1;
540 
541  /* read line */
542  struct Buffer expn = mutt_buffer_make(0);
543  expn.data = mutt_file_read_line(NULL, &expn.dsize, fp, NULL, MUTT_RL_NO_FLAGS);
544  mutt_file_fclose(&fp);
545  int rc = filter_wait(pid);
546  if (rc != 0)
547  mutt_debug(LL_DEBUG1, "backticks exited code %d for command: %s\n", rc, cmd);
548  FREE(&cmd.data);
549 
550  /* if we got output, make a new string consisting of the shell output
551  * plus whatever else was left on the original line */
552  /* BUT: If this is inside a quoted string, directly add output to
553  * the token */
554  if (expn.data)
555  {
556  if (qc)
557  {
558  mutt_buffer_addstr(dest, expn.data);
559  }
560  else
561  {
562  struct Buffer *copy = mutt_buffer_pool_get();
563  mutt_buffer_fix_dptr(&expn);
564  mutt_buffer_copy(copy, &expn);
565  mutt_buffer_addstr(copy, tok->dptr);
566  mutt_buffer_copy(tok, copy);
567  mutt_buffer_seek(tok, 0);
569  }
570  FREE(&expn.data);
571  }
572  }
573  else if ((ch == '$') && (!qc || (qc == '"')) &&
574  ((tok->dptr[0] == '{') || isalpha((unsigned char) tok->dptr[0])))
575  {
576  const char *env = NULL;
577  char *var = NULL;
578 
579  if (tok->dptr[0] == '{')
580  {
581  pc = strchr(tok->dptr, '}');
582  if (pc)
583  {
584  var = mutt_strn_dup(tok->dptr + 1, pc - (tok->dptr + 1));
585  tok->dptr = pc + 1;
586 
587  if ((flags & MUTT_TOKEN_NOSHELL))
588  {
589  mutt_buffer_addch(dest, ch);
590  mutt_buffer_addch(dest, '{');
591  mutt_buffer_addstr(dest, var);
592  mutt_buffer_addch(dest, '}');
593  FREE(&var);
594  }
595  }
596  }
597  else
598  {
599  for (pc = tok->dptr; isalnum((unsigned char) *pc) || (pc[0] == '_'); pc++)
600  ; // do nothing
601 
602  var = mutt_strn_dup(tok->dptr, pc - tok->dptr);
603  tok->dptr = pc;
604  }
605  if (var)
606  {
607  struct Buffer result;
608  mutt_buffer_init(&result);
609  int rc = cs_subset_str_string_get(NeoMutt->sub, var, &result);
610 
611  if (CSR_RESULT(rc) == CSR_SUCCESS)
612  {
613  mutt_buffer_addstr(dest, result.data);
614  FREE(&result.data);
615  }
616  else if ((env = myvar_get(var)))
617  {
618  mutt_buffer_addstr(dest, env);
619  }
620  else if (!(flags & MUTT_TOKEN_NOSHELL) && (env = mutt_str_getenv(var)))
621  {
622  mutt_buffer_addstr(dest, env);
623  }
624  else
625  {
626  mutt_buffer_addch(dest, ch);
627  mutt_buffer_addstr(dest, var);
628  }
629  FREE(&var);
630  }
631  }
632  else
633  mutt_buffer_addch(dest, ch);
634  }
635  mutt_buffer_addch(dest, 0); /* terminate the string */
636  SKIPWS(tok->dptr);
637  return 0;
638 }
639 
643 void mutt_opts_free(void)
644 {
646 
647  alias_shutdown();
648 #ifdef USE_SIDEBAR
649  sb_shutdown();
650 #endif
651 
657 
661 
662  /* Lists of strings */
672 
674 
676  FREE(&HomeDir);
677  FREE(&LastFolder);
679  FREE(&Username);
680 
682 
684 
685  mutt_hist_free();
686  mutt_keys_free();
687 
690 }
691 
698 HookFlags mutt_get_hook_type(const char *name)
699 {
700  struct Command *c = NULL;
701  for (size_t i = 0, size = mutt_commands_array(&c); i < size; i++)
702  {
703  if (((c[i].parse == mutt_parse_hook) || (c[i].parse == mutt_parse_idxfmt_hook)) &&
704  mutt_istr_equal(c[i].name, name))
705  {
706  return c[i].data;
707  }
708  }
709  return MUTT_HOOK_NO_FLAGS;
710 }
711 
720 int mutt_init(struct ConfigSet *cs, bool skip_sys_rc, struct ListHead *commands)
721 {
722  int need_pause = 0;
723  int rc = 1;
724  struct Buffer err = mutt_buffer_make(256);
725  struct Buffer buf = mutt_buffer_make(256);
726 
728  alias_init();
730 #ifdef USE_COMP_MBOX
731  mutt_comp_init();
732 #endif
733 #ifdef USE_IMAP
734  imap_init();
735 #endif
736 #ifdef USE_LUA
737  mutt_lua_init();
738 #endif
741 
742  menu_init();
743 #ifdef USE_SIDEBAR
744  sb_init();
745 #endif
746 #ifdef USE_NOTMUCH
747  nm_init();
748 #endif
749 
750  /* "$spool_file" precedence: config file, environment */
751  const char *p = mutt_str_getenv("MAIL");
752  if (!p)
753  p = mutt_str_getenv("MAILDIR");
754  if (!p)
755  {
756 #ifdef HOMESPOOL
757  mutt_buffer_concat_path(&buf, NONULL(HomeDir), MAILPATH);
758 #else
759  mutt_buffer_concat_path(&buf, MAILPATH, NONULL(Username));
760 #endif
761  p = mutt_buffer_string(&buf);
762  }
763  cs_str_initial_set(cs, "spool_file", p, NULL);
764  cs_str_reset(cs, "spool_file", NULL);
765 
766  p = mutt_str_getenv("REPLYTO");
767  if (p)
768  {
769  struct Buffer token;
770 
771  mutt_buffer_printf(&buf, "Reply-To: %s", p);
772  mutt_buffer_init(&token);
773  parse_my_hdr(&token, &buf, 0, &err); /* adds to UserHeader */
774  FREE(&token.data);
775  }
776 
777  p = mutt_str_getenv("EMAIL");
778  if (p)
779  {
780  cs_str_initial_set(cs, "from", p, NULL);
781  cs_str_reset(cs, "from", NULL);
782  }
783 
784  /* "$mailcap_path" precedence: config file, environment, code */
785  const char *env_mc = mutt_str_getenv("MAILCAPS");
786  if (env_mc)
787  cs_str_string_set(cs, "mailcap_path", env_mc, NULL);
788 
789  /* "$tmpdir" precedence: config file, environment, code */
790  const char *env_tmp = mutt_str_getenv("TMPDIR");
791  if (env_tmp)
792  cs_str_string_set(cs, "tmpdir", env_tmp, NULL);
793 
794  /* "$visual", "$editor" precedence: config file, environment, code */
795  const char *env_ed = mutt_str_getenv("VISUAL");
796  if (!env_ed)
797  env_ed = mutt_str_getenv("EDITOR");
798  if (!env_ed)
799  env_ed = "vi";
800  cs_str_initial_set(cs, "editor", env_ed, NULL);
801 
802  const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
803  if (!c_editor)
804  cs_str_reset(cs, "editor", NULL);
805 
806  const char *charset = mutt_ch_get_langinfo_charset();
807  cs_str_initial_set(cs, "charset", charset, NULL);
808  cs_str_reset(cs, "charset", NULL);
809  mutt_ch_set_charset(charset);
810  FREE(&charset);
811 
812  Matches = mutt_mem_calloc(MatchesListsize, sizeof(char *));
813 
814 #ifdef HAVE_GETSID
815  /* Unset suspend by default if we're the session leader */
816  if (getsid(0) == getpid())
817  cs_subset_str_native_set(NeoMutt->sub, "suspend", false, NULL);
818 #endif
819 
820  /* RFC2368, "4. Unsafe headers"
821  * The creator of a mailto URL can't expect the resolver of a URL to
822  * understand more than the "subject" and "body" headers. Clients that
823  * resolve mailto URLs into mail messages should be able to correctly
824  * create RFC822-compliant mail messages using the "subject" and "body"
825  * headers. */
826  add_to_stailq(&MailToAllow, "body");
827  add_to_stailq(&MailToAllow, "subject");
828  /* Cc, In-Reply-To, and References help with not breaking threading on
829  * mailing lists, see https://github.com/neomutt/neomutt/issues/115 */
830  add_to_stailq(&MailToAllow, "cc");
831  add_to_stailq(&MailToAllow, "in-reply-to");
832  add_to_stailq(&MailToAllow, "references");
833 
834  if (STAILQ_EMPTY(&Muttrc))
835  {
836  const char *xdg_cfg_home = mutt_str_getenv("XDG_CONFIG_HOME");
837 
838  if (!xdg_cfg_home && HomeDir)
839  {
840  mutt_buffer_printf(&buf, "%s/.config", HomeDir);
841  xdg_cfg_home = mutt_buffer_string(&buf);
842  }
843 
844  char *config = find_cfg(HomeDir, xdg_cfg_home);
845  if (config)
846  {
847  mutt_list_insert_tail(&Muttrc, config);
848  }
849  }
850  else
851  {
852  struct ListNode *np = NULL;
853  STAILQ_FOREACH(np, &Muttrc, entries)
854  {
855  mutt_buffer_strcpy(&buf, np->data);
856  FREE(&np->data);
858  np->data = mutt_buffer_strdup(&buf);
859  if (access(np->data, F_OK))
860  {
861  mutt_perror(np->data);
862  goto done; // TEST10: neomutt -F missing
863  }
864  }
865  }
866 
867  if (!STAILQ_EMPTY(&Muttrc))
868  {
869  cs_str_string_set(cs, "alias_file", STAILQ_FIRST(&Muttrc)->data, NULL);
870  }
871 
872  /* Process the global rc file if it exists and the user hasn't explicitly
873  * requested not to via "-n". */
874  if (!skip_sys_rc)
875  {
876  do
877  {
879  break;
880 
881  mutt_buffer_printf(&buf, "%s/neomuttrc", SYSCONFDIR);
882  if (access(mutt_buffer_string(&buf), F_OK) == 0)
883  break;
884 
885  mutt_buffer_printf(&buf, "%s/Muttrc", SYSCONFDIR);
886  if (access(mutt_buffer_string(&buf), F_OK) == 0)
887  break;
888 
889  mutt_buffer_printf(&buf, "%s/neomuttrc", PKGDATADIR);
890  if (access(mutt_buffer_string(&buf), F_OK) == 0)
891  break;
892 
893  mutt_buffer_printf(&buf, "%s/Muttrc", PKGDATADIR);
894  } while (false);
895 
896  if (access(mutt_buffer_string(&buf), F_OK) == 0)
897  {
898  if (source_rc(mutt_buffer_string(&buf), &err) != 0)
899  {
900  mutt_error("%s", err.data);
901  need_pause = 1; // TEST11: neomutt (error in /etc/neomuttrc)
902  }
903  }
904  }
905 
906  /* Read the user's initialization file. */
907  struct ListNode *np = NULL;
908  STAILQ_FOREACH(np, &Muttrc, entries)
909  {
910  if (np->data)
911  {
912  if (source_rc(np->data, &err) != 0)
913  {
914  mutt_error("%s", err.data);
915  need_pause = 1; // TEST12: neomutt (error in ~/.neomuttrc)
916  }
917  }
918  }
919 
920  if (execute_commands(commands) != 0)
921  need_pause = 1; // TEST13: neomutt -e broken
922 
923  if (!get_hostname(cs))
924  goto done;
925 
926  {
927  char name[256] = { 0 };
928  const char *c_real_name = cs_subset_string(NeoMutt->sub, "real_name");
929  if (!c_real_name)
930  {
931  struct passwd *pw = getpwuid(getuid());
932  if (pw)
933  c_real_name = mutt_gecos_name(name, sizeof(name), pw);
934  }
935  cs_str_initial_set(cs, "real_name", c_real_name, NULL);
936  cs_str_reset(cs, "real_name", NULL);
937  }
938 
939  if (need_pause && !OptNoCurses)
940  {
942  if (mutt_any_key_to_continue(NULL) == 'q')
943  goto done; // TEST14: neomutt -e broken (press 'q')
944  }
945 
946  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
947  mutt_file_mkdir(c_tmpdir, S_IRWXU);
948 
949  mutt_hist_init();
951 
952 #ifdef USE_NOTMUCH
953  const bool c_virtual_spool_file =
954  cs_subset_bool(NeoMutt->sub, "virtual_spool_file");
955  if (c_virtual_spool_file)
956  {
957  /* Find the first virtual folder and open it */
958  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
960  struct MailboxNode *mp = STAILQ_FIRST(&ml);
961  if (mp)
962  cs_str_string_set(cs, "spool_file", mailbox_path(mp->mailbox), NULL);
964  }
965 #endif
966  rc = 0;
967 
968 done:
969  mutt_buffer_dealloc(&err);
970  mutt_buffer_dealloc(&buf);
971  return rc;
972 }
973 
985 enum CommandResult mutt_parse_rc_buffer(struct Buffer *line,
986  struct Buffer *token, struct Buffer *err)
987 {
988  if (mutt_buffer_len(line) == 0)
989  return 0;
990 
992 
993  mutt_buffer_reset(err);
994 
995  /* Read from the beginning of line->data */
996  mutt_buffer_seek(line, 0);
997 
998  SKIPWS(line->dptr);
999  while (*line->dptr)
1000  {
1001  if (*line->dptr == '#')
1002  break; /* rest of line is a comment */
1003  if (*line->dptr == ';')
1004  {
1005  line->dptr++;
1006  continue;
1007  }
1009 
1010  struct Command *cmd = NULL;
1011  size_t size = mutt_commands_array(&cmd);
1012  size_t i;
1013  for (i = 0; i < size; i++)
1014  {
1015  if (mutt_str_equal(token->data, cmd[i].name))
1016  {
1017  mutt_debug(LL_NOTIFY, "NT_COMMAND: %s\n", cmd[i].name);
1018  rc = cmd[i].parse(token, line, cmd[i].data, err);
1019  if ((rc == MUTT_CMD_ERROR) || (rc == MUTT_CMD_FINISH))
1020  goto finish; /* Propagate return code */
1021 
1022  notify_send(NeoMutt->notify, NT_COMMAND, i, (void *) cmd);
1023  break; /* Continue with next command */
1024  }
1025  }
1026  if (i == size)
1027  {
1028  mutt_buffer_printf(err, _("%s: unknown command"), NONULL(token->data));
1029  rc = MUTT_CMD_ERROR;
1030  break; /* Ignore the rest of the line */
1031  }
1032  }
1033 finish:
1034  return rc;
1035 }
1036 
1043 enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
1044 {
1045  if (!line || (*line == '\0'))
1046  return MUTT_CMD_ERROR;
1047 
1048  struct Buffer *line_buffer = mutt_buffer_pool_get();
1049  struct Buffer *token = mutt_buffer_pool_get();
1050 
1051  mutt_buffer_strcpy(line_buffer, line);
1052 
1053  enum CommandResult rc = mutt_parse_rc_buffer(line_buffer, token, err);
1054 
1055  mutt_buffer_pool_release(&line_buffer);
1056  mutt_buffer_pool_release(&token);
1057  return rc;
1058 }
1059 
1067 int mutt_query_variables(struct ListHead *queries, bool show_docs)
1068 {
1069  struct Buffer value = mutt_buffer_make(256);
1070  struct Buffer tmp = mutt_buffer_make(256);
1071  int rc = 0;
1072 
1073  struct ListNode *np = NULL;
1074  STAILQ_FOREACH(np, queries, entries)
1075  {
1076  mutt_buffer_reset(&value);
1077 
1078  struct HashElem *he = cs_subset_lookup(NeoMutt->sub, np->data);
1079  if (!he)
1080  {
1081  mutt_warning(_("No such variable: %s"), np->data);
1082  rc = 1;
1083  continue;
1084  }
1085 
1086  if (he->type & DT_DEPRECATED)
1087  {
1088  mutt_warning(_("Config variable '%s' is deprecated"), np->data);
1089  rc = 1;
1090  continue;
1091  }
1092 
1093  int rv = cs_subset_he_string_get(NeoMutt->sub, he, &value);
1094  if (CSR_RESULT(rv) != CSR_SUCCESS)
1095  {
1096  rc = 1;
1097  continue;
1098  }
1099 
1100  int type = DTYPE(he->type);
1101  if (type == DT_PATH)
1102  mutt_pretty_mailbox(value.data, value.dsize);
1103 
1104  if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_LONG) && (type != DT_QUAD))
1105  {
1106  mutt_buffer_reset(&tmp);
1107  pretty_var(value.data, &tmp);
1108  mutt_buffer_strcpy(&value, tmp.data);
1109  }
1110 
1111  dump_config_neo(NeoMutt->sub->cs, he, &value, NULL,
1112  show_docs ? CS_DUMP_SHOW_DOCS : CS_DUMP_NO_FLAGS, stdout);
1113  }
1114 
1115  mutt_buffer_dealloc(&value);
1116  mutt_buffer_dealloc(&tmp);
1117 
1118  return rc; // TEST16: neomutt -Q charset
1119 }
1120 
1130 int mutt_command_complete(char *buf, size_t buflen, int pos, int numtabs)
1131 {
1132  char *pt = buf;
1133  int spaces; /* keep track of the number of leading spaces on the line */
1134  struct MyVar *myv = NULL;
1135 
1136  SKIPWS(buf);
1137  spaces = buf - pt;
1138 
1139  pt = buf + pos - spaces;
1140  while ((pt > buf) && !isspace((unsigned char) *pt))
1141  pt--;
1142 
1143  if (pt == buf) /* complete cmd */
1144  {
1145  /* first TAB. Collect all the matches */
1146  if (numtabs == 1)
1147  {
1148  NumMatched = 0;
1149  mutt_str_copy(UserTyped, pt, sizeof(UserTyped));
1150  memset(Matches, 0, MatchesListsize);
1151  memset(Completed, 0, sizeof(Completed));
1152 
1153  struct Command *c = NULL;
1154  for (size_t num = 0, size = mutt_commands_array(&c); num < size; num++)
1155  candidate(UserTyped, c[num].name, Completed, sizeof(Completed));
1158 
1159  /* All matches are stored. Longest non-ambiguous string is ""
1160  * i.e. don't change 'buf'. Fake successful return this time */
1161  if (UserTyped[0] == '\0')
1162  return 1;
1163  }
1164 
1165  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
1166  return 0;
1167 
1168  /* NumMatched will _always_ be at least 1 since the initial
1169  * user-typed string is always stored */
1170  if ((numtabs == 1) && (NumMatched == 2))
1171  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
1172  else if ((numtabs > 1) && (NumMatched > 2))
1173  {
1174  /* cycle through all the matches */
1175  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
1176  }
1177 
1178  /* return the completed command */
1179  strncpy(buf, Completed, buflen - spaces);
1180  }
1181  else if (mutt_str_startswith(buf, "set") || mutt_str_startswith(buf, "unset") ||
1182  mutt_str_startswith(buf, "reset") || mutt_str_startswith(buf, "toggle"))
1183  { /* complete variables */
1184  static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
1185 
1186  pt++;
1187  /* loop through all the possible prefixes (no, inv, ...) */
1188  if (mutt_str_startswith(buf, "set"))
1189  {
1190  for (int num = 0; prefixes[num]; num++)
1191  {
1192  if (mutt_str_startswith(pt, prefixes[num]))
1193  {
1194  pt += mutt_str_len(prefixes[num]);
1195  break;
1196  }
1197  }
1198  }
1199 
1200  /* first TAB. Collect all the matches */
1201  if (numtabs == 1)
1202  {
1203  NumMatched = 0;
1204  mutt_str_copy(UserTyped, pt, sizeof(UserTyped));
1205  memset(Matches, 0, MatchesListsize);
1206  memset(Completed, 0, sizeof(Completed));
1207 
1208  struct HashElem *he = NULL;
1209  struct HashElem **list = get_elem_list(NeoMutt->sub->cs);
1210  for (size_t i = 0; list[i]; i++)
1211  {
1212  he = list[i];
1213  const int type = DTYPE(he->type);
1214 
1215  if ((type == DT_SYNONYM) || (type & DT_DEPRECATED))
1216  continue;
1217 
1218  candidate(UserTyped, he->key.strkey, Completed, sizeof(Completed));
1219  }
1220  FREE(&list);
1221 
1222  TAILQ_FOREACH(myv, &MyVars, entries)
1223  {
1224  candidate(UserTyped, myv->name, Completed, sizeof(Completed));
1225  }
1228 
1229  /* All matches are stored. Longest non-ambiguous string is ""
1230  * i.e. don't change 'buf'. Fake successful return this time */
1231  if (UserTyped[0] == '\0')
1232  return 1;
1233  }
1234 
1235  if ((Completed[0] == 0) && UserTyped[0])
1236  return 0;
1237 
1238  /* NumMatched will _always_ be at least 1 since the initial
1239  * user-typed string is always stored */
1240  if ((numtabs == 1) && (NumMatched == 2))
1241  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
1242  else if ((numtabs > 1) && (NumMatched > 2))
1243  {
1244  /* cycle through all the matches */
1245  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
1246  }
1247 
1248  strncpy(pt, Completed, buf + buflen - pt - spaces);
1249  }
1250  else if (mutt_str_startswith(buf, "exec"))
1251  {
1252  const enum MenuType mtype = menu_get_current_type();
1253  const struct Binding *menu = km_get_table(mtype);
1254  if (!menu && (mtype != MENU_PAGER))
1255  menu = OpGeneric;
1256 
1257  pt++;
1258  /* first TAB. Collect all the matches */
1259  if (numtabs == 1)
1260  {
1261  NumMatched = 0;
1262  mutt_str_copy(UserTyped, pt, sizeof(UserTyped));
1263  memset(Matches, 0, MatchesListsize);
1264  memset(Completed, 0, sizeof(Completed));
1265  for (int num = 0; menu[num].name; num++)
1266  candidate(UserTyped, menu[num].name, Completed, sizeof(Completed));
1267  /* try the generic menu */
1268  if ((Completed[0] == '\0') && (mtype != MENU_PAGER))
1269  {
1270  menu = OpGeneric;
1271  for (int num = 0; menu[num].name; num++)
1272  candidate(UserTyped, menu[num].name, Completed, sizeof(Completed));
1273  }
1276 
1277  /* All matches are stored. Longest non-ambiguous string is ""
1278  * i.e. don't change 'buf'. Fake successful return this time */
1279  if (UserTyped[0] == '\0')
1280  return 1;
1281  }
1282 
1283  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
1284  return 0;
1285 
1286  /* NumMatched will _always_ be at least 1 since the initial
1287  * user-typed string is always stored */
1288  if ((numtabs == 1) && (NumMatched == 2))
1289  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
1290  else if ((numtabs > 1) && (NumMatched > 2))
1291  {
1292  /* cycle through all the matches */
1293  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
1294  }
1295 
1296  strncpy(pt, Completed, buf + buflen - pt - spaces);
1297  }
1298  else
1299  return 0;
1300 
1301  return 1;
1302 }
1303 
1312 int mutt_label_complete(char *buf, size_t buflen, int numtabs)
1313 {
1314  char *pt = buf;
1315  int spaces; /* keep track of the number of leading spaces on the line */
1316 
1317  if (!Context || !Context->mailbox->label_hash)
1318  return 0;
1319 
1320  SKIPWS(buf);
1321  spaces = buf - pt;
1322 
1323  /* first TAB. Collect all the matches */
1324  if (numtabs == 1)
1325  {
1326  struct HashElem *entry = NULL;
1327  struct HashWalkState state = { 0 };
1328 
1329  NumMatched = 0;
1330  mutt_str_copy(UserTyped, buf, sizeof(UserTyped));
1331  memset(Matches, 0, MatchesListsize);
1332  memset(Completed, 0, sizeof(Completed));
1333  while ((entry = mutt_hash_walk(Context->mailbox->label_hash, &state)))
1334  candidate(UserTyped, entry->key.strkey, Completed, sizeof(Completed));
1336  qsort(Matches, NumMatched, sizeof(char *), (sort_t) mutt_istr_cmp);
1338 
1339  /* All matches are stored. Longest non-ambiguous string is ""
1340  * i.e. don't change 'buf'. Fake successful return this time */
1341  if (UserTyped[0] == '\0')
1342  return 1;
1343  }
1344 
1345  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
1346  return 0;
1347 
1348  /* NumMatched will _always_ be at least 1 since the initial
1349  * user-typed string is always stored */
1350  if ((numtabs == 1) && (NumMatched == 2))
1351  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
1352  else if ((numtabs > 1) && (NumMatched > 2))
1353  {
1354  /* cycle through all the matches */
1355  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
1356  }
1357 
1358  /* return the completed label */
1359  strncpy(buf, Completed, buflen - spaces);
1360 
1361  return 1;
1362 }
1363 
1364 #ifdef USE_NOTMUCH
1376 bool mutt_nm_query_complete(char *buf, size_t buflen, int pos, int numtabs)
1377 {
1378  char *pt = buf;
1379  int spaces;
1380 
1381  SKIPWS(buf);
1382  spaces = buf - pt;
1383 
1384  pt = (char *) mutt_strn_rfind((char *) buf, pos, "tag:");
1385  if (pt)
1386  {
1387  pt += 4;
1388  if (numtabs == 1)
1389  {
1390  /* First TAB. Collect all the matches */
1392 
1393  /* All matches are stored. Longest non-ambiguous string is ""
1394  * i.e. don't change 'buf'. Fake successful return this time. */
1395  if (UserTyped[0] == '\0')
1396  return true;
1397  }
1398 
1399  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
1400  return false;
1401 
1402  /* NumMatched will _always_ be at least 1 since the initial
1403  * user-typed string is always stored */
1404  if ((numtabs == 1) && (NumMatched == 2))
1405  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
1406  else if ((numtabs > 1) && (NumMatched > 2))
1407  {
1408  /* cycle through all the matches */
1409  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
1410  }
1411 
1412  /* return the completed query */
1413  strncpy(pt, Completed, buf + buflen - pt - spaces);
1414  }
1415  else
1416  return false;
1417 
1418  return true;
1419 }
1420 #endif
1421 
1422 #ifdef USE_NOTMUCH
1433 bool mutt_nm_tag_complete(char *buf, size_t buflen, int numtabs)
1434 {
1435  if (!buf)
1436  return false;
1437 
1438  char *pt = buf;
1439 
1440  /* Only examine the last token */
1441  char *last_space = strrchr(buf, ' ');
1442  if (last_space)
1443  pt = (last_space + 1);
1444 
1445  /* Skip the +/- */
1446  if ((pt[0] == '+') || (pt[0] == '-'))
1447  pt++;
1448 
1449  if (numtabs == 1)
1450  {
1451  /* First TAB. Collect all the matches */
1453 
1454  /* All matches are stored. Longest non-ambiguous string is ""
1455  * i.e. don't change 'buf'. Fake successful return this time. */
1456  if (UserTyped[0] == '\0')
1457  return true;
1458  }
1459 
1460  if ((Completed[0] == '\0') && (UserTyped[0] != '\0'))
1461  return false;
1462 
1463  /* NumMatched will _always_ be at least 1 since the initial
1464  * user-typed string is always stored */
1465  if ((numtabs == 1) && (NumMatched == 2))
1466  snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
1467  else if ((numtabs > 1) && (NumMatched > 2))
1468  {
1469  /* cycle through all the matches */
1470  snprintf(Completed, sizeof(Completed), "%s", Matches[(numtabs - 2) % NumMatched]);
1471  }
1472 
1473  /* return the completed query */
1474  strncpy(pt, Completed, buf + buflen - pt);
1475 
1476  return true;
1477 }
1478 #endif
1479 
1486 int mutt_var_value_complete(char *buf, size_t buflen, int pos)
1487 {
1488  char *pt = buf;
1489 
1490  if (buf[0] == '\0')
1491  return 0;
1492 
1493  SKIPWS(buf);
1494  const int spaces = buf - pt;
1495 
1496  pt = buf + pos - spaces;
1497  while ((pt > buf) && !isspace((unsigned char) *pt))
1498  pt--;
1499  pt++; /* move past the space */
1500  if (*pt == '=') /* abort if no var before the '=' */
1501  return 0;
1502 
1503  if (mutt_str_startswith(buf, "set"))
1504  {
1505  const char *myvarval = NULL;
1506  char var[256];
1507  mutt_str_copy(var, pt, sizeof(var));
1508  /* ignore the trailing '=' when comparing */
1509  int vlen = mutt_str_len(var);
1510  if (vlen == 0)
1511  return 0;
1512 
1513  var[vlen - 1] = '\0';
1514 
1515  struct HashElem *he = cs_subset_lookup(NeoMutt->sub, var);
1516  if (!he)
1517  {
1518  myvarval = myvar_get(var);
1519  if (myvarval)
1520  {
1521  struct Buffer pretty = mutt_buffer_make(256);
1522  pretty_var(myvarval, &pretty);
1523  snprintf(pt, buflen - (pt - buf), "%s=%s", var, pretty.data);
1524  mutt_buffer_dealloc(&pretty);
1525  return 1;
1526  }
1527  return 0; /* no such variable. */
1528  }
1529  else
1530  {
1531  struct Buffer value = mutt_buffer_make(256);
1532  struct Buffer pretty = mutt_buffer_make(256);
1533  int rc = cs_subset_he_string_get(NeoMutt->sub, he, &value);
1534  if (CSR_RESULT(rc) == CSR_SUCCESS)
1535  {
1536  pretty_var(value.data, &pretty);
1537  snprintf(pt, buflen - (pt - buf), "%s=%s", var, pretty.data);
1538  mutt_buffer_dealloc(&value);
1539  mutt_buffer_dealloc(&pretty);
1540  return 0;
1541  }
1542  mutt_buffer_dealloc(&value);
1543  mutt_buffer_dealloc(&pretty);
1544  return 1;
1545  }
1546  }
1547  return 0;
1548 }
Email Address Handling.
Email Aliases.
void alias_init(void)
Set up the Alias globals.
Definition: alias.c:664
void alias_shutdown(void)
Clean up the Alias globals.
Definition: alias.c:672
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:432
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
void mutt_buffer_seek(struct Buffer *buf, size_t offset)
Set current read/write position to offset from beginning.
Definition: buffer.c:466
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:445
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
size_t mutt_buffer_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:374
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Color and attribute parsing.
void mutt_colors_cleanup(void)
Cleanup all the colours.
Definition: color.c:163
CommandResult
Error codes for command_t parse functions.
Definition: command.h:34
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:37
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition: command.h:35
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition: command.h:36
@ MUTT_CMD_FINISH
Finish: Stop processing this file.
Definition: command.h:38
void clear_source_stack(void)
Free memory from the stack used for the source command.
int source_rc(const char *rcfile_path, struct Buffer *err)
Read an initialization file.
Functions to parse commands in a config file.
void mutt_comp_init(void)
Setup feature commands.
Definition: compress.c:69
Compressed mbox local mailbox type.
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: mutt_globals.h:51
Connection Library.
struct Mailbox * ctx_mailbox(struct Context *ctx)
Wrapper to get the mailbox in a Context, or NULL.
Definition: context.c:444
The "currently-open" mailbox.
Convenience wrapper for the core headers.
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:455
size_t pretty_var(const char *str, struct Buffer *buf)
Escape and stringify a config item value.
Definition: dump.c:83
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:106
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:526
#define CS_DUMP_NO_FLAGS
No flags are set.
Definition: dump.h:35
#define CS_DUMP_SHOW_DOCS
Show one-liner documentation for the config item.
Definition: dump.h:45
Structs that make up an email.
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:671
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:881
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:593
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
const struct Binding OpGeneric[]
Key bindings for the generic menu.
Definition: functions.c:53
int getdnsdomainname(struct Buffer *result)
Lookup the host's name using DNS.
Definition: getdomain.c:118
struct ReplaceList SpamList
List of regexes and patterns to match spam emails.
Definition: globals.c:34
struct RegexList SubscribedLists
List of regexes to match subscribed mailing lists.
Definition: globals.c:43
struct RegexList UnSubscribedLists
List of regexes to blacklist false matches in SubscribedLists.
Definition: globals.c:40
struct RegexList UnMailLists
List of regexes to blacklist false matches in MailLists.
Definition: globals.c:42
struct RegexList MailLists
List of regexes to match mailing lists.
Definition: globals.c:41
struct ListHead MailToAllow
List of permitted fields in a mailto: url.
Definition: globals.c:37
struct ListHead Ignore
List of header patterns to ignore.
Definition: globals.c:35
struct RegexList NoSpamList
List of regexes to whitelist non-spam emails.
Definition: globals.c:33
struct ListHead UnIgnore
List of header patterns to unignore (see)
Definition: globals.c:36
void mutt_grouplist_free(void)
Free GroupList singleton resource.
Definition: group.c:102
void mutt_grouplist_init(void)
Initialize the GroupList singleton.
Definition: group.c:90
enum CommandResult mutt_parse_idxfmt_hook(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'index-format-hook' command - Implements Command::parse() -.
Definition: hook.c:371
enum CommandResult mutt_parse_hook(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'hook' family of commands - Implements Command::parse() -.
Definition: hook.c:81
enum CommandResult parse_my_hdr(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'my_hdr' command - Implements Command::parse() -.
#define mutt_warning(...)
Definition: logging.h:85
int log_disp_terminal(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Save a log line to the terminal - Implements log_dispatcher_t -.
Definition: logging.c:441
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
Convenience wrapper for the gui headers.
struct HashElem * mutt_hash_walk(const struct HashTable *table, struct HashWalkState *state)
Iterate through all the HashElem's in a Hash Table.
Definition: hash.c:481
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:251
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:449
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:109
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition: hash.h:110
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Read/write command history from/to a file.
void mutt_hist_read_file(void)
Read the History from a file.
Definition: history.c:599
void mutt_hist_init(void)
Create a set of empty History ring buffers.
Definition: history.c:468
void mutt_hist_free(void)
Free all the history lists.
Definition: history.c:441
void mutt_delete_hooks(HookFlags type)
Delete matching hooks.
Definition: hook.c:327
uint32_t HookFlags
Flags for mutt_parse_hook(), e.g. MUTT_FOLDER_HOOK.
Definition: hook.h:34
#define MUTT_HOOK_NO_FLAGS
No flags are set.
Definition: hook.h:37
IMAP network mailbox.
void imap_init(void)
Setup feature commands.
Definition: imap.c:83
static void candidate(char *user, const char *src, char *dest, size_t dlen)
Helper function for completion.
Definition: init.c:120
static int MatchesListsize
Definition: init.c:86
static char Completed[256]
Definition: init.c:83
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:398
static char * getmailname(void)
Try to retrieve the FQDN from mailname files.
Definition: init.c:280
enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:1043
int mutt_var_value_complete(char *buf, size_t buflen, int pos)
Complete a variable/value.
Definition: init.c:1486
bool mutt_nm_query_complete(char *buf, size_t buflen, int pos, int numtabs)
Complete to the nearest notmuch tag.
Definition: init.c:1376
static char ** nm_tags
Definition: init.c:90
enum CommandResult mutt_parse_rc_buffer(struct Buffer *line, struct Buffer *token, struct Buffer *err)
Parse a line of user config.
Definition: init.c:985
HookFlags mutt_get_hook_type(const char *name)
Find a hook by name.
Definition: init.c:698
static void matches_ensure_morespace(int current)
Allocate more space for auto-completion.
Definition: init.c:97
int mutt_label_complete(char *buf, size_t buflen, int numtabs)
Complete a label name.
Definition: init.c:1312
static int execute_commands(struct ListHead *p)
Execute a set of NeoMutt commands.
Definition: init.c:207
static int complete_all_nm_tags(const char *pt)
Pass a list of Notmuch tags to the completion code.
Definition: init.c:149
int mutt_query_variables(struct ListHead *queries, bool show_docs)
Implement the -Q command line flag.
Definition: init.c:1067
bool mutt_nm_tag_complete(char *buf, size_t buflen, int numtabs)
Complete to the nearest notmuch tag.
Definition: init.c:1433
void mutt_opts_free(void)
Clean up before quitting.
Definition: init.c:643
int mutt_init(struct ConfigSet *cs, bool skip_sys_rc, struct ListHead *commands)
Initialise NeoMutt.
Definition: init.c:720
static char UserTyped[1024]
Definition: init.c:80
static bool get_hostname(struct ConfigSet *cs)
Find the Fully-Qualified Domain Name.
Definition: init.c:312
int mutt_command_complete(char *buf, size_t buflen, int pos, int numtabs)
Complete a command name.
Definition: init.c:1130
static const char ** Matches
Definition: init.c:84
static int NumMatched
Definition: init.c:82
static char * find_cfg(const char *home, const char *xdg_cfg_home)
Find a config file.
Definition: init.c:239
Config/command parsing.
const struct Binding * km_get_table(enum MenuType mtype)
Lookup a menu's keybindings.
Definition: keymap.c:1301
void mutt_keys_free(void)
Free the key maps.
Definition: keymap.c:1731
Manage keymappings.
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void log_queue_flush(log_dispatcher_t disp)
Replay the log queue.
Definition: logging.c:348
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
@ LL_NOTIFY
Log of notifications.
Definition: logging.h:45
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:215
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:54
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:40
#define mutt_array_size(x)
Definition: memory.h:33
GUI present the user with a selectable list.
enum MenuType menu_get_current_type(void)
Get the type of the current Window.
Definition: menu.c:531
void menu_init(void)
Initialise all the Menus.
Definition: menu.c:118
char * mutt_ch_get_langinfo_charset(void)
Get the user's choice of character set.
Definition: charset.c:473
void mutt_ch_set_charset(const char *charset)
Update the records for a new character set.
Definition: charset.c:1014
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:171
void mutt_regexlist_free(struct RegexList *rl)
Free a RegexList object.
Definition: regex.c:171
void mutt_replacelist_free(struct ReplaceList *rl)
Free a ReplaceList object.
Definition: regex.c:448
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:548
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:580
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1024
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
const char * mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
Find last instance of a substring.
Definition: string.c:953
Many unsorted constants and some structs.
#define MUTT_TOKEN_CONDENSE
^(char) to control chars (macros)
Definition: mutt.h:68
#define MUTT_TOKEN_BACKTICK_VARS
Expand variables within backticks.
Definition: mutt.h:74
#define MUTT_TOKEN_MINUS
Treat '-' as a special.
Definition: mutt.h:78
#define MUTT_TOKEN_PLUS
Treat '+' as a special.
Definition: mutt.h:77
#define MUTT_TOKEN_COMMENT
Don't reap comments.
Definition: mutt.h:72
#define MUTT_TOKEN_QUOTE
Don't interpret quotes.
Definition: mutt.h:70
#define MUTT_TOKEN_NOSHELL
Don't expand environment variables.
Definition: mutt.h:75
uint16_t TokenFlags
Flags for mutt_extract_token(), e.g. MUTT_TOKEN_EQUAL.
Definition: mutt.h:65
#define MUTT_TOKEN_SPACE
Don't treat whitespace as a term.
Definition: mutt.h:69
#define MUTT_TOKEN_SEMICOLON
Don't treat ; as special.
Definition: mutt.h:73
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:66
#define MUTT_TOKEN_PATTERN
~%=!| are terms (for patterns)
Definition: mutt.h:71
#define MUTT_TOKEN_EQUAL
Treat '=' as a special.
Definition: mutt.h:67
#define MUTT_TOKEN_QUESTION
Treat '?' as a special.
Definition: mutt.h:76
void mutt_commands_free(void)
Free Commands array.
size_t mutt_commands_array(struct Command **first)
Get Commands array.
Definitions of NeoMutt commands.
void mutt_commands_init(void)
Hundreds of global variables to back the user variables.
char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:57
char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:52
struct ListHead MimeLookupList
List of mime types that that shouldn't use the mailcap entry.
Definition: mutt_globals.h:67
struct ListHead AlternativeOrderList
List of preferred mime types to display.
Definition: mutt_globals.h:64
struct ListHead AutoViewList
List of mime types to auto view.
Definition: mutt_globals.h:65
struct HashTable * TagFormats
Hash Table of tag-formats (tag -> format string)
Definition: mutt_globals.h:61
char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:56
struct ListHead UserHeader
List of custom headers to add to outgoing emails.
Definition: mutt_globals.h:70
struct ListHead Muttrc
List of config files to read.
Definition: mutt_globals.h:68
char * Username
User's login name.
Definition: mutt_globals.h:54
struct ListHead HeaderOrderList
List of header fields in the order they should be displayed.
Definition: mutt_globals.h:66
void mutt_lua_init(void)
Setup feature commands.
Definition: mutt_lua.c:469
Integrated Lua scripting.
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1728
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1516
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user's real name in /etc/passwd.
Definition: muttlib.c:361
Some miscellaneous functions.
struct MyVarList MyVars
List of all the user's custom config variables.
Definition: myvar.c:34
const char * myvar_get(const char *var)
Get the value of a "my_" variable.
Definition: myvar.c:92
Handling of personal config ('my' variables)
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:141
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:164
@ NT_COMMAND
A Command has been executed, Command.
Definition: notify_type.h:40
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: db.c:345
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: db.c:330
Notmuch virtual mailbox type.
int nm_get_all_tags(struct Mailbox *m, char **tag_list, int *tag_count)
Fill a list with all notmuch tags.
Definition: notmuch.c:1967
void nm_init(void)
Setup feature commands.
Definition: notmuch.c:99
Handling of global boolean variables.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:53
Pager functions.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Prototypes for many functions.
@ XDG_CONFIG_DIRS
XDG system dir: /etc/xdg.
Definition: protos.h:47
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_EMPTY(head)
Definition: queue.h:348
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:458
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:393
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:595
#define CSR_RESULT(x)
Definition: set.h:52
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
GUI display the mailboxes in a side panel.
void sb_init(void)
Set up the Sidebar.
Definition: sidebar.c:198
void sb_shutdown(void)
Clean up the Sidebar.
Definition: sidebar.c:210
Assorted sorting methods.
int(* sort_t)(const void *a, const void *b)
Definition: sort.h:47
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
#define SKIPWS(ch)
Definition: string2.h:46
Mapping between a user key and a function.
Definition: keymap.h:92
const char * name
name of the function
Definition: keymap.h:93
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
enum CommandResult(* parse)(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Definition: command.h:61
intptr_t data
Data or flags to pass to the command.
Definition: command.h:63
const char * name
Name of the command.
Definition: command.h:48
Container for lots of config items.
Definition: set.h:260
struct ConfigSet * cs
Parent ConfigSet.
Definition: subset.h:51
The "current" mailbox.
Definition: context.h:38
struct Mailbox * mailbox
Current Mailbox.
Definition: context.h:49
The item stored in a Hash Table.
Definition: hash.h:44
union HashKey key
Key representing the data.
Definition: hash.h:46
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition: hash.h:45
Cursor to iterate through a Hash Table.
Definition: hash.h:132
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
List of Mailboxes.
Definition: mailbox.h:157
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:158
A mailbox.
Definition: mailbox.h:82
struct HashTable * label_hash
Hash Table for x-labels.
Definition: mailbox.h:129
A user-set variable.
Definition: myvar.h:32
char * name
Name of user variable.
Definition: myvar.h:33
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int cs_subset_he_string_get(const struct ConfigSubset *sub, struct HashElem *he, struct Buffer *result)
Get a config item as a string.
Definition: subset.c:354
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
int cs_subset_str_string_get(const struct ConfigSubset *sub, const char *name, struct Buffer *result)
Get a config item as a string.
Definition: subset.c:370
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition: subset.c:179
struct HashElem ** get_elem_list(struct ConfigSet *cs)
Create a sorted list of all config items.
Definition: subset.c:75
struct HashTable * TagTransforms
Lookup table of alternative tag names.
Definition: tags.c:37
MenuType
Types of GUI selections.
Definition: type.h:36
@ MENU_PAGER
Pager pager (email viewer)
Definition: type.h:54
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:44
#define DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:37
#define DT_LONG
a number (long)
Definition: types.h:33
#define DT_BOOL
boolean option
Definition: types.h:30
#define DT_DEPRECATED
Config item shouldn't be used any more.
Definition: types.h:77
#define DT_PATH
a path to a file/directory
Definition: types.h:36
#define DT_SYNONYM
synonym for another variable
Definition: types.h:42
#define DT_NUMBER
a number
Definition: types.h:35
const char * strkey
String key.
Definition: hash.h:36