NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
muttlib.c
Go to the documentation of this file.
1 
31 #include "config.h"
32 #include <ctype.h>
33 #include <errno.h>
34 #include <inttypes.h>
35 #include <limits.h>
36 #include <pwd.h>
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include "mutt/lib.h"
45 #include "address/lib.h"
46 #include "config/lib.h"
47 #include "email/lib.h"
48 #include "core/lib.h"
49 #include "alias/lib.h"
50 #include "gui/lib.h"
51 #include "mutt.h"
52 #include "muttlib.h"
53 #include "ncrypt/lib.h"
54 #include "question/lib.h"
55 #include "format_flags.h"
56 #include "hook.h"
57 #include "init.h"
58 #include "mutt_globals.h"
59 #include "mx.h"
60 #include "protos.h"
61 #ifdef USE_IMAP
62 #include "imap/lib.h"
63 #endif
64 
65 static const char *xdg_env_vars[] = {
66  [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
67  [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
68 };
69 
70 static const char *xdg_defaults[] = {
71  [XDG_CONFIG_HOME] = "~/.config",
72  [XDG_CONFIG_DIRS] = "/etc/xdg",
73 };
74 
83 void mutt_adv_mktemp(struct Buffer *buf)
84 {
85  if (!(buf->data && (buf->data[0] != '\0')))
86  {
87  mutt_buffer_mktemp(buf);
88  }
89  else
90  {
91  struct Buffer *prefix = mutt_buffer_pool_get();
92  mutt_buffer_strcpy(prefix, buf->data);
93  mutt_file_sanitize_filename(prefix->data, true);
94  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
95  mutt_buffer_printf(buf, "%s/%s", NONULL(c_tmpdir), mutt_buffer_string(prefix));
96 
97  struct stat sb;
98  if ((lstat(mutt_buffer_string(buf), &sb) == -1) && (errno == ENOENT))
99  goto out;
100 
101  char *suffix = strchr(prefix->data, '.');
102  if (suffix)
103  {
104  *suffix = '\0';
105  suffix++;
106  }
107  mutt_buffer_mktemp_pfx_sfx(buf, prefix->data, suffix);
108 
109  out:
110  mutt_buffer_pool_release(&prefix);
111  }
112 }
113 
122 char *mutt_expand_path(char *buf, size_t buflen)
123 {
124  return mutt_expand_path_regex(buf, buflen, false);
125 }
126 
134 void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
135 {
136  const char *s = NULL;
137  const char *tail = "";
138 
139  bool recurse = false;
140 
141  struct Buffer *p = mutt_buffer_pool_get();
142  struct Buffer *q = mutt_buffer_pool_get();
143  struct Buffer *tmp = mutt_buffer_pool_get();
144 
145  do
146  {
147  recurse = false;
148  s = mutt_buffer_string(buf);
149 
150  switch (*s)
151  {
152  case '~':
153  {
154  if ((s[1] == '/') || (s[1] == '\0'))
155  {
157  tail = s + 1;
158  }
159  else
160  {
161  char *t = strchr(s + 1, '/');
162  if (t)
163  *t = '\0';
164 
165  struct passwd *pw = getpwnam(s + 1);
166  if (pw)
167  {
168  mutt_buffer_strcpy(p, pw->pw_dir);
169  if (t)
170  {
171  *t = '/';
172  tail = t;
173  }
174  else
175  tail = "";
176  }
177  else
178  {
179  /* user not found! */
180  if (t)
181  *t = '/';
183  tail = s;
184  }
185  }
186  break;
187  }
188 
189  case '=':
190  case '+':
191  {
192  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
193  enum MailboxType mb_type = mx_path_probe(c_folder);
194 
195  /* if folder = {host} or imap[s]://host/: don't append slash */
196  if ((mb_type == MUTT_IMAP) && ((c_folder[strlen(c_folder) - 1] == '}') ||
197  (c_folder[strlen(c_folder) - 1] == '/')))
198  {
199  mutt_buffer_strcpy(p, NONULL(c_folder));
200  }
201  else if (mb_type == MUTT_NOTMUCH)
202  mutt_buffer_strcpy(p, NONULL(c_folder));
203  else if (c_folder && (c_folder[strlen(c_folder) - 1] == '/'))
204  mutt_buffer_strcpy(p, NONULL(c_folder));
205  else
206  mutt_buffer_printf(p, "%s/", NONULL(c_folder));
207 
208  tail = s + 1;
209  break;
210  }
211 
212  /* elm compatibility, @ expands alias to user name */
213 
214  case '@':
215  {
216  struct AddressList *al = alias_lookup(s + 1);
217  if (al && !TAILQ_EMPTY(al))
218  {
219  struct Email *e = email_new();
220  e->env = mutt_env_new();
221  mutt_addrlist_copy(&e->env->from, al, false);
222  mutt_addrlist_copy(&e->env->to, al, false);
223 
224  /* TODO: fix mutt_default_save() to use Buffer */
226  mutt_default_save(p->data, p->dsize, e);
228 
229  email_free(&e);
230  /* Avoid infinite recursion if the resulting folder starts with '@' */
231  if (*p->data != '@')
232  recurse = true;
233 
234  tail = "";
235  }
236  break;
237  }
238 
239  case '>':
240  {
241  const char *const c_mbox = cs_subset_string(NeoMutt->sub, "mbox");
242  mutt_buffer_strcpy(p, c_mbox);
243  tail = s + 1;
244  break;
245  }
246 
247  case '<':
248  {
249  const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
250  mutt_buffer_strcpy(p, c_record);
251  tail = s + 1;
252  break;
253  }
254 
255  case '!':
256  {
257  if (s[1] == '!')
258  {
260  tail = s + 2;
261  }
262  else
263  {
264  const char *const c_spool_file =
265  cs_subset_string(NeoMutt->sub, "spool_file");
266  mutt_buffer_strcpy(p, c_spool_file);
267  tail = s + 1;
268  }
269  break;
270  }
271 
272  case '-':
273  {
275  tail = s + 1;
276  break;
277  }
278 
279  case '^':
280  {
282  tail = s + 1;
283  break;
284  }
285 
286  default:
287  {
289  tail = s;
290  }
291  }
292 
293  if (regex && *(mutt_buffer_string(p)) && !recurse)
294  {
296  mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(q), tail);
297  }
298  else
299  mutt_buffer_printf(tmp, "%s%s", mutt_buffer_string(p), tail);
300 
301  mutt_buffer_copy(buf, tmp);
302  } while (recurse);
303 
307 
308 #ifdef USE_IMAP
309  /* Rewrite IMAP path in canonical form - aids in string comparisons of
310  * folders. May possibly fail, in which case buf should be the same. */
311  if (imap_path_probe(mutt_buffer_string(buf), NULL) == MUTT_IMAP)
312  imap_expand_path(buf);
313 #endif
314 }
315 
323 {
324  mutt_buffer_expand_path_regex(buf, false);
325 }
326 
336 char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
337 {
338  struct Buffer *tmp = mutt_buffer_pool_get();
339 
340  mutt_buffer_addstr(tmp, NONULL(buf));
341  mutt_buffer_expand_path_regex(tmp, regex);
342  mutt_str_copy(buf, mutt_buffer_string(tmp), buflen);
343 
345 
346  return buf;
347 }
348 
361 char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
362 {
363  regmatch_t pat_match[1];
364  size_t pwnl;
365  char *p = NULL;
366 
367  if (!pw || !pw->pw_gecos)
368  return NULL;
369 
370  memset(dest, 0, destlen);
371 
372  const struct Regex *c_gecos_mask =
373  cs_subset_regex(NeoMutt->sub, "gecos_mask");
374  if (mutt_regex_capture(c_gecos_mask, pw->pw_gecos, 1, pat_match))
375  {
376  mutt_str_copy(dest, pw->pw_gecos + pat_match[0].rm_so,
377  MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
378  }
379  else if ((p = strchr(pw->pw_gecos, ',')))
380  mutt_str_copy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
381  else
382  mutt_str_copy(dest, pw->pw_gecos, destlen);
383 
384  pwnl = strlen(pw->pw_name);
385 
386  for (int idx = 0; dest[idx]; idx++)
387  {
388  if (dest[idx] == '&')
389  {
390  memmove(&dest[idx + pwnl], &dest[idx + 1],
391  MAX((ssize_t) (destlen - idx - pwnl - 1), 0));
392  memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
393  dest[idx] = toupper((unsigned char) dest[idx]);
394  }
395  }
396 
397  return dest;
398 }
399 
406 bool mutt_needs_mailcap(struct Body *m)
407 {
408  switch (m->type)
409  {
410  case TYPE_TEXT:
411  if (mutt_istr_equal("plain", m->subtype))
412  return false;
413  break;
414  case TYPE_APPLICATION:
416  return false;
418  return false;
419  break;
420 
421  case TYPE_MULTIPART:
422  case TYPE_MESSAGE:
423  return false;
424  }
425 
426  return true;
427 }
428 
434 bool mutt_is_text_part(struct Body *b)
435 {
436  int t = b->type;
437  char *s = b->subtype;
438 
440  return false;
441 
442  if (t == TYPE_TEXT)
443  return true;
444 
445  if (t == TYPE_MESSAGE)
446  {
447  if (mutt_istr_equal("delivery-status", s))
448  return true;
449  }
450 
451  if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
452  {
453  if (mutt_istr_equal("pgp-keys", s))
454  return true;
455  }
456 
457  return false;
458 }
459 
468 void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix,
469  const char *suffix, const char *src, int line)
470 {
471  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
472  mutt_buffer_printf(buf, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(c_tmpdir),
473  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
474  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
475 
476  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line,
477  mutt_buffer_string(buf));
478  if (unlink(mutt_buffer_string(buf)) && (errno != ENOENT))
479  {
480  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
481  line, mutt_buffer_string(buf), strerror(errno), errno);
482  }
483 }
484 
496 void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix,
497  const char *suffix, const char *src, int line)
498 {
499  const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
500  size_t n =
501  snprintf(buf, buflen, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(c_tmpdir),
502  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
503  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
504  if (n >= buflen)
505  {
507  "%s:%d: ERROR: insufficient buffer space to hold temporary "
508  "filename! buflen=%zu but need %zu\n",
509  src, line, buflen, n);
510  }
511  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, buf);
512  if ((unlink(buf) != 0) && (errno != ENOENT))
513  {
514  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
515  line, buf, strerror(errno), errno);
516  }
517 }
518 
526 void mutt_pretty_mailbox(char *buf, size_t buflen)
527 {
528  if (!buf)
529  return;
530 
531  char *p = buf, *q = buf;
532  size_t len;
533  enum UrlScheme scheme;
534  char tmp[PATH_MAX];
535 
536  scheme = url_check_scheme(buf);
537 
538  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
539  if ((scheme == U_IMAP) || (scheme == U_IMAPS))
540  {
541  imap_pretty_mailbox(buf, buflen, c_folder);
542  return;
543  }
544 
545  if (scheme == U_NOTMUCH)
546  return;
547 
548  /* if buf is an url, only collapse path component */
549  if (scheme != U_UNKNOWN)
550  {
551  p = strchr(buf, ':') + 1;
552  if (mutt_strn_equal(p, "//", 2))
553  q = strchr(p + 2, '/');
554  if (!q)
555  q = strchr(p, '\0');
556  p = q;
557  }
558 
559  /* cleanup path */
560  if (strstr(p, "//") || strstr(p, "/./"))
561  {
562  /* first attempt to collapse the pathname, this is more
563  * lightweight than realpath() and doesn't resolve links */
564  while (*p)
565  {
566  if ((p[0] == '/') && (p[1] == '/'))
567  {
568  *q++ = '/';
569  p += 2;
570  }
571  else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
572  {
573  *q++ = '/';
574  p += 3;
575  }
576  else
577  *q++ = *p++;
578  }
579  *q = '\0';
580  }
581  else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
582  realpath(p, tmp))
583  {
584  mutt_str_copy(p, tmp, buflen - (p - buf));
585  }
586 
587  if ((len = mutt_str_startswith(buf, c_folder)) && (buf[len] == '/'))
588  {
589  *buf++ = '=';
590  memmove(buf, buf + len, mutt_str_len(buf + len) + 1);
591  }
592  else if ((len = mutt_str_startswith(buf, HomeDir)) && (buf[len] == '/'))
593  {
594  *buf++ = '~';
595  memmove(buf, buf + len - 1, mutt_str_len(buf + len - 1) + 1);
596  }
597 }
598 
604 {
605  if (!buf || !buf->data)
606  return;
607  /* This reduces the size of the Buffer, so we can pass it through.
608  * We adjust the size just to make sure buf->data is not NULL though */
610  mutt_pretty_mailbox(buf->data, buf->dsize);
612 }
613 
625 int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
626  enum SaveAttach *opt, char **directory)
627 {
628  struct stat st;
629 
630  mutt_buffer_strcpy(fname, path);
631  if (access(mutt_buffer_string(fname), F_OK) != 0)
632  return 0;
633  if (stat(mutt_buffer_string(fname), &st) != 0)
634  return -1;
635  if (S_ISDIR(st.st_mode))
636  {
637  enum QuadOption ans = MUTT_NO;
638  if (directory)
639  {
640  switch (mutt_multi_choice
641  /* L10N: Means "The path you specified as the destination file is a directory."
642  See the msgid "Save to file: " (alias.c, recvattach.c)
643  These three letters correspond to the choices in the string. */
644  (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
645  {
646  case 3: /* all */
647  mutt_str_replace(directory, mutt_buffer_string(fname));
648  break;
649  case 1: /* yes */
650  FREE(directory);
651  break;
652  case -1: /* abort */
653  FREE(directory);
654  return -1;
655  case 2: /* no */
656  FREE(directory);
657  return 1;
658  }
659  }
660  /* L10N: Means "The path you specified as the destination file is a directory."
661  See the msgid "Save to file: " (alias.c, recvattach.c) */
662  else if ((ans = mutt_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
663  return (ans == MUTT_NO) ? 1 : -1;
664 
665  struct Buffer *tmp = mutt_buffer_pool_get();
667  if ((mutt_buffer_get_field(_("File under directory: "), tmp, MUTT_FILE | MUTT_CLEAR,
668  false, NULL, NULL, NULL) != 0) ||
670  {
672  return (-1);
673  }
674  mutt_buffer_concat_path(fname, path, mutt_buffer_string(tmp));
676  }
677 
678  if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(mutt_buffer_string(fname), F_OK) == 0))
679  {
680  switch (
681  mutt_multi_choice(_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"),
682  // L10N: Options for: File exists, (o)verwrite, (a)ppend, or (c)ancel?
683  _("oac")))
684  {
685  case -1: /* abort */
686  return -1;
687  case 3: /* cancel */
688  return 1;
689 
690  case 2: /* append */
691  *opt = MUTT_SAVE_APPEND;
692  break;
693  case 1: /* overwrite */
694  *opt = MUTT_SAVE_OVERWRITE;
695  break;
696  }
697  }
698  return 0;
699 }
700 
710 void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
711 {
712  if (addr && addr->mailbox)
713  {
714  mutt_str_copy(buf, addr->mailbox, buflen);
715  const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
716  if (!c_save_address)
717  {
718  char *p = strpbrk(buf, "%@");
719  if (p)
720  *p = '\0';
721  }
722  mutt_str_lower(buf);
723  }
724  else
725  *buf = '\0';
726 }
727 
733 void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
734 {
735  if (a && a->mailbox)
736  {
737  mutt_buffer_strcpy(dest, a->mailbox);
738  const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
739  if (!c_save_address)
740  {
741  char *p = strpbrk(dest->data, "%@");
742  if (p)
743  {
744  *p = '\0';
745  mutt_buffer_fix_dptr(dest);
746  }
747  }
748  mutt_str_lower(dest->data);
749  }
750  else
751  mutt_buffer_reset(dest);
752 }
753 
761 void mutt_safe_path(struct Buffer *dest, const struct Address *a)
762 {
763  mutt_buffer_save_path(dest, a);
764  for (char *p = dest->data; *p; p++)
765  if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
766  *p = '_';
767 }
768 
780 void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
781  format_t callback, intptr_t data, MuttFormatFlags flags)
782 {
783  char prefix[128], tmp[1024];
784  char *cp = NULL, *wptr = buf;
785  char ch;
786  char if_str[128], else_str[128];
787  size_t wlen, count, len, wid;
788  FILE *fp_filter = NULL;
789  char *recycler = NULL;
790 
791  char src2[256];
792  mutt_str_copy(src2, src, mutt_str_len(src) + 1);
793  src = src2;
794 
795  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
796  const char *const c_arrow_string =
797  cs_subset_string(NeoMutt->sub, "arrow_string");
798 
799  prefix[0] = '\0';
800  buflen--; /* save room for the terminal \0 */
801  wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ?
802  mutt_strwidth(c_arrow_string) + 1 :
803  0;
804  col += wlen;
805 
806  if ((flags & MUTT_FORMAT_NOFILTER) == 0)
807  {
808  int off = -1;
809 
810  /* Do not consider filters if no pipe at end */
811  int n = mutt_str_len(src);
812  if ((n > 1) && (src[n - 1] == '|'))
813  {
814  /* Scan backwards for backslashes */
815  off = n;
816  while ((off > 0) && (src[off - 2] == '\\'))
817  off--;
818  }
819 
820  /* If number of backslashes is even, the pipe is real. */
821  /* n-off is the number of backslashes. */
822  if ((off > 0) && (((n - off) % 2) == 0))
823  {
824  char srccopy[1024];
825  int i = 0;
826 
827  mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
828 
829  strncpy(srccopy, src, n);
830  srccopy[n - 1] = '\0';
831 
832  /* prepare Buffers */
833  struct Buffer srcbuf = mutt_buffer_make(0);
834  mutt_buffer_addstr(&srcbuf, srccopy);
835  /* note: we are resetting dptr and *reading* from the buffer, so we don't
836  * want to use mutt_buffer_reset(). */
837  mutt_buffer_seek(&srcbuf, 0);
838  struct Buffer word = mutt_buffer_make(0);
839  struct Buffer cmd = mutt_buffer_make(0);
840 
841  /* Iterate expansions across successive arguments */
842  do
843  {
844  /* Extract the command name and copy to command line */
845  mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
846  if (word.data)
847  *word.data = '\0';
848  mutt_extract_token(&word, &srcbuf, MUTT_TOKEN_NO_FLAGS);
849  mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
850  mutt_buffer_addch(&cmd, '\'');
851  mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
852  data, flags | MUTT_FORMAT_NOFILTER);
853  for (char *p = tmp; p && (*p != '\0'); p++)
854  {
855  if (*p == '\'')
856  {
857  /* shell quoting doesn't permit escaping a single quote within
858  * single-quoted material. double-quoting instead will lead
859  * shell variable expansions, so break out of the single-quoted
860  * span, insert a double-quoted single quote, and resume. */
861  mutt_buffer_addstr(&cmd, "'\"'\"'");
862  }
863  else
864  mutt_buffer_addch(&cmd, *p);
865  }
866  mutt_buffer_addch(&cmd, '\'');
867  mutt_buffer_addch(&cmd, ' ');
868  } while (MoreArgs(&srcbuf));
869 
870  mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
871 
872  col -= wlen; /* reset to passed in value */
873  wptr = buf; /* reset write ptr */
874  pid_t pid = filter_create(cmd.data, NULL, &fp_filter, NULL);
875  if (pid != -1)
876  {
877  int rc;
878 
879  n = fread(buf, 1, buflen /* already decremented */, fp_filter);
880  mutt_file_fclose(&fp_filter);
881  rc = filter_wait(pid);
882  if (rc != 0)
883  mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
884  if (n > 0)
885  {
886  buf[n] = '\0';
887  while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
888  buf[--n] = '\0';
889  mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
890 
891  /* If the result ends with '%', this indicates that the filter
892  * generated %-tokens that neomutt can expand. Eliminate the '%'
893  * marker and recycle the string through mutt_expando_format().
894  * To literally end with "%", use "%%". */
895  if ((n > 0) && (buf[n - 1] == '%'))
896  {
897  n--;
898  buf[n] = '\0'; /* remove '%' */
899  if ((n > 0) && (buf[n - 1] != '%'))
900  {
901  recycler = mutt_str_dup(buf);
902  if (recycler)
903  {
904  /* buflen is decremented at the start of this function
905  * to save space for the terminal nul char. We can add
906  * it back for the recursive call since the expansion of
907  * format pipes does not try to append a nul itself. */
908  mutt_expando_format(buf, buflen + 1, col, cols, recycler,
909  callback, data, flags);
910  FREE(&recycler);
911  }
912  }
913  }
914  }
915  else
916  {
917  /* read error */
918  mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
919  strerror(errno), errno);
920  *wptr = '\0';
921  }
922  }
923  else
924  {
925  /* Filter failed; erase write buffer */
926  *wptr = '\0';
927  }
928 
929  mutt_buffer_dealloc(&cmd);
930  mutt_buffer_dealloc(&srcbuf);
931  mutt_buffer_dealloc(&word);
932  return;
933  }
934  }
935 
936  while (*src && (wlen < buflen))
937  {
938  if (*src == '%')
939  {
940  if (*++src == '%')
941  {
942  *wptr++ = '%';
943  wlen++;
944  col++;
945  src++;
946  continue;
947  }
948 
949  if (*src == '?')
950  {
951  /* change original %? to new %< notation */
952  /* %?x?y&z? to %<x?y&z> where y and z are nestable */
953  char *p = (char *) src;
954  *p = '<';
955  /* skip over "x" */
956  for (; *p && (*p != '?'); p++)
957  ; // do nothing
958 
959  /* nothing */
960  if (*p == '?')
961  p++;
962  /* fix up the "y&z" section */
963  for (; *p && (*p != '?'); p++)
964  {
965  /* escape '<' and '>' to work inside nested-if */
966  if ((*p == '<') || (*p == '>'))
967  {
968  memmove(p + 2, p, mutt_str_len(p) + 1);
969  *p++ = '\\';
970  *p++ = '\\';
971  }
972  }
973  if (*p == '?')
974  *p = '>';
975  }
976 
977  if (*src == '<')
978  {
979  flags |= MUTT_FORMAT_OPTIONAL;
980  ch = *(++src); /* save the character to switch on */
981  src++;
982  cp = prefix;
983  count = 0;
984  while ((count < (sizeof(prefix) - 1)) && (*src != '\0') && (*src != '?'))
985  {
986  *cp++ = *src++;
987  count++;
988  }
989  *cp = '\0';
990  }
991  else
992  {
993  flags &= ~MUTT_FORMAT_OPTIONAL;
994 
995  /* eat the format string */
996  cp = prefix;
997  count = 0;
998  while ((count < (sizeof(prefix) - 1)) && strchr("0123456789.-=", *src))
999  {
1000  *cp++ = *src++;
1001  count++;
1002  }
1003  *cp = '\0';
1004 
1005  if (*src == '\0')
1006  break; /* bad format */
1007 
1008  ch = *src++; /* save the character to switch on */
1009  }
1010 
1011  if (flags & MUTT_FORMAT_OPTIONAL)
1012  {
1013  int lrbalance;
1014 
1015  if (*src != '?')
1016  break; /* bad format */
1017  src++;
1018 
1019  /* eat the 'if' part of the string */
1020  cp = if_str;
1021  count = 0;
1022  lrbalance = 1;
1023  while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
1024  {
1025  if ((src[0] == '%') && (src[1] == '>'))
1026  {
1027  /* This is a padding expando; copy two chars and carry on */
1028  *cp++ = *src++;
1029  *cp++ = *src++;
1030  count += 2;
1031  continue;
1032  }
1033 
1034  if (*src == '\\')
1035  {
1036  src++;
1037  *cp++ = *src++;
1038  }
1039  else if ((src[0] == '%') && (src[1] == '<'))
1040  {
1041  lrbalance++;
1042  }
1043  else if (src[0] == '>')
1044  {
1045  lrbalance--;
1046  }
1047  if (lrbalance == 0)
1048  break;
1049  if ((lrbalance == 1) && (src[0] == '&'))
1050  break;
1051  *cp++ = *src++;
1052  count++;
1053  }
1054  *cp = '\0';
1055 
1056  /* eat the 'else' part of the string (optional) */
1057  if (*src == '&')
1058  src++; /* skip the & */
1059  cp = else_str;
1060  count = 0;
1061  while ((lrbalance > 0) && (count < sizeof(else_str)) && (*src != '\0'))
1062  {
1063  if ((src[0] == '%') && (src[1] == '>'))
1064  {
1065  /* This is a padding expando; copy two chars and carry on */
1066  *cp++ = *src++;
1067  *cp++ = *src++;
1068  count += 2;
1069  continue;
1070  }
1071 
1072  if (*src == '\\')
1073  {
1074  src++;
1075  *cp++ = *src++;
1076  }
1077  else if ((src[0] == '%') && (src[1] == '<'))
1078  {
1079  lrbalance++;
1080  }
1081  else if (src[0] == '>')
1082  {
1083  lrbalance--;
1084  }
1085  if (lrbalance == 0)
1086  break;
1087  if ((lrbalance == 1) && (src[0] == '&'))
1088  break;
1089  *cp++ = *src++;
1090  count++;
1091  }
1092  *cp = '\0';
1093 
1094  if ((*src == '\0'))
1095  break; /* bad format */
1096 
1097  src++; /* move past the trailing '>' (formerly '?') */
1098  }
1099 
1100  /* handle generic cases first */
1101  if ((ch == '>') || (ch == '*'))
1102  {
1103  /* %>X: right justify to EOL, left takes precedence
1104  * %*X: right justify to EOL, right takes precedence */
1105  int soft = ch == '*';
1106  int pl, pw;
1107  pl = mutt_mb_charlen(src, &pw);
1108  if (pl <= 0)
1109  {
1110  pl = 1;
1111  pw = 1;
1112  }
1113 
1114  /* see if there's room to add content, else ignore */
1115  if (((col < cols) && (wlen < buflen)) || soft)
1116  {
1117  int pad;
1118 
1119  /* get contents after padding */
1120  mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1121  len = mutt_str_len(tmp);
1122  wid = mutt_strwidth(tmp);
1123 
1124  pad = (cols - col - wid) / pw;
1125  if (pad >= 0)
1126  {
1127  /* try to consume as many columns as we can, if we don't have
1128  * memory for that, use as much memory as possible */
1129  if (wlen + (pad * pl) + len > buflen)
1130  pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1131  else
1132  {
1133  /* Add pre-spacing to make multi-column pad characters and
1134  * the contents after padding line up */
1135  while (((col + (pad * pw) + wid) < cols) && ((wlen + (pad * pl) + len) < buflen))
1136  {
1137  *wptr++ = ' ';
1138  wlen++;
1139  col++;
1140  }
1141  }
1142  while (pad-- > 0)
1143  {
1144  memcpy(wptr, src, pl);
1145  wptr += pl;
1146  wlen += pl;
1147  col += pw;
1148  }
1149  }
1150  else if (soft)
1151  {
1152  int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && c_arrow_cursor) ?
1153  mutt_strwidth(c_arrow_string) + 1 :
1154  0;
1155  int avail_cols = (cols > offset) ? (cols - offset) : 0;
1156  /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1157  *wptr = '\0';
1158  /* make sure right part is at most as wide as display */
1159  len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1160  /* truncate left so that right part fits completely in */
1161  wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1162  wptr = buf + wlen;
1163  /* Multi-column characters may be truncated in the middle.
1164  * Add spacing so the right hand side lines up. */
1165  while (((col + wid) < avail_cols) && ((wlen + len) < buflen))
1166  {
1167  *wptr++ = ' ';
1168  wlen++;
1169  col++;
1170  }
1171  }
1172  if ((len + wlen) > buflen)
1173  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1174  memcpy(wptr, tmp, len);
1175  wptr += len;
1176  }
1177  break; /* skip rest of input */
1178  }
1179  else if (ch == '|')
1180  {
1181  /* pad to EOL */
1182  int pl, pw;
1183  pl = mutt_mb_charlen(src, &pw);
1184  if (pl <= 0)
1185  {
1186  pl = 1;
1187  pw = 1;
1188  }
1189 
1190  /* see if there's room to add content, else ignore */
1191  if ((col < cols) && (wlen < buflen))
1192  {
1193  int c = (cols - col) / pw;
1194  if ((c > 0) && ((wlen + (c * pl)) > buflen))
1195  c = ((signed) (buflen - wlen)) / pl;
1196  while (c > 0)
1197  {
1198  memcpy(wptr, src, pl);
1199  wptr += pl;
1200  wlen += pl;
1201  col += pw;
1202  c--;
1203  }
1204  }
1205  break; /* skip rest of input */
1206  }
1207  else
1208  {
1209  bool to_lower = false;
1210  bool no_dots = false;
1211 
1212  while ((ch == '_') || (ch == ':'))
1213  {
1214  if (ch == '_')
1215  to_lower = true;
1216  else if (ch == ':')
1217  no_dots = true;
1218 
1219  ch = *src++;
1220  }
1221 
1222  /* use callback function to handle this case */
1223  *tmp = '\0';
1224  src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1225  else_str, data, flags);
1226 
1227  if (to_lower)
1228  mutt_str_lower(tmp);
1229  if (no_dots)
1230  {
1231  char *p = tmp;
1232  for (; *p; p++)
1233  if (*p == '.')
1234  *p = '_';
1235  }
1236 
1237  len = mutt_str_len(tmp);
1238  if ((len + wlen) > buflen)
1239  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1240 
1241  memcpy(wptr, tmp, len);
1242  wptr += len;
1243  wlen += len;
1244  col += mutt_strwidth(tmp);
1245  }
1246  }
1247  else if (*src == '\\')
1248  {
1249  if (!*++src)
1250  break;
1251  switch (*src)
1252  {
1253  case 'f':
1254  *wptr = '\f';
1255  break;
1256  case 'n':
1257  *wptr = '\n';
1258  break;
1259  case 'r':
1260  *wptr = '\r';
1261  break;
1262  case 't':
1263  *wptr = '\t';
1264  break;
1265  case 'v':
1266  *wptr = '\v';
1267  break;
1268  default:
1269  *wptr = *src;
1270  break;
1271  }
1272  src++;
1273  wptr++;
1274  wlen++;
1275  col++;
1276  }
1277  else
1278  {
1279  int bytes, width;
1280  /* in case of error, simply copy byte */
1281  bytes = mutt_mb_charlen(src, &width);
1282  if (bytes < 0)
1283  {
1284  bytes = 1;
1285  width = 1;
1286  }
1287  if ((bytes > 0) && ((wlen + bytes) < buflen))
1288  {
1289  memcpy(wptr, src, bytes);
1290  wptr += bytes;
1291  src += bytes;
1292  wlen += bytes;
1293  col += width;
1294  }
1295  else
1296  {
1297  src += buflen - wlen;
1298  wlen = buflen;
1299  }
1300  }
1301  }
1302  *wptr = '\0';
1303 }
1304 
1315 FILE *mutt_open_read(const char *path, pid_t *thepid)
1316 {
1317  FILE *fp = NULL;
1318  struct stat s;
1319 
1320  size_t len = mutt_str_len(path);
1321  if (len == 0)
1322  {
1323  return NULL;
1324  }
1325 
1326  if (path[len - 1] == '|')
1327  {
1328  /* read from a pipe */
1329 
1330  char *p = mutt_str_dup(path);
1331 
1332  p[len - 1] = 0;
1333  mutt_endwin();
1334  *thepid = filter_create(p, NULL, &fp, NULL);
1335  FREE(&p);
1336  }
1337  else
1338  {
1339  if (stat(path, &s) < 0)
1340  return NULL;
1341  if (S_ISDIR(s.st_mode))
1342  {
1343  errno = EINVAL;
1344  return NULL;
1345  }
1346  fp = fopen(path, "r");
1347  *thepid = -1;
1348  }
1349  return fp;
1350 }
1351 
1360 int mutt_save_confirm(const char *s, struct stat *st)
1361 {
1362  int ret = 0;
1363 
1364  enum MailboxType type = mx_path_probe(s);
1365 
1366 #ifdef USE_POP
1367  if (type == MUTT_POP)
1368  {
1369  mutt_error(_("Can't save message to POP mailbox"));
1370  return 1;
1371  }
1372 #endif
1373 
1374  if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
1375  {
1376  const bool c_confirm_append =
1377  cs_subset_bool(NeoMutt->sub, "confirm_append");
1378  if (c_confirm_append)
1379  {
1380  struct Buffer *tmp = mutt_buffer_pool_get();
1381  mutt_buffer_printf(tmp, _("Append messages to %s?"), s);
1383  if (ans == MUTT_NO)
1384  ret = 1;
1385  else if (ans == MUTT_ABORT)
1386  ret = -1;
1388  }
1389  }
1390 
1391 #ifdef USE_NNTP
1392  if (type == MUTT_NNTP)
1393  {
1394  mutt_error(_("Can't save message to news server"));
1395  return 0;
1396  }
1397 #endif
1398 
1399  if (stat(s, st) != -1)
1400  {
1401  if (type == MUTT_MAILBOX_ERROR)
1402  {
1403  mutt_error(_("%s is not a mailbox"), s);
1404  return 1;
1405  }
1406  }
1407  else if (type != MUTT_IMAP)
1408  {
1409  st->st_mtime = 0;
1410  st->st_atime = 0;
1411 
1412  /* pathname does not exist */
1413  if (errno == ENOENT)
1414  {
1415  const bool c_confirm_create =
1416  cs_subset_bool(NeoMutt->sub, "confirm_create");
1417  if (c_confirm_create)
1418  {
1419  struct Buffer *tmp = mutt_buffer_pool_get();
1420  mutt_buffer_printf(tmp, _("Create %s?"), s);
1422  if (ans == MUTT_NO)
1423  ret = 1;
1424  else if (ans == MUTT_ABORT)
1425  ret = -1;
1427  }
1428 
1429  /* user confirmed with MUTT_YES or set `$confirm_create` */
1430  if (ret == 0)
1431  {
1432  /* create dir recursively */
1433  char *tmp_path = mutt_path_dirname(s);
1434  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1435  {
1436  /* report failure & abort */
1437  mutt_perror(s);
1438  FREE(&tmp_path);
1439  return 1;
1440  }
1441  FREE(&tmp_path);
1442  }
1443  }
1444  else
1445  {
1446  mutt_perror(s);
1447  return 1;
1448  }
1449  }
1450 
1452  return ret;
1453 }
1454 
1461 void mutt_sleep(short s)
1462 {
1463  const short c_sleep_time = cs_subset_number(NeoMutt->sub, "sleep_time");
1464  if (c_sleep_time > s)
1465  sleep(c_sleep_time);
1466  else if (s)
1467  sleep(s);
1468 }
1469 
1476 const char *mutt_make_version(void)
1477 {
1478  static char vstring[256];
1479  snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1480  return vstring;
1481 }
1482 
1490 void mutt_encode_path(struct Buffer *buf, const char *src)
1491 {
1492  char *p = mutt_str_dup(src);
1493  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
1494  int rc = mutt_ch_convert_string(&p, c_charset, "us-ascii", MUTT_ICONV_NO_FLAGS);
1495  size_t len = mutt_buffer_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1496 
1497  /* convert the path to POSIX "Portable Filename Character Set" */
1498  for (size_t i = 0; i < len; i++)
1499  {
1500  if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
1501  {
1502  buf->data[i] = '_';
1503  }
1504  }
1505  FREE(&p);
1506 }
1507 
1516 int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
1517 {
1518  const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1519  char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(xdg_defaults[type]);
1520  char *x = xdg; /* strsep() changes xdg, so free x instead later */
1521  char *token = NULL;
1522  int rc = 0;
1523 
1524  while ((token = strsep(&xdg, ":")))
1525  {
1526  if (mutt_buffer_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1527  continue;
1529  if (access(mutt_buffer_string(buf), F_OK) == 0)
1530  {
1531  rc = 1;
1532  break;
1533  }
1534 
1535  if (mutt_buffer_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
1536  continue;
1538  if (access(mutt_buffer_string(buf), F_OK) == 0)
1539  {
1540  rc = 1;
1541  break;
1542  }
1543  }
1544 
1545  FREE(&x);
1546  return rc;
1547 }
1548 
1555 void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1556 {
1557  enum MailboxType mb_type = mx_path_probe(path);
1558 
1559  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
1560  if (mb_type == MUTT_IMAP)
1561  imap_get_parent_path(path, buf, buflen);
1562  else if (mb_type == MUTT_NOTMUCH)
1563  mutt_str_copy(buf, c_folder, buflen);
1564  else
1565  {
1566  mutt_str_copy(buf, path, buflen);
1567  int n = mutt_str_len(buf);
1568  if (n == 0)
1569  return;
1570 
1571  /* remove any final trailing '/' */
1572  if (buf[n - 1] == '/')
1573  buf[n - 1] = '\0';
1574 
1575  /* Remove everything until the next slash */
1576  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1577  ; // do nothing
1578 
1579  if (n > 0)
1580  buf[n] = '\0';
1581  else
1582  {
1583  buf[0] = '/';
1584  buf[1] = '\0';
1585  }
1586  }
1587 }
1588 
1611 int mutt_inbox_cmp(const char *a, const char *b)
1612 {
1613  /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1614  if ((a[0] == '+') && (b[0] == '+'))
1615  {
1616  return mutt_istr_equal(a + 1, "inbox") ? -1 :
1617  mutt_istr_equal(b + 1, "inbox") ? 1 :
1618  0;
1619  }
1620 
1621  const char *a_end = strrchr(a, '/');
1622  const char *b_end = strrchr(b, '/');
1623 
1624  /* If one path contains a '/', but not the other */
1625  if ((!a_end) ^ (!b_end))
1626  return 0;
1627 
1628  /* If neither path contains a '/' */
1629  if (!a_end)
1630  return 0;
1631 
1632  /* Compare the subpaths */
1633  size_t a_len = a_end - a;
1634  size_t b_len = b_end - b;
1635  size_t min = MIN(a_len, b_len);
1636  int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1637  (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
1638 
1639  if (!same)
1640  return 0;
1641 
1642  if (mutt_istr_equal(&a[min + 1], "inbox"))
1643  return -1;
1644 
1645  if (mutt_istr_equal(&b[min + 1], "inbox"))
1646  return 1;
1647 
1648  return 0;
1649 }
1650 
1657 void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1658 {
1659  if (!buf || !path)
1660  return;
1661 
1662  mutt_buffer_reset(buf);
1663 
1664  for (; *path; path++)
1665  {
1666  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1667  mutt_buffer_addch(buf, '_');
1668  else
1669  mutt_buffer_addch(buf, *path);
1670  }
1671 }
1672 
1679 void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1680 {
1681  if (!buf || (buflen == 0))
1682  return;
1683 
1684  const bool c_size_show_bytes =
1685  cs_subset_bool(NeoMutt->sub, "size_show_bytes");
1686  const bool c_size_show_fractions =
1687  cs_subset_bool(NeoMutt->sub, "size_show_fractions");
1688  const bool c_size_show_mb = cs_subset_bool(NeoMutt->sub, "size_show_mb");
1689  const bool c_size_units_on_left =
1690  cs_subset_bool(NeoMutt->sub, "size_units_on_left");
1691 
1692  if (c_size_show_bytes && (num < 1024))
1693  {
1694  snprintf(buf, buflen, "%d", (int) num);
1695  }
1696  else if (num == 0)
1697  {
1698  mutt_str_copy(buf, c_size_units_on_left ? "K0" : "0K", buflen);
1699  }
1700  else if (c_size_show_fractions && (num < 10189)) /* 0.1K - 9.9K */
1701  {
1702  snprintf(buf, buflen, c_size_units_on_left ? "K%3.1f" : "%3.1fK",
1703  (num < 103) ? 0.1 : (num / 1024.0));
1704  }
1705  else if (!c_size_show_mb || (num < 1023949)) /* 10K - 999K */
1706  {
1707  /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1708  snprintf(buf, buflen, c_size_units_on_left ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1709  }
1710  else if (c_size_show_fractions && (num < 10433332)) /* 1.0M - 9.9M */
1711  {
1712  snprintf(buf, buflen, c_size_units_on_left ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1713  }
1714  else /* 10M+ */
1715  {
1716  /* (10433332 + 52428) / 1048576 = 10 */
1717  snprintf(buf, buflen, c_size_units_on_left ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1718  }
1719 }
1720 
1728 void add_to_stailq(struct ListHead *head, const char *str)
1729 {
1730  /* don't add a NULL or empty string to the list */
1731  if (!str || (*str == '\0'))
1732  return;
1733 
1734  /* check to make sure the item is not already on this list */
1735  struct ListNode *np = NULL;
1736  STAILQ_FOREACH(np, head, entries)
1737  {
1738  if (mutt_istr_equal(str, np->data))
1739  {
1740  return;
1741  }
1742  }
1743  mutt_list_insert_tail(head, mutt_str_dup(str));
1744 }
1745 
1753 void remove_from_stailq(struct ListHead *head, const char *str)
1754 {
1755  if (mutt_str_equal("*", str))
1756  mutt_list_free(head); /* "unCMD *" means delete all current entries */
1757  else
1758  {
1759  struct ListNode *np = NULL, *tmp = NULL;
1760  STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1761  {
1762  if (mutt_istr_equal(str, np->data))
1763  {
1764  STAILQ_REMOVE(head, np, ListNode, entries);
1765  FREE(&np->data);
1766  FREE(&np);
1767  break;
1768  }
1769  }
1770  }
1771 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:221
Convenience wrapper for the gui headers.
Url is notmuch://.
Definition: url.h:46
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string -.
Definition: muttlib.c:780
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:402
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
#define WithCrypto
Definition: lib.h:113
IMAP network mailbox.
The envelope/body of an email.
Definition: email.h:37
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:58
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define MIN(a, b)
Definition: memory.h:31
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
Config/command parsing.
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
Structs that make up an email.
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer&#39;s contents to another Buffer.
Definition: buffer.c:445
#define mutt_error(...)
Definition: logging.h:88
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
int mutt_buffer_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:268
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: muttlib.h:75
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:526
No flags set.
Definition: mutt_attach.h:57
size_t idx
Definition: mailbox.c:257
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:737
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
Url is imaps://.
Definition: url.h:40
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:623
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:621
String manipulation buffer.
Definition: buffer.h:33
Overwrite existing file.
Definition: mutt_attach.h:59
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
Url wasn&#39;t recognised.
Definition: url.h:35
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final &#39;/&#39;.
Definition: path.c:376
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
Error occurred examining Mailbox.
Definition: mailbox.h:46
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:758
An email address.
Definition: address.h:35
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:590
char * mailbox
Mailbox and host address.
Definition: address.h:38
Url is imap://.
Definition: url.h:39
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:42
XDG home dir: ~/.config.
Definition: protos.h:46
void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:733
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int mutt_mb_charlen(const char *s, int *width)
Count the bytes in a (multibyte) character.
Definition: mbyte.c:54
#define IsPrint(ch)
Definition: mbyte.h:38
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:677
Flags to control mutt_expando_format()
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char *(* format_t)(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Definition: format_flags.h:65
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:242
static const char * xdg_env_vars[]
Definition: muttlib.c:65
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:603
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1360
The body of an email.
Definition: body.h:34
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
Convenience wrapper for the config headers.
char * HomeDir
User&#39;s home directory.
Definition: mutt_globals.h:45
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1024
Email Address Handling.
void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:134
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:336
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:122
#define MoreArgs(buf)
Definition: buffer.h:40
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1461
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2442
void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix, const char *suffix, const char *src, int line)
Create a temporary filename.
Definition: muttlib.c:496
Parse and execute user-defined hooks.
Many unsorted constants and some structs.
API for mailboxes.
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:161
UrlScheme
All recognised Url types.
Definition: url.h:33
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition: alias.c:282
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:877
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2402
Email Aliases.
User aborted the question (with Ctrl-G)
Definition: quad.h:37
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:362
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:761
char * subtype
content-type subtype
Definition: body.h:37
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 mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:618
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:637
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:88
Prototypes for many functions.
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1728
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:87
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:422
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:130
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:983
#define PATH_MAX
Definition: mutt.h:40
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:395
XdgType
XDG variable types.
Definition: protos.h:44
Type: &#39;text/*&#39;.
Definition: mime.h:38
char * dptr
Current read/write position.
Definition: buffer.h:36
WHERE char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:46
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1679
int mutt_inbox_cmp(const char *a, const char *b)
do two folders share the same path and one is an inbox
Definition: muttlib.c:1611
char * data
Pointer to data.
Definition: buffer.h:35
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:933
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
API for encryption/signing of emails.
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: question.c:49
Ask the user a question.
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:434
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
WHERE char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:51
#define MUTT_FILE
Do file completion.
Definition: mutt.h:54
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:180
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname, enum SaveAttach *opt, char **directory)
Ask the user if overwriting is necessary.
Definition: muttlib.c:625
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:1516
Type: &#39;message/*&#39;.
Definition: mime.h:35
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
match a regex against a string, with provided options
Definition: regex.c:596
#define IS_SPACE(ch)
Definition: string2.h:38
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1753
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1317
void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix, const char *suffix, const char *src, int line)
Create a temporary file.
Definition: muttlib.c:468
char * data
String.
Definition: list.h:36
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
Log at debug level 1.
Definition: logging.h:40
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:361
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
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:504
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1657
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
Cached regular expression.
Definition: regex3.h:89
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:406
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:565
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:50
Append to existing file.
Definition: mutt_attach.h:58
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:83
#define FREE(x)
Definition: memory.h:40
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
const char filename_safe_chars[]
Definition: file.c:61
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:184
#define MUTT_FORMAT_NOFILTER
Do not allow filtering on this pass.
Definition: format_flags.h:37
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
Hundreds of global variables to back the user variables.
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
#define TAILQ_EMPTY(head)
Definition: queue.h:721
void mutt_buffer_seek(struct Buffer *buf, size_t offset)
set current read/write position to offset from beginning
Definition: buffer.c:466
Log at debug level 5.
Definition: logging.h:44
const char * GitVer
Convenience wrapper for the library headers.
A List node for strings.
Definition: list.h:34
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1315
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
XDG system dir: /etc/xdg.
Definition: protos.h:47
static const char * xdg_defaults[]
Definition: muttlib.c:70
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1555
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:200
Type: &#39;application/*&#39;.
Definition: mime.h:33
Log at debug level 3.
Definition: logging.h:42
Url is file://.
Definition: url.h:36
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:66
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to &#39;us-ascii&#39;.
Definition: muttlib.c:1490
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:55
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1476
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
Turn an email address into a filename (for saving)
Definition: muttlib.c:710
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